我正在编写一个具有相互身份验证的客户端。服务器向我提供了证书(.cer)、密钥和密码。有了这些,我就可以毫无问题地通过 Insomnia 进行连接。通过 openSSL,我生成了一个 pfx 文件,用于从 FireFox 连接(以及我在这里尝试使用什么)。但是我无法从 JAVA 连接,因为我总是收到消息“PKIX...”(getOutputStream 行)。下面是我的代码:
private static void testConnection(String urlS) throws Exception {
KeyStore keyStore;
keyStore = KeyStore.getInstance("PKCS12");
keyStore.load((Main.class.getResourceAsStream("serverCertificate.pfx")), "password".toCharArray());
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(keyStore, "password".toCharArray());
KeyManager[] kms = kmf.getKeyManagers();
KeyStore trustStore = KeyStore.getInstance("JKS");
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(keyStore);
TrustManager[] tms = tmf.getTrustManagers();
SSLContext sc;
sc = SSLContext.getInstance("TLSv1.2");
sc.init(kms, tms, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
URL url = new URL(urlS);
HttpsURLConnection urlConn = (HttpsURLConnection) url.openConnection();
urlConn.setSSLSocketFactory(sc.getSocketFactory());
urlConn.setRequestMethod("POST");
urlConn.setRequestProperty("KeyId", "myKeyID");
urlConn.setRequestProperty("X-Request-ID", "XXX");
urlConn.setRequestProperty("Content-Type", "application/json");
urlConn.setDoOutput(true);
String jsonString = "my json string here";
OutputStream os = urlConn.getOutputStream();
os.write(jsonString.getBytes());
os.flush();
os.close();
int response = urlConn.getResponseCode();
}
有错吗?
PS:我在这个link中看到了这个问题,我认为这不是同一个问题,因为我的代码不同,而且我不打算创建一个密钥库并将其添加到jvm(如果可能),因为解决方案中建议。
我注意到没有文档明确定义如何在不使用jvm(cacerts)的情况下在相互身份验证中设置客户端。即使在 StackOverflow 上,配置模式也是最广泛且唯一的一种。我询问过如何做到这一点,由于使用了 OpenSSL,它变得有点复杂,但结果是值得的。接下来我给您留下一个分步文档,以便您可以通过这种方式配置您的客户端。
相互身份验证要求客户端和服务器均提供其证书并验证彼此的证书。
客户端证书可以由拥有服务器的实体或认证机构颁发。
(你可以在网上找到很多信息,从维基百科开始)
用于在 JVM 内配置客户端证书(cacerts)的现有文档非常丰富。事实上它是默认的。
此配置有一些缺点,例如:
本文档将解释如何在项目中配置两个证书,而不对 JVM 进行任何更改。
OpenSSL(您可以从这里下载,为了安全起见,不要从第三方下载)
你必须有:
如果您已经满足条件,请参阅以下说明:
KeyStore 是单个文件,在本例中扩展名为 pfx(也可以是 .jks),其中包括客户端证书 + 密钥。
它是由openssl生成的,如下所示:
openssl pkcs12 -export -out keystore.pfx -inkey clientCertificateKey.key -in clientCertificate.crt
openssl 会要求输入客户端证书的密码
如果您有根 CA 和中间证书,请使用各种 -in 参数包含这些证书,例如:
openssl pkcs12 -export -out keystore.pfx -inkey clientCertificateKey.key -in clientCertificate.crt -in intermediate.crt -in rootca.crt
获得服务器证书后,必须首先将其转换为 cer 文件,如下所述:
.pem 到 .cer
openssl x509 -outform der -in serverCertificate.pem -out serverCertificate.crt
.cer 到 .jks
keytool -import -alias $CERT_ALIAS -file serverCertificate.crt -keystore truststore.jks -deststorepass $CERT_PASSWORD
您必须定义 $CERT_ALIAS 和 $CERT_PASSWORD
此时您应该拥有 KeyStore (keystore.pfx) 和 TrustStore (truststore.jks)。那么让我们看看它们在代码中是如何使用的。
稍后我们将部分然后完整地了解如何在方法中使用这些文件。
在项目内的主类路径中添加 keystore.pfx 和 truststore.jks 文件。
KeyStore keyStore = KeyStore.getInstance("PKCS12");
keyStore.load((Main.class.getResourceAsStream("keystore.pfx")), "password".toCharArray());
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(keyStore, "password".toCharArray());
KeyManager[] kms = kmf.getKeyManagers();
KeyStore trustStore = KeyStore.getInstance("JKS");
trustStore.load((Main.class.getResourceAsStream("truststore.jks")), "password".toCharArray());
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(trustStore);
TrustManager[] tms = tmf.getTrustManagers();
在示例情况下,服务器请求:
以下方法旨在说明必须定义的代码部分,以便根据上面建立的前提通过相互身份验证与服务器进行通信。错误处理超出了范围 - 我将把这部分留给您,就像定义其他库的使用等一样;-)
private static String postJson (String urlS, String json) {
String response = "";
KeyStore keyStore = KeyStore.getInstance("PKCS12");
keyStore.load((Main.class.getResourceAsStream("keystore.pfx")), "password".toCharArray());
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(keyStore, "password".toCharArray());
KeyManager[] kms = kmf.getKeyManagers();
KeyStore trustStore = KeyStore.getInstance("JKS");
trustStore.load((Main.class.getResourceAsStream("truststore.jks")), "password".toCharArray());
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(trustStore);
TrustManager[] tms = tmf.getTrustManagers();
SSLContext sc;
sc = SSLContext.getInstance("TLSv1.2");
sc.init(kms, tms, new java.security.SecureRandom());
URL url = new URL(urlS);
HttpsURLConnection urlConn = (HttpsURLConnection) url.openConnection();
urlConn.setSSLSocketFactory(sc.getSocketFactory());
urlConn.setRequestMethod("POST");
urlConn.setRequestProperty("KeyId", "myKeyID");
urlConn.setRequestProperty("X-Request-ID", UUID.randomUUID().toString());
urlConn.setRequestProperty("Content-Type", "application/json");
urlConn.setDoOutput(true);
OutputStream os = urlConn.getOutputStream();
os.write(json.getBytes());
os.flush();
os.close();
if (urlConn.getResponseCode() != -1) {
boolean connection_ok = (urlConn.getResponseCode() == HttpURLConnection.HTTP_OK);
if (connection_ok) {
StringBuilder responseBuilder = new StringBuilder();
String responseLine = null;
while ((responseLine = br.readLine()) != null) {
responseBuilder.append(responseLine.trim());
}
response = responseBuilder.toString();
}
}
return response;
}
如果觉得有用,别忘了点个“赞”哦!
如有任何疑问,请留下您的评论。 谢谢。