我正在将 SSL 客户端连接到我的 SSL 服务器。 当客户端由于客户端的 keystore 中不存在根而无法验证证书时,我需要选择将该证书添加到代码中的本地 keystore 并继续。 有一些始终接受所有证书的示例,但我希望用户在不离开应用程序的情况下验证证书并将其添加到本地 keystore 。
SSLSocketFactory sslsocketfactory = (SSLSocketFactory) SSLSocketFactory.getDefault();
SSLSocket sslsocket = (SSLSocket) sslsocketfactory.createSocket("localhost", 23467);
try{
sslsocket.startHandshake();
} catch (IOException e) {
//here I want to get the peer's certificate, conditionally add to local key store, then reauthenticate successfully
}
有很多关于自定义 SocketFactory、TrustManager、SSLContext 等的东西,我真的不明白它们是如何组合在一起的,或者哪一条是实现我目标的最短路径。
最佳答案
您可以使用 X509TrustManager 来实现它.
使用
获取 SSLContextSSLContext ctx = SSLContext.getInstance("TLS");
然后使用您的自定义 X509TrustManager
通过使用 SSLContext#init 对其进行初始化. SecureRandom 和 KeyManager[] 可能为空。后者仅在您执行客户端身份验证时有用,如果在您的场景中只有服务器需要进行身份验证,您不需要设置它。
从此 SSLContext 中,使用 SSLContext#getSocketFactory 获取您的 SSLSocketFactory并按计划进行。
就您的 X509TrustManager 实现而言,它可能如下所示:
TrustManager tm = new X509TrustManager() {
public void checkClientTrusted(X509Certificate[] chain,
String authType)
throws CertificateException {
//do nothing, you're the client
}
public X509Certificate[] getAcceptedIssuers() {
//also only relevant for servers
}
public void checkServerTrusted(X509Certificate[] chain,
String authType)
throws CertificateException {
/* chain[chain.length -1] is the candidate for the
* root certificate.
* Look it up to see whether it's in your list.
* If not, ask the user for permission to add it.
* If not granted, reject.
* Validate the chain using CertPathValidator and
* your list of trusted roots.
*/
}
};
编辑:
Ryan 是对的,我忘了解释如何将新根添加到现有根中。假设您当前的受信任根 KeyStore 来自 cacerts
(JDK 附带的“Java 默认信任库”,位于 jre/lib/security 下)。我假设您使用 KeyStore#load(InputStream, char[]) 加载了该 keystore (它是 JKS 格式)。
KeyStore ks = KeyStore.getInstance("JKS");
FileInputStream in = new FileInputStream("<path to cacerts"");
ks.load(in, "changeit".toCharArray);
cacerts
的默认密码是“changeit”,如果您还没有更改的话。
然后您可以使用 KeyStore#setEntry 添加其他受信任的根.您可以省略 ProtectionParameter(即 null),KeyStore.Entry 将是 TrustedCertificateEntry它将新根作为其构造函数的参数。
KeyStore.Entry newEntry = new KeyStore.TrustedCertificateEntry(newRoot);
ks.setEntry("someAlias", newEntry, null);
如果您希望在某个时候保留更改后的信任存储,您可以使用 KeyStore#store(OutputStream, char[] 来实现此目的。 .
关于Java SSL连接,以编程方式将服务器证书添加到 keystore ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6755180/