使用Java创建HTTPS服务器 - 本地证书在哪里?

时间:2016-07-21 08:20:49

标签: java ssl

我找到了一些处理https服务器和https客户端的教程。我创建了一些密钥库,它工作正常。但是我有一些问题从教程中不清楚。

这是我的https-server

public class HTTPSServer {

    private int port = 9999;
    private boolean isServerDone = false;

    public static void main(String[] args) {
        HTTPSServer server = new HTTPSServer();
        server.run();
    }

    HTTPSServer() {
    }

    HTTPSServer(int port) {
        this.port = port;
    }

    // Create the and initialize the SSLContext
    private SSLContext createSSLContext() {
        try {

            //Returns keystore object in definied type, here jks
            KeyStore keyStore = KeyStore.getInstance("JKS");
            //loads the keystore from givin input stream, and the password to unclock jks
            keyStore.load(new FileInputStream("x509-ca.jks"), "password".toCharArray());

            // Create key manager
            KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509");
            keyManagerFactory.init(keyStore, "password".toCharArray());
            KeyManager[] km = keyManagerFactory.getKeyManagers();

            // Create trust manager
            TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("SunX509");
            trustManagerFactory.init(keyStore);
            TrustManager[] tm = trustManagerFactory.getTrustManagers();

            // opens a secure socket with definied protocol
            SSLContext sslContext = SSLContext.getInstance("TLSv1.2");

            //System.out.println(keyStore.getCertificate("root").getPublicKey());
            //System.out.println(keyStore.isKeyEntry("root"));
            sslContext.init(km, tm, null);

            return sslContext;
        } catch (Exception ex) {
            ex.printStackTrace();
        }

        return null;
    }

    // Start to run the server
    public void run() {
        SSLContext sslContext = this.createSSLContext();

        try {
            // Create server socket factory
            SSLServerSocketFactory sslServerSocketFactory = sslContext.getServerSocketFactory();

            // Create server socket
            SSLServerSocket sslServerSocket = (SSLServerSocket) sslServerSocketFactory.createServerSocket(this.port);

            System.out.println("SSL server started");
            while (!isServerDone) {
                SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept();

                // Start the server thread
                new ServerThread(sslSocket).start();
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    // Thread handling the socket from client
    static class ServerThread extends Thread {

        private SSLSocket sslSocket = null;

        ServerThread(SSLSocket sslSocket) {
            this.sslSocket = sslSocket;
        }

        public void run() {
            sslSocket.setEnabledCipherSuites(sslSocket.getSupportedCipherSuites());
            //System.out.println("HIER: " + sslSocket.getHandshakeSession());

            //Klappt nicht, auch nicht, wenn der Client diese Zeile ebenfalls besitzt
            //sslSocket.setEnabledCipherSuites(new String[]{"TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256"});
            try {
                // Start handshake
                sslSocket.startHandshake();

                // Get session after the connection is established
                SSLSession sslSession = sslSocket.getSession();
                System.out.println(sslSession.getPeerHost());
                System.out.println(sslSession.getLocalCertificates());
                System.out.println("\tProtocol : " + sslSession.getProtocol());
                System.out.println("\tCipher suite : " + sslSession.getCipherSuite());
                System.out.println("\tSession context : " + sslSession.getSessionContext());
                //System.out.println("\tPeer pricipal of peer : " + sslSession.getPeerPrincipal());

                // Start handling application content
                InputStream inputStream = sslSocket.getInputStream();
                OutputStream outputStream = sslSocket.getOutputStream();

                BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
                PrintWriter printWriter = new PrintWriter(new OutputStreamWriter(outputStream));

                String line = null;

                String[] suites = sslSocket.getSupportedCipherSuites();
                for (int i = 0; i < suites.length; i++) {
                    //System.out.println(suites[i]);
                    //System.out.println(sslSession.getCipherSuite());
                }

                while ((line = bufferedReader.readLine()) != null) {
                    System.out.println("Inut : " + line);

                    if (line.trim().isEmpty()) {
                        break;
                    }
                }

                // Write data
                printWriter.print("HTTP/1.1 200\r\n");
                printWriter.flush();

                sslSocket.close();
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
    }
}

这是我的输出:

SSL server started
127.0.0.1
null
    Protocol : TLSv1.2
    Cipher suite : TLS_DH_anon_WITH_AES_128_GCM_SHA256
    Session context : sun.security.ssl.SSLSessionContextImpl@781df1a4 

我想知道,为什么这行

 System.out.println(sslSession.getLocalCertificates());

打印出“null”?

非常感谢你,Mira

2 个答案:

答案 0 :(得分:1)

来自documentation

  

Certificate[] getLocalCertificates()

     

返回在握手期间发送给对等方的证书。

     

注意:此方法仅在使用基于证书的密码套件时才有用。

     

当多个证书可用于握手时,实现会选择它认为可用的“最佳”证书链,并将其传输到另一方。此方法允许调用者知道实际使用的证书链。

     

<强>返回:

     

一个有序的证书数组,首先是本地证书,然后是任何证书颁发机构。如果没有发送证书,则返回null

我们关心的部分是“返回在握手期间发送给对等方的证书。”和“此方法仅在使用基于证书的密码套件时才有用。”。

鉴于它正在返回null,我们可以假设您没有向客户端发送任何证书。但它也是HTTPS,那么它给出了什么?好吧,看起来你正在使用TLS_DH_anon_WITH_AES_128_GCM_SHA256,顾名思义,匿名。根据{{​​3}}:

  

Anonymous Diffie-Hellman 使用Diffie-Hellman,但没有身份验证。由于交换中使用的密钥未经过身份验证,因此协议容易受到中间人攻击。注意:如果您使用此方案,则对SSL_get_peer_certificate的调用将返回NULL,因为您已选择匿名协议。这是SSL_get_peer_certificate在正常情况下唯一允许返回NULL的时间。

虽然这适用于OpenSSL,但它在Java中似乎是相同的 - 也就是说,您没有使用基于证书的密码。对TLS有更多了解的人需要加入,但看起来生成了AES密钥 ,并且它们被发送到客户端,但客户端无法保证这些密钥来自您,而通常你会生成密钥,然后使用RSA密钥对这些密钥进行签名/加密(不是100%肯定),以证明它们来自你。

要解决这个问题,我相信你需要选择一个不同的密码套件,例如: TLS_RSA_WITH_AES_128_GCM_SHA256。我不是100%肯定如何你会这样做,但这似乎是解决方案。

答案 1 :(得分:0)

sslSocket.setEnabledCipherSuites(sslSocket.getSupportedCipherSuites());

您正在启用所有匿名和低级密码套件,因此您允许服务器不发送证书,因此它不会发送证书,因此它不会在getLocalCertificates()中为您提供证书

删除此行。