Java 密钥库 - 以编程方式从密钥库文件中选择要使用的证书

问题描述:

我有一个包含多个客户端证书的 java 密钥库文件.我希望在我的 Java 应用程序中只选择这些证书之一来连接到服务.有没有一种简单的方法可以做到这一点?到目前为止,我找到解决方案的唯一方法是使用原始密钥库文件中的客户端证书详细信息(通过其别名找到)在程序中创建一个新的密钥库.我虽然可能有一种简单的方法可以说使用带有此别名的 keystore.jks 文件中的证书",而不必仅为要使用的证书创建新的密钥库.代码如下:

I have a java keystore file which contains multiple client certificates. I wish to select just one of these certificates in my Java application to connect to a service. Is there a simple way to do this? The only way I've found a solution to this so far is to create a new KeyStore in the program using the client cert details (found by its alias) from the original keystore file. I though there might be a simple way to just say "use the cert from the keystore.jks file with this alias", rather than have to create a new keystore just for the cert you want to use. Code is as follows:

        // Set up Client Cert settings
        KeyStore clientCertStore = KeyStore.getInstance("JKS");
        clientCertStore.load(new FileInputStream(clientKeystoreLocation), clientKeystorePassword);            

        // Create temporary one keystore, then extract the client cert using it's alias from keystore.jks, then create
        // a new keystore with this cert, that the process will use to connect with.
        KeyStore tempKstore = KeyStore.getInstance("JKS");
        tempKstore.load(null);
        tempKstore.setKeyEntry(certificateAlias, clientCertStore.getKey(certificateAlias, bwConfig.clientKeystorePassword),
                clientKeystorePassword, clientCertStore.getCertificateChain(certificateAlias));
        clientCertStore = tempKstore;

        KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
        keyManagerFactory.init(clientCertStore, clientKeystorePassword);            

        // Set up Truststore settings
        File truststoreFile = new File(TrustStoreLocation);
        KeyStore trustStore = KeyStore.getInstance("JKS");
        trustStore.load(new FileInputStream(truststoreFile), TrustStorePassword);
        TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        trustManagerFactory.init(trustStore);

        // Set to TLS 1.2 encryption
        SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
        sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null);

        SSLSocketFactory ssf = sslContext.getSocketFactory();
        ssf.createSocket(serviceURL.getHost(), servicePort);

        bp.getRequestContext().put("com.sun.xml.internal.ws.transport.https.client.SSLSocketFactory", ssf);

你的问题类似于 如何在连接前将所需密钥条目的别名告诉 SSLSocket?

默认KeyManager会选择握手中的第一个证书(根据服务器发送的CA列表),您可以自己构建X509KeyManager来指定要使用的别名包装默认值.

The default KeyManager will select the first certificate in handshake (according to CA list sent by server), You can build your own X509KeyManager to specify the alias to be used wrapping the default.

final X509KeyManager origKm = (X509KeyManager)keyManagerFactory.getKeyManagers()[0];
X509KeyManager km = new X509KeyManager() {
   public String chooseClientAlias(String[] keyType, Principal[] issuers, Socket socket) {
       return "alias";
   }

   public X509Certificate[] getCertificateChain(String alias) {
       return origKm.getCertificateChain(alias);
   }

// override the rest of the methods delegating to origKm ...
}

SSLContext 中设置新的 keyManager

 sslContext.init(new KeyManager[] { km }, trustManagerFactory.getTrustManagers(), null);