在一个SSL套接字连接中使用两个私钥(密钥库)和两个公钥(信任库)

我需要在一个套接字ssl连接中使用密钥对而不会在客户端中进行任何更改。

为什么?

因为一个客户端在信任存储中使用CN属性进行连接握手,而在同一属性中使用另一个值以同一方式处理相同任务的其他客户端。

所以我需要使用具有不同CN属性的 两个密钥存储 (私有)以及别名并共享 具有不同CN属性的 两个不同信任存储 (公钥)以及别名。

描述如下:

keyStore1

密钥库类型:JKS

密钥库提供商:SUN

别名: identity1

所有者: CN = app1

发行人:CN = app1 ……

trustStore1

别名: identity1

所有者: CN = app1

发行人:CN = app1 ……

keyStore2

别名: identity2

所有者:CN = app2 …

发行人:CN = app2 ……

trustStore2

别名: identity2

所有者: CN = app2

发行人:CN = app2 ……

我尝试以这种方式实现此function:

KeyStore KeyStore1; try { String keyStoreFile1 = "privatekey1"; String keyStoreType1 = "jks"; char[] keyStorePwd1 = "password".toCharArray(); keyStore1 = KeyStore.getInstance(keyStoreType1); keyStore1.load(new FileInputStream(keyStoreFile1), keyStorePwd1); } catch (java.security.GeneralSecurityException thr) { throw new IOException("Cannot load keystore (" + thr + ")"); } KeyStore trustStore1; try { String trustStoreFile1 = "publickey1"; String trustStoreType1 = "jks"; char[] trustStorePwd1 = "password".toCharArray(); trustStore1 = KeyStore.getInstance(trustStoreType1); trustStore.load(new FileInputStream(trustStoreFile1), trustStorePwd1); } catch (java.security.GeneralSecurityException thr) { throw new IOException("Cannot load truststore (" + thr + ")"); } KeyStore keyStore2; try { String keyStoreFile2 = "privatekey2"; String keyStoreType2 = "jks"; char[] keyStorePwd2 = "anotherpass".toCharArray(); keyStore2 = KeyStore.getInstance(key2StoreType); keyStore2.load(new FileInputStream(keyStoreFile2), keyStorePwd2); } catch (java.security.GeneralSecurityException thr) { throw new IOException("Cannot load keystore (" + thr + ")"); } KeyStore trustStore2; try { String trustStoreFile2 = "publickey2"; String trustStoreType2 = "jks"; char[] trustStorePwd2 = "anotherpass".toCharArray(); trustStore2 = KeyStore.getInstance(trustStoreType2); trustStore2.load(new FileInputStream(trustStoreFile2), trustStorePwd2); } catch (java.security.GeneralSecurityException thr) { throw new IOException("Cannot load truststore (" + thr + ")"); } KeyManagerFactory kmfkey1 = KeyManagerFactory .getInstance(KeyManagerFactory.getkey1Algorithm()); kmfkey1.init(keyStore1, "password".toCharArray()); TrustManagerFactory tmfkey1 = TrustManagerFactory.getInstance(TrustManagerFactory.getkey1Algorithm()); tmfkey1.init(trustStore1); SSLContext ctx = SSLContext.getInstance("SSL"); ctx.init(kmfkey1.getKeyManagers(), tmfkey1.getTrustManagers(), null); KeyManagerFactory kmfkey2 = KeyManagerFactory. getInstance(KeyManagerFactory.getkey1Algorithm()); kmfkey2.init(keyStore2, "password".toCharArray()); TrustManagerFactory tmfkey2 = TrustManagerFactory .getInstance(TrustManagerFactory.getkey1Algorithm()); tmfkey2.init(trustStore2); SSLContext ctxkey2 = SSLContext.getInstance("SSL"); ctxkey2.init(kmfkey2.getKeyManagers(), tmfkey2.getTrustManagers(), null); SSLServerSocketFactory sslSrvSockFact = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault(); serverSocket = sslSrvSockFact.createServerSocket(port); 

…但我收到此错误消息

java.security.KeyStoreException:位于com.sun的com.sun.net.ssl.internal.ssl.SunX509KeyManagerImpl。(SunX509KeyManagerImpl.java:106)的java.security.KeyStore.aliases(KeyStore.java:941)中未初始化的密钥库。 net.ssl.internal.ssl.KeyManagerFactoryImpl $ SunX509.engineInit(KeyManagerFactoryImpl.java:41)at javax.net.ssl.KeyManagerFactory.init(KeyManagerFactory.java:192)

所以我真的想知道是否可以在一个套接字连接中使用密钥对,或者以一种我无法看到或处理的不同方式解决这个问题。

编辑1

布鲁诺,你能给我一个完整或完整的例子吗?

因为我不清楚….

我尝试了两件事:

一个解决方案是将两个密钥放在私有密钥库中,遵循先前的建议……但是不起作用,我收到了以下消息:

线程“main”中的exceptionjava.lang.NoSuchMethodError:javax.net.ssl.SSLContext.setDefault(Ljavax / net / ssl / SSLContext;)at … initialiseManager(499)

你是对的…我仍然需要选择一个上下文或

javax.net.ssl.SSLException:没有可用的证书或密钥对应于已启用的SSL密码套件。 在尝试向服务器发送消息时……

其次我遵循了你的建议……但是SSL Socket连接没有启动

我在许多其他人的帮助下实现了这一点,他们在这个网站上显示了他们的代码…感谢所有人

 private KeyManager[] getKeyManagers() throws IOException, GeneralSecurityException { // First, get the default KeyManagerFactory. String alg = KeyManagerFactory.getDefaultAlgorithm(); KeyManagerFactory kmFact = KeyManagerFactory.getInstance(alg); // Next, set up the KeyStore to use. We need to load the file into // a KeyStore instance. File keyFile = new File("privatekey1"); FileInputStream fis = new FileInputStream(keyFile); LogManager.log("Loaded keystore privatekey1 " + keyFile.getAbsolutePath(), LogManager.LOG_LOWEST_LEVEL); KeyStore ks = KeyStore.getInstance("jks"); String keyStorePassword = "password"; ks.load(fis, keyStorePassword.toCharArray()); fis.close(); // Now we initialise the KeyManagerFactory with this KeyStore kmFact.init(ks, keyStorePassword.toCharArray()); KeyManagerFactory dkmFact = KeyManagerFactory.getInstance(alg); File keyFileTwo = new File("privatekey2"); FileInputStream fisTwo = new FileInputStream(keyFileTwo); LogManager.log("Loaded keystore privatekey2 " + keyFileTwo.getAbsolutePath(), LogManager.LOG_LOWEST_LEVEL); KeyStore ksTwo = KeyStore.getInstance("jks"); String keyStorePasswordTwo = "password"; ksTwo.load(fisTwo, keyStorePasswordTwo.toCharArray()); fisTwo.close(); // Now we initialise the KeyManagerFactory with this KeyStore dkmFact.init(ksTwo, keyStorePasswordTwo.toCharArray()); // default //KeyManagerFactory dkmFact = KeyManagerFactory.getInstance(alg); //dkmFact.init(null, null); // Get the first X509KeyManager in the list X509KeyManager customX509KeyManager = getX509KeyManager(alg, kmFact); X509KeyManager jvmX509KeyManager = getX509KeyManager(alg, dkmFact); KeyManager[] km = {new MultiKeyStoreManager(jvmX509KeyManager, customX509KeyManager)}; LogManager.log("Number of key managers registered:" + km.length, LogManager.LOG_LOWEST_LEVEL); return km; } /** * Find a X509 Key Manager compatible with a particular algorithm * @param algorithm * @param kmFact * @return * @throws NoSuchAlgorithmException */ private X509KeyManager getX509KeyManager(String algorithm, KeyManagerFactory kmFact) throws NoSuchAlgorithmException { KeyManager[] keyManagers = kmFact.getKeyManagers(); if (keyManagers == null || keyManagers.length == 0) { throw new NoSuchAlgorithmException("The default algorithm :" + algorithm + " produced no key managers"); } X509KeyManager x509KeyManager = null; for (int i = 0; i < keyManagers.length; i++) { if (keyManagers[i] instanceof X509KeyManager) { x509KeyManager = (X509KeyManager) keyManagers[i]; break; } } if (x509KeyManager == null) { throw new NoSuchAlgorithmException("The default algorithm :" + algorithm + " did not produce a X509 Key manager"); } return x509KeyManager; } private void initialiseManager(int iPort) throws IOException, GeneralSecurityException { // Next construct and initialise a SSLContext with the KeyStore and // the TrustStore. We use the default SecureRandom. // load your key store as a stream and initialize a KeyStore File trustFile = new File("publicKey"); LogManager.log("Trust File Loaded " + trustFile.getAbsolutePath(), LogManager.LOG_LOWEST_LEVEL); InputStream trustStream = new FileInputStream(trustFile); KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType()); // if your store is password protected then declare it (it can be null however) char[] trustPassword = "password".toCharArray(); // load the stream to your store trustStore.load(trustStream, trustPassword); File trustFileTwo = new File("publicKeyTwo"); LogManager.log("Trust File Loaded " + trustFileTwo.getAbsolutePath(), LogManager.LOG_LOWEST_LEVEL); InputStream trustStreamTwo = new FileInputStream(trustFileTwo); KeyStore trustStoreTwo = KeyStore.getInstance(KeyStore.getDefaultType()); // if your store is password protected then declare it (it can be null however) char[] trustPasswordTwo = "password".toCharArray(); // load the stream to your store trustStoreTwo.load(trustStreamTwo, trustPasswordTwo); // initialize a trust manager factory with the trusted store TrustManagerFactory trustFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); trustFactory.init(trustStore); trustFactory.init(trustStoreTwo); // get the trust managers from the factory TrustManager[] managers = trustFactory.getTrustManagers(); SSLContext context = SSLContext.getInstance("SSL"); context.init(getKeyManagers(), managers, new SecureRandom()); SSLContext.setDefault(context); SSLServerSocketFactory sslSrvFact = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault(); serverSocket = sslSrvFact.createServerSocket(iPort); // this method didn't create a Socket Connection correctly } class MultiKeyStoreManager implements X509KeyManager { private final X509KeyManager jvmKeyManager; private final X509KeyManager customKeyManager; public MultiKeyStoreManager(X509KeyManager jvmKeyManager, X509KeyManager customKeyManager) { this.jvmKeyManager = jvmKeyManager; this.customKeyManager = customKeyManager; } @Override public String chooseClientAlias(String[] keyType, Principal[] issuers, Socket socket) { // try the first key manager String alias = customKeyManager.chooseClientAlias(keyType, issuers, socket); if (alias == null) { alias = jvmKeyManager.chooseClientAlias(keyType, issuers, socket); LogManager.log("Reverting to JVM CLIENT alias : " + alias, LogManager.LOG_LOWEST_LEVEL); } return alias; } @Override public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) { // try the first key manager String alias = customKeyManager.chooseServerAlias(keyType, issuers, socket); if (alias == null) { alias = jvmKeyManager.chooseServerAlias(keyType, issuers, socket); LogManager.log("Reverting to JVM Server alias : " + alias ,LogManager.LOG_LOWEST_LEVEL); } return alias; } @Override public String[] getClientAliases(String keyType, Principal[] issuers) { String[] cAliases = customKeyManager.getClientAliases(keyType, issuers); String[] jAliases = jvmKeyManager.getClientAliases(keyType, issuers); LogManager.log("Supported Client Aliases Custom: " + cAliases.length + " JVM : " + jAliases.length, LogManager.LOG_LOWEST_LEVEL); return (String[]) ArrayUtils.addAll(cAliases, jAliases); } @Override public PrivateKey getPrivateKey(String alias) { PrivateKey key = customKeyManager.getPrivateKey(alias); if (key == null) { System.out.println("Reverting to JVM Key : " + alias); return jvmKeyManager.getPrivateKey(alias); } else { return key; } } @Override public String[] getServerAliases(String keyType, Principal[] issuers) { String[] cAliases = customKeyManager.getServerAliases(keyType, issuers); String[] jAliases = jvmKeyManager.getServerAliases(keyType, issuers); LogManager.log("Supported Server Aliases Custom: " + cAliases.length + " JVM : " + jAliases.length, LogManager.LOG_LOWEST_LEVEL); return (String[]) ArrayUtils.addAll(cAliases, jAliases); } @Override public java.security.cert.X509Certificate[] getCertificateChain(String string) { java.security.cert.X509Certificate[] chain = customKeyManager.getCertificateChain("alias_key1"); if (chain == null || chain.length == 0) { LogManager.log("Reverting to JVM Chain : " + string, LogManager.LOG_LOWEST_LEVEL); return jvmKeyManager.getCertificateChain("alias_key2"); } else { return chain; } } } and this gave me this status 

* 2012.02.09 18:47:00激活SSL连接

2012.02.09 18:47:00 [… :: run]

2012.02.09 18:47:00信任文件加载的publicKey

2012.02.09 18:47:00信任文件加载publicKeyTwo

2012.02.09 18:47:00加载的密钥库privateKey privateKey

2012.02.09 18:47:00加载的密钥库privateKey2 privateKeyTwo

2012.02.09 18:47:00注册的主要管理人员数量:1 *

但是当我尝试为服务器发送消息时没有任何反应……

很难找到一个在这种情况下真正起作用的例子。

编辑2

嗨布鲁诺

真的,你有关于这个主题,java和ssh的高级知识,我感谢你的帮助。 所有这些信息都有助于我更好地理解这个问题,同时向我展示方式……很多

你是对的……我在Java 5 JRE上使用Java 6的代码

但是再次关注你的食谱我得到了这个代码:

  // Load the key store: change store type if needed KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType()); FileInputStream fis = new FileInputStream("keyStore1"); char[] keyPass = "passw".toCharArray(); try { ks.load(fis, keyPass); } finally { if (fis != null) { fis.close(); } } // Get the default Key Manager KeyManagerFactory kmf = KeyManagerFactory.getInstance( KeyManagerFactory.getDefaultAlgorithm()); kmf.init(ks, keyPass); final X509KeyManager origKm = (X509KeyManager) kmf.getKeyManagers()[0]; X509KeyManager km = new X509KeyManager() { public String chooseServerAlias(String[] keyType, Principal[] issuers, Socket socket) { String alias; InetAddress remoteAddress = socket.getInetAddress(); if (remoteAddress.getHostAddress().equalsIgnoreCase("11.111.111.11")) { alias = "alias1"; LogManager.log("Reverting to JVM CLIENT alias : " + alias, LogManager.LOG_LOWEST_LEVEL); } else { alias = "alias2"; LogManager.log("Reverting to JVM CLIENT alias : " + alias, LogManager.LOG_LOWEST_LEVEL); } return alias; } public String chooseClientAlias(String[] keyType, Principal[] issuers, Socket socket) { // try this key manager String alias = origKm.chooseClientAlias(keyType, issuers, socket); LogManager.log("Reverting to JVM CLIENT alias : " + alias, LogManager.LOG_LOWEST_LEVEL); return alias; } public String[] getClientAliases(String keyType, Principal[] issues) { String[] cAliases = origKm.getClientAliases(keyType, issues); LogManager.log("Supported Client Aliases : " + cAliases.length, LogManager.LOG_LOWEST_LEVEL); return cAliases; } public String[] getServerAliases(String keyType, Principal[] issues) { String[] sAliases = origKm.getServerAliases(keyType, issues); LogManager.log("Supported Server Aliases: " + sAliases.length, LogManager.LOG_LOWEST_LEVEL); return sAliases; } public String chooseServerAlias(String keyType, Principal[] issues, Socket socket) { // try this key manager String alias = origKm.chooseServerAlias(keyType, issues, socket); LogManager.log("Reverting to JVM Server alias : " + alias, LogManager.LOG_LOWEST_LEVEL); return alias; } public X509Certificate[] getCertificateChain(String keyType) { // here I could specify my other keystore, keystore2 how I could do this? // I'm thinking in the righ way when I implemented this code to get the correct private key? X509Certificate[] chain = origKm.getCertificateChain("alias1"); if (chain == null || chain.length == 0) { LogManager.log("Reverting to JVM Chain : " + keyType, LogManager.LOG_LOWEST_LEVEL); return origKm.getCertificateChain("alias2"); } else { return chain; } } public PrivateKey getPrivateKey(String alias) { PrivateKey key = origKm.getPrivateKey(alias); // here I could get my other keystore according the alias, for example // keystore2 how I could do this? LogManager.log("Reverting to JVM Key : " + alias, LogManager.LOG_LOWEST_LEVEL); return key; } }; SSLContext sslContext = SSLContext.getInstance("SSL"); sslContext.init(new KeyManager[]{km}, null, null); SSLServerSocketFactory sslSrvFact = sslContext.getServerSocketFactory(); objServerSocket = sslSrvFact.createServerSocket(iPort); 

这正是我需要实现我的目标吗?

编辑3

使用此方法,客户端和服务器之间使用带有alias2的第二个密钥库在客户端中使用公共信任存储2进行握手

…但是当我尝试在客户端使用trust store1时,我仍然遇到错误

…收到来自[addr = / 11.111.111.11]的消息恢复到JVM服务器别名:alias2恢复为JVM密钥:alias2重试时出错:javax.net.ssl.SSLHandshakeException:收到致命警报:certificate_unknown

当客户端使用带有cert alias1的public truststore1时,我的代码不会更改服务器以使用私有key1和alias1 …

为解决这个问题还有什么必要做的……我相信已接近最终的解决方案……

再次!

编辑4

Bruno,我按照你的建议改变了getCertificateChain方法

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

和方法

 public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) { String alias; 

我也删除了重复的方法……

在成瘾中,使用旧信任库的客户端不validation主机名

 private static final HostnameVerifier DO_NOT_VERIFY = new HostnameVerifier() { public boolean verify(String hostname, SSLSession session) { return true; } }; 

但对于需要使用第二个信任存储的客户端进行此validation,这是一个原因,因为我需要处理密钥对证书…

EDIT5

我想知道如何实现服务器套接字,以便能够根据客户端使用的证书来识别和使用正确的证书,以便与服务器进行握手通信。

在服务器端解释更好的是:

AppServerSideSocket.jar

  • private keystore: privateKeyApp (类型JKS,使用keytool生成)
  • public keystore: publicKeyApp (类型JKS,与所有客户共享)

而在客户端……

AppClientSideSocket.jar – 公钥库: publicKeyApp

AppServerSideSocket.jar监听客户端请求并且一旦接收到客户端发送的进程信息

AppClientSideSocket.jar使用使用validation服务器主机名的 publicKeyApp使用SSL连接服务器,并在握手后发送AppServerSideSocket应用程序的信息。

现在,我有另一个客户端应用程序AppClientSideSocketNEW.jar ,以及此validation服务器主机名,以便与服务器进行通信。 在这种情况下,客户端公共证书中使用的CN必须与AppServerSideSocket.jar所在的主机名匹配。

最初在服务器端以这种方式配置连接:

 if (usingSSLConn) { System.setProperty("javax.net.ssl.keyStore", "privateKeyApp"); System.setProperty("javax.net.ssl.keyStorePassword", "privateKeyAppPassword"); SSLServerSocketFactory sslServerSocketFactory = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault(); ServerSocket serverSocketApp = sslServerSocketFactory.createServerSocket(Port); } else serverSocketApp = new ServerSocket(Port); } 

所有客户端都收到了相同的publicKeyApp,并且没有validation主机名与服务器连接,所以如果在服务器上安装服务器套接字应用程序( AppServerSideSocket.jar )的主机名为badServer1.com且密钥为CN,则无关紧要。 privateKeyApp和publicKeyApp使用goodServer1.com进行设置,因为所有客户端都不validation密钥的主机名或CN属性。

贝娄展示了这种联系

  private static final HostnameVerifier DO_NOT_VERIFY = new HostnameVerifier() { public boolean verify(String hostname, SSLSession session) { return true; } }; System.setProperty("javax.net.ssl.trustStore", publicKey1); System.getProperties().setProperty("java.protocol.handler.pkgs", "javax.net.ssl.internal.www.protocol"); HttpsURLConnection.setDefaultHostnameVerifier(DO_NOT_VERIFY); ... SOAPConnectionFactory soapConn = SOAPConnectionFactory.newInstance(); SOAPConnection connection = soapConn.createConnection(); ... URL endpoint = new URL(hostname + ":" + port); 

但是新客户端( AppClientSideSocketNEW.jar )必须执行此validation,现在必须为此客户端提供新证书,其中CN属性的新值反映了服务器套接字所在的正确主机名CN。

我无法访问第二个客户端,我确信它会进行主机名validation。

所以我创建了两个新的密钥对证书( privateKeyAppNew和publicKeyAppNew ),显然在配置服务器使用这个新密钥后,使用这个新密钥对的服务器和使用这个新的公共publicKeyAppNew密钥的新客户端之间的通信成功了 -一对当然。

但我需要继续为老客户使用旧密钥对。 我想知道如何解决这个问题。

使用密钥管理器让我能够在客户端尝试连接时validation服务器应用程序上的客户端证书并选择适当的并使用正确的证书进行握手?

或者我需要在不同的端口中使用不同的ssl套接字连接哪种客户端?

“显而易见”的问题是你实际上没有使用你创建的SSLContext

 SSLServerSocketFactory sslSrvSockFact = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault(); 

这至少应该是:

 SSLServerSocketFactory sslSrvSockFact = (SSLServerSocketFactory) ctx.getServerSocketFactory(); 

问题是你必须在一个上下文或另一个上下文之间进行选择……

问题的解决方案是我几天前给你的其他类似问题的答案 :你需要实现自己的X509KeyManager才能选择你要使用的密钥。

无论您是要使用单个密钥库还是从两个密钥库加载密钥/证书都无关紧要:如果您真的想要,您当然可以实现getPrivateKeygetCertificateChain以便从两个不同的密钥库加载密钥/证书。在别名中。 但是,这将是不必要的复杂。 无论如何,您仍然必须根据别名选择执行某些操作,因此您也可以使用不同的别名从单个密钥存储区加载密钥/证书。

从服务器的角度来看,选择一个别名(以及密钥/证书对)的唯一方法是使用套接字中的可用内容(如果您使用的是X509ExtendedKeyManager则使用X509ExtendedKeyManager )。 由于Java 7不支持服务器名称指示(这将允许客户端在此选择过程之前告知它正在请求哪个主机名),您可能必须根据客户端IP地址或您的服务器IP地址执行此操作。正在使用(如果您有多个)。

使用两个私钥(密钥库)和两个公钥(信任库)

您似乎对密钥库和信任库的含义感到困惑。 除非您计划使用客户端证书身份validation,否则可以忽略服务器上的信任存储设置。 您可以使用默认值( null )作为SSLContext.init(...)的第二个参数。 您的“密钥库(密钥库)”是本地方(本例中为您的服务器)使用的信息,“信任库(密钥库)”用于确定要信任的远程方。

您要呈现给客户端的公钥(或者,要被准备好的证书)也在您的密钥库中,与您的私钥相关联,而不是在信任库中。

编辑:

线程“main”中的exceptionjava.lang.NoSuchMethodError:javax.net.ssl.SSLContext.setDefault(Ljavax / net / ssl / SSLContext;)at … initialiseManager(499)

NoSuchMethodError :

如果应用程序试图调用类的指定方法(静态或实例),并且该类不再具有该方法的定义,则抛出该exception。

通常,编译器会捕获此错误; 如果类的定义不兼容地更改,则此错误只能在运行时发生。

这与您的SSL设置无关。 不确定你在这里做了什么,但看起来你可能在Java 5 JRE上使用Java 6的代码(Java 6在SSLContext上没有setDefault )。 更重要的是,你似乎在这里使用Java的一般方式有些不对劲。

javax.net.ssl.SSLException:

没有可用的证书或密钥对应于已启用的SSL密码套件。

这可以很好地解释为你似乎没有使用你初始化的SSLContext的事实……

  • 如果密钥库中有两对私钥/证书。

我的答案仍然存在。 我会尝试让它更明确一点。 我在这里假设你的一个证书/私钥正在使用alias1和另一个alias2 。 如果你不确定,请找出使用keyool -list 。 由您来选择和设置它们。

 // Load the key store: change store type if needed KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType()); FileInputStream fis = new FileInputStream("/path/to/keystore"); try { ks.load(fis, keystorePassword); } finally { if (fis != null) { fis.close(); } } // Get the default Key Manager KeyManagerFactory kmf = KeyManagerFactory.getInstance( KeyManagerFactory.getDefaultAlgorithm()); kmf.init(ks, keyPassword); final X509KeyManager origKm = (X509KeyManager)kmf.getKeyManagers()[0]; X509KeyManager km = new X509KeyManager() { public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) { InetAddress remoteAddress = socket.getInetAddress(); if (/* remoteAddress has some conditions you need to define yourself */ { return "alias1"; } else { return "alias2"; } } public String chooseClientAlias(String[] keyType, Principal[] issuers, Socket socket) { // Delegate this other methods to origKm. origKm.chooseClientAlias(keyType, issuers, socket); } // Delegate this other methods to origKm, in the same way as // it was done for chooseClientAlias. } SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(new KeyManager[] { km }, null, null); SSLSocketFactory sslSocketFactory = sslContext.getSSLSocketFactory(); 
  • 如果你真的想要两个不同的密钥库。

执行相同操作,并在此基础上,在getCertificateChain(String alias) ,根据别名选择要使用的两个密钥库中的哪一个,并使用它来获取证书链。 getPrivateKey(...)

简单的方法是将所有内容放在一个密钥库中 – 所有密钥和所有信任证书。 这将消除你的问题。