通过 Java 的 SSL 套接字连接

SSL socket connection over Java

看了很多关于这些话题的回答,我发现自己完全无法把拼图拼凑起来,还望见谅。

我正在尝试更改 Java 中的简单套接字连接以使用 SSL。如果可能的话,我希望服务器和客户端都对自己进行身份验证,但只有服务器身份验证才是好的开始。

目前,这是服务器端极其简单的代码:

    ServerSocket serverSocket = new ServerSocket(port);
    Socket socket = serverSocket.accept();

这是客户端的代码:

    Socket socket = null;
    while (true) {
        try {
            socket = new Socket(ipAddress, port);
            break;
        } catch (Exception e) {}
    }

这工作正常,但没有 SSL。

我已经使用 OpenSSL 为服务器和客户端生成了 SSL 证书,最后是:

据此,我使用 OpenSSL 为客户端和服务器创建了 PKCS12 (.p12) 密钥库,如下所示

然后,我使用 keytool 将它们变成 JKS 密钥库(例如,对于服务器,命令是 keytool -importkeystore -srckeystore server.p12 -srcstoretype PKCS12 -destkeystore server.jks -deststoretype JKS),导致两个文件名为 server.jksclient.jks.

然后我使用以下代码替换之前的服务器代码段:

    char[] keyStorePassword =  "JKSPassword".toCharArray();
    FileInputStream keyStoreFile = new FileInputStream("somepath/server.jks");
    KeyStore keyStore = KeyStore.getInstance("JKS");
    keyStore.load(keyStoreFile, keyStorePassword);
    KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
    keyManagerFactory.init(keyStore, "PKCS12Password".toCharArray());
    SSLContext sslContext = SSLContext.getDefault();
    ServerSocket serverSocket = sslContext.getServerSocketFactory().createServerSocket(port);
    Socket socket = serverSocket.accept();

并使用以下代码替换客户端代码段:

    Socket socket = null;
    while (true) {
        try {
            char[] keyStorePassword =  "JKSPassword".toCharArray();
            FileInputStream keyStoreFile = new FileInputStream("somepath/client.jks");
            KeyStore keyStore = KeyStore.getInstance("JKS");
            keyStore.load(keyStoreFile, keyStorePassword);
            KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
            keyManagerFactory.init(keyStore, "PKCS12Password".toCharArray());
            SSLContext sslContext = SSLContext.getDefault();
            socket = sslContext.getSocketFactory().createSocket(ipAddress, port);
            break;
        } catch (Exception e) {}
    }

我现在仍然得到javax.net.ssl.SSLHandshakeException:服务器上没有共同的密码套件(和javax.net.ssl.SSLHandshakeException:收到致命警报: handshake_failure 在客户端)。

有什么问题吗?

我找到了拼图缺失的部分(我相信)。

而不是客户端上的 SSLContext sslContext = SSLContext.getDefault(); 行,我输入:

    BufferedInputStream serverCertificateFile = new BufferedInputStream(new FileInputStream("somepath/server-cert.der"));
    X509Certificate serverCertificate = (X509Certificate)
              CertificateFactory.getInstance("X.509").generateCertificate(serverCertificateFile);
            sslContext.init(keyManagerFactory.getKeyManagers(), new TrustManager[] {
                new X509TrustManager() {
                    @Override
                    public void checkClientTrusted(X509Certificate[] arg0,
                            String arg1) throws CertificateException {
                        throw new CertificateException();
                    }
                    @Override
                    public void checkServerTrusted(X509Certificate[] arg0,
                            String arg1) throws CertificateException {
                        boolean valid = false;
                        for (X509Certificate certificate : arg0) {
                            try {
                                certificate.verify(serverCertificate.getPublicKey());
                                valid = true;
                                break;
                            } catch (SignatureException e) {}
                        }
                        if (!valid) {
                            throw new CertificateException();
                        }
                    }
                    @Override
                    public X509Certificate[] getAcceptedIssuers() {
                        return new X509Certificate[0];
                    }
                }
            }, new SecureRandom());

server-cert.der 是一个文件,使用 openssl509 -outform der -in server-cert.pem -out server-cert.der 创建.

我无法在 Whosebug 上找到关于此事的好的教程或问题,所以这是我通过尝试理解参考指南而创建的。

本质上,我正在创建一个 TrustManager,对于服务器,当提供的证书之一属于您自己的服务器时信任它。

它似乎有效,请让我知道这种方法是否有任何主要错误(不幸的是我假设有)。感谢阅读这么多!