在建立安全连接时,没有共同的密码套件

我正在尝试在两个Java项目之间建立安全连接,但我遇到了SSLHandshakeException(没有共同的密码套件)。 这是在两侧创建套接字的方法:

客户:

private SSLSocket getSocketConnection() throws SSLConnectionException { try { /* Load properties */ String keystore = properties.getProperty("controller.keystore"); String passphrase = properties.getProperty("controller.passphrase"); String host = properties.getProperty("controller.host"); int port = Integer.parseInt(properties .getProperty("controller.port")); /* Create keystore */ KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); keyStore.load(new FileInputStream(keystore), passphrase.toCharArray()); /* Get factory for the given keystore */ TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); tmf.init(keyStore); SSLContext ctx = SSLContext.getInstance("SSL"); ctx.init(null, tmf.getTrustManagers(), null); SSLSocketFactory factory = ctx.getSocketFactory(); return (SSLSocket) factory.createSocket(host, port); } catch (Exception e) { throw new SSLConnectionException( "Problem connecting with remote controller: " + e.getMessage(), e.getCause()); } } 

服务器:

 private SSLServerSocket getServerSocket() throws SSLConnectionException { try { /* Load properties */ Properties properties = getProperties("controller.properties"); String keystore = properties.getProperty("controller.keystore"); String passphrase = properties.getProperty("controller.passphrase"); int port = Integer.parseInt(properties .getProperty("controller.port")); /* Create keystore */ KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); keyStore.load(new FileInputStream(keystore), passphrase.toCharArray()); /* Get factory for the given keystore */ TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); tmf.init(keyStore); SSLContext ctx = SSLContext.getInstance("SSL"); ctx.init(null, tmf.getTrustManagers(), null); SSLServerSocketFactory factory = ctx.getServerSocketFactory(); return (SSLServerSocket) factory.createServerSocket(port); } catch (Exception e) { throw new SSLConnectionException( "Problem starting auth server: " + e.getMessage(), e.getCause()); } } 

我有一个用keytool生成的RSA密钥。 此代码从磁盘加载它。

我做错了什么?

更新:我在这个数组的两侧添加了对setEnabledCipherSuites的调用:

 String enableThese[] = { "SSL_RSA_WITH_3DES_EDE_CBC_SHA", "SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA", "SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA" }; 

我得到了相同的结果。

在服务器端,您没有初始化密钥库/密钥管理器,只有信任库/信任管理器: ctx.init(null, tmf.getTrustManagers(), null)

在服务器上,始终需要初始化密钥管理器来配置服务器证书。 只有在要使用客户端证书身份validation时才需要初始化信任库。 (关于keymanager和trustmanager之间的区别, 这个问题还有更多细节。)

如果没有配置任何密钥管理器,则没有可用的基于RSA或DSA的证书,因此没有依赖证书进行身份validation的密码套件(默认情况下启用的所有密码套件)都可用。 因此,您在客户端和服务器之间没有共同的密码套件。

你需要这样的东西:

 KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); kmf.init(keystore, password.toCharArray()); // That's the key's password, if different. // ... ctx.init(kmf.getKeyManagers(), null, null); 

从您的示例中不清楚,但您当然不应该在客户端(作为信任库)和服务器端(作为密钥库)使用相同的密钥库:私钥应该只为服务器所知,并且不会不需要在客户的信托商店。


编辑:(我会尝试以不同的方式重新解释,因为每个人都不清楚。也许它可能会有所帮助。)

以下代码使用密钥管理器的null数组(第一个参数)初始化SSLContextctx.init(null, tmf.getTrustManagers(), null) 。 (没有默认密钥管理器。)

密钥管理器是在代码运行的一侧管理您的 (私有)密钥和证书。 在服务器上,密钥管理器负责处理服务器证书及其私钥。 密钥管理器本身通常由“密钥库密钥库”初始化。 Java中的“ keystore ”可以有多种含义。 密钥库的含义之一是可以存储密钥和证书的实体,通常是文件。 这样的密钥库可以用于初始化信任管理器1 (在这种情况下,它被称为信任库 )或密钥管理器(在这种情况下,它被称为密钥库 )。 对不起,不是我选择的名字,但这就是系统属性的调用方式。

当服务器配置了空密钥管理器时,它配置为没有任何证书和关联的私钥。 因此,它没有任何RSA或DSA证书。 因此,它无法使用任何*_RSA_**_DSS_*密码套件,无论它们是否已明确启用(它们将因缺少与其一起使用的证书而自动禁用)。 这有效地丢弃了默认启用的任何密码套件(或者任何明确启用的任何此类密码套件)。 因此,“没有共同的密码套件”。

简而言之,服务器端的SSLContext需要配置证书及其私钥2 。 这是通过配置其密钥管理器来完成的。 反过来,这通常通过使用KeyManagerFactory (而不是TrustManagerFactory )的密钥库来完成。

1:信任管理器使用本地信任锚(例如,可信CA证书)来评估远程方(即​​信任客户端证书的服务器或信任服务器证书的客户端)中的信任。

2:JSSE支持的一些密码套件不需要证书,但它们是匿名密码套件(不安全)或Kerberos密码套件(需要完全不同地设置)。 两者都默认禁用。