我尝试并阅读了许多不同的内容,但不得不承认我无法解决这个问题,并且无法找到任何有关内容的信息。
我试图编写一个Java客户端应用程序,可以在单个会话期间与不同的服务器(使用不同的密钥)建立SSL连接。所需的工作流程是:
1a)客户端基于密钥库1和证书1与服务器1建立SSL连接(ssl1) 1b)客户端和服务器1交换数据然后关闭ssl1 2a)客户端基于密钥库2和证书2与服务器2建立SSL连接(ssl2) 2b)客户端和服务器2交换数据然后关闭ssl2
我无法实施此工作流程。我的客户端可以在给定的会话中执行上面的1或2但是当我尝试在同一会话中执行1和2时,我在尝试第二次连接的握手时会收到以下错误:
javax.net.ssl.SSLException:连接已关闭:javax.net.ssl.SSLHandshakeException:sun.security.validator.ValidatorException:PKIX路径验证失败:java.security.cert.CertPathValidatorException:签名检查失败
显然,第二个连接的证书未被接受/识别。以下说明了我客户的代码。
首先,我构建一个包含2个KeyManagerFactory实例的List - 每个连接1个。这是我执行此操作的方法 - ksFileList数组包含两个连接中涉及的每个jks文件的文件路径的值。 keyManagerFactoryList包含2个KeyManagerFactory实例。
public static List getKeyManagerFactories(String[] ksFileList) {
List keyManagerFactoryList = new ArrayList<KeyManagerFactory>();
String ksFile;
for (int i = 0; i < ksFileList.length; i++) {
ksFile = ksFileList[i];
try {
KeyStore ks = KeyStore.getInstance("JKS");
ks.load(new FileInputStream(ksFile), "passdemo".toCharArray());
KeyManagerFactory factory =
KeyManagerFactory.getInstance("SunX509");
factory.init(ks, "passdemo".toCharArray());
keyManagerFactoryList.add(factory);
} catch (Exception e) {
e.printStackTrace();
}
}
return keyManagerFactoryList;
}
其次,我构建了一个List,其中包含2个TrustManagerFactory实例 - 我尝试用于连接的每个证书都有1个实例。这是我执行此操作的方法 - ksFileList数组与上面相同。 trustManagerFactoryList包含连接所需的2个TrustManagerFactory实例。
public static List getTrustManagerFactories(String[] ksFileList) {
List trustManagerFactoryList = new ArrayList<KeyManagerFactory>();
String ksFile;
for (int i = 0; i < ksFileList.length; i++) {
ksFile = ksFileList[i];
try {
KeyStore ks = KeyStore.getInstance("JKS");
ks.load(new FileInputStream(ksFile), "passdemo".toCharArray());
TrustManagerFactory factory =
TrustManagerFactory.getInstance("SunX509");
factory.init(ks);
trustManagerFactoryList.add(factory);
} catch (Exception e) {
e.printStackTrace();
}
}
return trustManagerFactoryList;
}
获得上面的列表后,我将它们传递给名为testServerA和testServerB的方法,以执行所需的工作流程。这些方法尝试与不同的运行服务器建立SSL连接。完成握手后,连接到服务器会向打印出的客户端发送一条消息。 testServerA的代码如下:
public static void testServerA(List keyManagerFactoryList, List trustManagerFactoryList) {
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
SSLSocketFactory f = (SSLSocketFactory) SSLSocketFactory.getDefault();
try {
SSLContext sc = SSLContext.getInstance("TLS");
KeyManagerFactory kmf = (KeyManagerFactory)keyManagerFactoryList.get(0);
TrustManagerFactory tmf = (TrustManagerFactory)trustManagerFactoryList.get(0);
sc.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
SSLSocket client = (SSLSocket) f.createSocket("localhost", 7777);
printSocketInfo(client);
client.startHandshake();
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(client.getOutputStream()));
BufferedReader reader = new BufferedReader(new InputStreamReader(client.getInputStream()));
String content;
while ((content=reader.readLine())!= null) {
System.out.println("Client: " + content);
writer.newLine();
writer.flush();
writer.close();
reader.close();
client.close();
}
} catch (Exception e) {
System.err.println(e.toString());
}
}
testServerB方法类似于上述方法,除了1)变量&#34; kmf&#34;和&#34; tmf&#34;使用管理器工厂阵列列表的索引1处的对象而不是索引0和2处的对象设置上面的内容。)为本地主机上的端口8888创建SSLSocket客户端。
当我的客户端在运行时执行testServerA和testServerB时,无论哪个方法首先调用,第二个调用导致上面提供的CertPathValidatorException。该异常确认连接在握手阶段失败。
不幸的是,上面提供的代码是我在所需工作流程中的最佳尝试,我对如何向前推进感到茫然。我最好的猜测是我的客户端失败了因为我正在使用SSLContext实例&#34; init&#34;为每个连接设置我想要的KeyManager和TrustManager对象的方法。虽然此方法适用于第一个连接,但第二个连接失败,因为第一个连接中的KeyManager和/或TrustManager对象未正确更新。另一个猜测是SSLContext&#34; init&#34;方法只能在给定的会话中有效调用一次。无论如何我的猜测都是正确的吗?
感谢您的关注 - 我很感激我能得到的任何建议或想法。
-----补充帖子1 -----------------
当我尝试建立两个服务器连接时,我的客户端应用程序出现错误输出(抱歉我没有格式化)。注意DCLKS&#34; op&#34; op打印输出,这是服务器2(或B)在成功握手后发送给客户端的内容。
当我尝试建立两个服务器连接并使用下面建议的getDefault调用时,我的客户端应用程序输出错误...
在这种情况下,即使通过客户端和服务器B完成握手,我的客户端也没有收到服务器2 / B发送的内容(而是抛出SocketException)。