建立安全连接时没有共同的密码套件 [英] No cipher suites in common while establishing a secure connection

查看:29
本文介绍了建立安全连接时没有共同的密码套件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm trying to establish a secure connection between two Java projects but I'm getting a SSLHandshakeException (no cipher suites in common). This are the methods to create sockets in both sides:

客户:

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 密钥.此代码从磁盘加载它.

I have a RSA key generated with keytool. This code load it from disk.

我做错了什么?

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

UPDATE: I added the a call to setEnabledCipherSuites in both sides with this array:

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).

On the server side, you're not initialising the keystore/keymanagers, only the truststore/trustmanagers: ctx.init(null, tmf.getTrustManagers(), null).

在服务器上,始终需要初始化密钥管理器以配置服务器证书.仅当您想使用客户端证书身份验证时才需要初始化信任库.(这个问题中有更多关于keymanager和trustmanager的区别的细节.)

On the server, initialising the keymanager is always necessary to configure the server certificate. Initialising the truststore is only necessary when you want to use client-certificate authentication. (There are more details in this question for the difference between keymanager and trustmanager.)

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

Without any keymanager configured, there is no RSA or DSA based certificate available, so no cipher suite that rely on a certificate for authentication (all the ones enabled by default are) are available. Hence, you get no cipher suites in common between the client and the server.

你需要这样的东西:

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);

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

It's not clear from your example, but you shouldn't of course use the same keystore on the client (as a truststore) and on the server side (as a keystore): the private key should only be known to the server, and doesn't need to be in the client's trust store.

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

(I'll try to re-explain in a different way, since it wasn't clear for everyone. Perhaps it might help.)

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

The following code initialises the SSLContext with a null array of key managers (the first argument): ctx.init(null, tmf.getTrustManagers(), null). (There is no default key manager.)

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

The key manager is what manages your (private) keys and certificates, on the side where the code is running. On the server, the key manager is what's responsible for handling the server certificate and its private key. The key manager is itself usually initialised by the "keystore keystore". "keystore" in Java can have multiple meanings. One of the meaning of keystore is the entity into which keys and certificates can be stored, typically a file. Such a keystore can be used to initialise a trust manager1 (in which case it's referred to as the truststore) or a key manager (in which case it's referred to as a keystore). Sorry, not my choice of names, but that's the way the system properties are called.

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

When the server is configured with a null key manager, it is configured without any certificate and associated private key. Therefore, it doesn't have any RSA or DSA certificate. Therefore, it won't be able to use any of the *_RSA_* or *_DSS_* cipher suites, whether they've been explicitly enabled or not (they will be disabled automatically by lack of certificate to use with them). This effectively discards any cipher suite enabled by default (or any such cipher suite enabled explicitly anyway). Hence, there is "no cipher suite in common".

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

In short, an SSLContext on the server side needs to be configured with a certificate and its private key2. This is done by configuring its key manager. In turn, this is often done by using a keystore with a KeyManagerFactory (not a TrustManagerFactory).

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

1: The trust manager uses local trust anchors (e.g. trusted CA certificates) to evaluate trust in a remote party (i.e. a server trusting a client certificate or a client trusting a server certificate).

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

这篇关于建立安全连接时没有共同的密码套件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆