7

我正在尝试在两个 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"
};

我得到相同的结果。

4

1 回答 1

14

在服务器端,您没有初始化 keystore/keymanagers,只有 truststore/trustmanagers: ctx.init(null, tmf.getTrustManagers(), null)

在服务器上,始终需要初始化密钥管理器来配置服务器证书。仅当您要使用客户端证书身份验证时才需要初始化信任库。(关于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 ”可以有多种含义。密钥库的含义之一是可以存储密钥和证书的实体,通常是文件。这样的密钥库可用于初始化信任管理器1(在这种情况下,它被称为truststore)或密钥管理器(在这种情况下,它被称为密钥库))。抱歉,不是我选择的名称,而是调用系统属性的方式。

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

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

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

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

于 2013-03-14T10:23:27.123 回答