我正在尝试在两个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 管理器,仅初始化truststore/trustmanagers:ctx.init(null, tmf.getTrustManagers(), null)

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

没有配置任何 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 :私钥应该仅由服务器知道,并且不会不必位于客户的信任库中。

编辑:(我将尝试以另一种方式重新解释,因为尚不清楚每个人。也许可能会有帮助。)

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

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

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

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

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

2:JSSE支持的某些密码套件不需要证书,但是它们是匿名密码套件(不安全)或Kerberos密码套件(需要完全不同地设置)。默认情况下,两者均处于禁用状态。

09-30 17:49