我正在尝试设置 SSL 套接字连接(并在客户端上执行以下操作)
我生成证书签名请求以获取签名的客户端证书
现在我有一个私钥(在 CSR 期间使用)、一个签名的客户端证书和根证书(在带外获得)。
我将私钥和签名的客户端证书添加到证书链并将其添加到密钥管理器。以及信任管理器的根证书。 但我收到了错误的证书错误。
我很确定我使用的是正确的证书。我是否也应该将签名的客户端证书添加到信任管理器中?试过了,还是不行。
//I add the private key and the client cert to KeyStore ks
FileInputStream certificateStream = new FileInputStream(clientCertFile);
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
java.security.cert.Certificate[] chain = {};
chain = certificateFactory.generateCertificates(certificateStream).toArray(chain);
certificateStream.close();
String privateKeyEntryPassword = "123";
ks.setEntry("abc", new KeyStore.PrivateKeyEntry(privateKey, chain),
new KeyStore.PasswordProtection(privateKeyEntryPassword.toCharArray()));
//Add the root certificate to keystore jks
FileInputStream is = new FileInputStream(new File(filename));
CertificateFactory cf = CertificateFactory.getInstance("X.509");
java.security.cert.X509Certificate cert = (X509Certificate) cf.generateCertificate(is);
System.out.println("Certificate Information: ");
System.out.println(cert.getSubjectDN().toString());
jks.setCertificateEntry(cert.getSubjectDN().toString(), cert);
//Initialize the keymanager and trustmanager and add them to the SSL context
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(ks, "123".toCharArray());
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
tmf.init(jks);
我需要在这里创建某种证书链吗?
我也有一个带有这些组件的 p12,并且使用非常相似的代码,将私钥添加到密钥管理器并将根证书从 p12 添加到信任管理器,我可以使其工作。但现在我需要让它在没有 p12 的情况下工作。
编辑:请求堆栈跟踪。希望这应该足够了。 (注意:我屏蔽了文件名)
Caused by: javax.net.ssl.SSLHandshakeException: Received fatal alert: bad_certificate
at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Alerts.java:174)
at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Alerts.java:136)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.recvAlert(SSLSocketImpl.java:1720)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:954)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1138)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1165)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1149)
at client.abc2.openSocketConnection(abc2.java:33)
at client.abc1.runClient(abc1.java:63)
at screens.app.abc.validateLogin(abc.java:197)
... 32 more
您还需要将根证书添加到密钥库中。
bad_certificate
的意思是:
证书已损坏,包含未包含的签名 验证正确等
[RFC 2246]。
如果 JKS 中使用的证书之一已过期,您也会收到此错误。
在我的具体测试中,我通过 SSL 使用 JRE8 到 IBM MQ,并且个人证书已过期。我仅在
bad_certificate
是 JRE 版本或 IBM MQ SSL 握手的症状时才会提及这一点。
这并不一定回答问题,因为作者说他们刚刚生成了一个证书,但是如果有人 Google 错误(就像我一样),你可能会在这里结束。
当我删除这两行时,我收到了此错误。如果您知道您的密钥库具有正确的证书,请确保您的代码正在查看正确的密钥库。
System.setProperty("javax.net.ssl.keyStore", <keystorePath>));
System.setProperty("javax.net.ssl.keyStorePassword",<keystorePassword>));
我还需要这个 VM 参数:
-Djavax.net.ssl.trustStore=/app/certs/keystore.jk
请参阅此处了解更多详细信息:
https://stackoverflow.com/a/34311797/1308453
只要服务器证书已签名且有效,您只需照常打开连接即可:
import java.net.*;
import java.io.*;
public class URLConnectionReader {
public static void main(String[] args) throws Exception {
URL google = new URL("https://www.google.com/");
URLConnection yc = google.openConnection();
BufferedReader in = new BufferedReader(new InputStreamReader(
yc.getInputStream()));
String inputLine;
while ((inputLine = in.readLine()) != null)
System.out.println(inputLine);
in.close();
}
}
请注意,URL 具有 HTTPS 架构以指示使用 SSL。
如果服务器的证书已签名,但您使用与证书中不同的 IP 地址/域名进行访问,则可以通过以下方式绕过主机名验证:
HostnameVerifier hv = new HostnameVerifier() {
public boolean verify(String urlHostName,SSLSession session) {
return true;
}
};
HttpsURLConnection.setDefaultHostnameVerifier(hv);
如果证书未签名,那么您需要将其添加到 JVM 使用的密钥库中(有用的命令)。