我正在尝试在两个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密钥。此代码从磁盘加载。
我做错了什么?
更新 我用这个数组添加了对两侧setEnabledCipherSuite的调用:
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"
};
我得到了相同的结果。
答案 0 :(得分:14)
在服务器端,您没有初始化密钥库/密钥管理器,只是信任库/信任管理器:ctx.init(null, tmf.getTrustManagers(), null)
。
在服务器上,始终需要初始化密钥管理器来配置服务器证书。只有在要使用客户端证书身份验证时,才需要初始化信任库。 (this question中有更多关于keymanager和trustmanager之间区别的细节。)
如果没有配置任何密钥管理器,则没有可用的基于RSA或DSA的证书,因此没有依赖证书进行身份验证的密码套件(默认情况下启用的所有密码套件)都可用。因此,客户端和服务器之间没有共同的密码套件。
你需要这样的东西:
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);
从您的示例中不清楚,但您当然不应该在客户端(作为信任库)和服务器端(作为密钥库)使用相同的密钥库:私钥应该只为服务器所知,并且不需要在客户的信任存储区中。
编辑:(我会尝试以不同的方式重新解释,因为每个人都不清楚。也许这可能会有所帮助。)
以下代码使用SSLContext
密钥管理器数组(第一个参数)初始化null
:ctx.init(null, tmf.getTrustManagers(), null)
。 (没有默认密钥管理器。)
密钥管理器是在代码运行的一侧管理您的(私有)密钥和证书的东西。在服务器上,密钥管理器负责处理服务器证书及其私钥。密钥管理器本身通常由“密钥库密钥库”初始化。 Java中的“ keystore ”可以有多种含义。 keystore 的含义之一是可以存储密钥和证书的实体,通常是文件。这样的密钥库可用于初始化信任管理器 1 (在这种情况下,它被称为 truststore )或密钥管理器(在这种情况下,它被称为密钥库)。对不起,不是我选择的名字,但这就是系统属性的调用方式。
当服务器配置了空密钥管理器时,它配置为没有任何证书和关联的私钥。因此,它没有任何RSA或DSA证书。因此,它无法使用任何*_RSA_*
或*_DSS_*
密码套件,无论它们是否已明确启用(由于缺少证书而无法自动禁用它们) )。这有效地丢弃了默认启用的任何密码套件(或者任何明确启用的任何此类密码套件)。因此,“没有共同的密码套件”。
简而言之,服务器端的SSLContext
需要配置证书及其私钥 2 。这是通过配置其密钥管理器来完成的。反过来,这通常是通过使用带有KeyManagerFactory
(而不是TrustManagerFactory
)的密钥库来完成的。
1:信任管理器使用本地信任锚(例如,可信CA证书)来评估远程方(即信任客户端证书的服务器或信任服务器证书的客户端)中的信任)。
2:JSSE支持的一些密码套件不需要证书,但它们是匿名密码套件(不安全)或Kerberos密码套件(需要完全不同地设置)。两者都默认禁用。