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

标签 java ssl rsa jsse

我正在尝试在两个 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 key 。此代码从磁盘加载它。

我做错了什么?

更新: 我用这个数组在两侧添加了对 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"
};

我得到了相同的结果。

最佳答案

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

在服务器上,初始化 key 管理器始终是配置服务器证书所必需的。仅当您要使用客户端证书身份验证时才需要初始化信任库。 (关于 keymanager 和 trustmanager 之间的区别,在 this question 中有更多详细信息。)

在没有配置任何 key 管理器的情况下,没有基于 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);

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


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

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

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

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

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

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

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

关于java - 建立安全连接时没有共同的密码套件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15405581/

相关文章:

java - 为单个 Elastic Beanstalk TOMCAT 实例配置 SSL 证书

java - 嵌套类有静态变量吗?

java - 如何让 ReSTLet 客户端忽略 SSL 证书问题

apache - 使用 mod_jk 的 HTTPD + JBOSS 通信

c# - 使用 aspnet_regiis 获取 RSA 自定义容器 key 列表

java - 如何在验证错误时添加 css 类到列表项

java - 如何将 txt 文件从精确格式的 url 解析为 TextView ?

JRE 1.5 上的 java.lang.IllegalArgumentException : TLSv1. 2

java - 当数据数组的第一个条目为零时,使用 RSA 加密的数组太短

ios - 如何使用 Linux openssl 为 iOS 生成 CSR?