我认为您的设置有几个问题。
要正确配置与 JSSE 的 SSL 连接,您需要做一些事情,具体取决于您是否需要验证服务器、客户端或执行相互验证。
让我们假设稍后更完整的相互身份验证用例。
目标是配置一个SSLSocketFactory
您可以使用它来联系您的服务器。
要配置一个SSLSocketFactory
, 你需要一个SSLContext
.
对于相互身份验证用例,该元素又需要至少两个元素,一个KeyManagerFactory
,客户端 SSL 身份验证所需的,即服务器信任客户端,以及TrustManagerFactory
,配置客户端信任服务器所需的。
Both KeyManagerFactory
and TrustManagerFactory
需要正确配置的密钥库以及必要的加密材料。
因此,第一步将包括生成这种加密材料。
您已经使用服务器证书创建了密钥库:
keytool -keystore serverpublic.keystore -alias clientstore -import -file server.crt.der -storepass yourserverpublickeystorepassword
请注意,与服务器情况类似,您还需要为客户端创建一对公钥和私钥,当然,这与服务器不同。
您通过 OpenSSL 提供的相关代码和keytool
看起来合适。请为客户端重复该过程:
openssl req -new -text -out client.csr
openssl rsa -in clientpriv.pem -out client.key
openssl req -x509 -in client.csr -text -key client.key -out client.crt
// You can use PKCS12 also with Java but it is also ok on this way
openssl pkcs12 -inkey client.key -in client.crt -export -out client.pkcs12
// Do not bother yourself and, in this use case, use always the same password for the key and keystore
keytool -importkeystore -srckeystore client.pkcs12 -srcstoretype PKCS12 -destkeystore client.keystore -storepass "yourclientkeystorepassword"
准备好正确的密钥库后,请尝试执行以下操作来与服务器进行交互:
// First, let's configure the SSL for client authentication
KeyStore clientKeyStore = KeyStore.getInstance("JKS");
clientKeyStore.load(
new FileInputStream("/path/to/client.keystore"),
"yourclientkeystorepassword".toCharArray()
);
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); // SunX509
kmf.init(clientKeyStore, "yourclientkeystorepassword".toCharArray());
KeyManager[] keyManagers = kmf.getKeyManagers();
// Now, let's configure the client to trust the server
KeyStore serverKeyStore = KeyStore.getInstance("JKS");
serverKeyStore.load(
new FileInputStream("/path/to/serverpublic.keystore"),
"yourserverpublickeystorepassword".toCharArray()
);
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); // SunX509
tmf.init(serverKeyStore);
TrustManager[] trustManagers = tmf.getTrustManagers();
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(keyManagers, trustManagers, null); // You can provide SecureRandom also if you wish
// Create the SSL socket factory and establish the connection
SSLSocketFactory sf = sslContext.getSocketFactory();
SSLSocket socket = (SSLSocket)sf.createSocket(serverName, port);
// Interact with your server. Place your code here
// Please, consider the following link for alternatives approaches on how to
// interchange information with the server:
// https://web.mit.edu/java_v1.5.0_22/distrib/share/docs/guide/security/jsse/samples/sockets/client/SSLSocketClient.java
// It also suggest the use of startHandshake explicitly if your are using PrintWriter for the reason explained in the example an in the docs:
// https://docs.oracle.com/en/java/javase/11/docs/api/java.base/javax/net/ssl/SSLSocket.html
//...
// Close the socket
socket.close();
所描述的方法可以扩展为使用更高级别的抽象组件来代替套接字,例如HttpsURLConnection和 HTTP 客户端 - 除了以不同方式处理 SSL 的 Apache HttpClient - 就像OkHttp
在幕后,使用SSLSocketFactory
和相关的东西。
请也考虑查看此内容很棒的文章来自 IBM 的 DeveloperWorks,除了解释上述许多要点之外,还将为您在必要时为客户端和服务器生成密钥库提供很好的指导。
另请注意,根据您的服务器代码,您可能需要将其配置为信任提供的客户端证书。
根据您的评论,您使用的服务器端代码类似于 Postgresql 8.1 提供的代码。请参阅相关文件为了在该数据库中配置 SSL,如果您使用一些类似的服务器端代码,它可能会有所帮助。
最好的方法可能是生成从服务器信任的根证书派生的客户端证书,而不是使用自签名证书。
我认为它也与您的服务器端 SSL 证书和关联的私钥相关:首先,创建一个根自签名证书(您的 CA 证书),配置您的服务器端 C 代码以信任它,然后派生客户端和服务器端来自该 CA 的 SSL 加密材料:可能会简化您的设置并使一切正常工作。