【问题标题】:No cipher suites in common while establishing a secure connection建立安全连接时没有共同的密码套件
【发布时间】:2013-03-02 14:01:11
【问题描述】:

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

我得到了同样的结果。

【问题讨论】:

  • 难以置信。你确定你没有在任何地方打电话给setEnabledCipherSuites()吗?
  • 我不这样做。确实我不知道那个方法是做什么的。我去谷歌看看。
  • @EJP 我在双方都添加了对该方法的调用(更新后),但我得到了同样的异常。
  • 这个想法是删除任何限制密码套件的代码,而不是添加它。你让事情变得更糟了。如果您在两端都有标准 Java,那么您描述的情况是不可能的,因为 Java 支持数十种密码套件,并且与服务器和客户端相同。肯定还有更多。

标签: java ssl rsa jsse


【解决方案1】:

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

在服务器上,始终需要初始化密钥管理器来配置服务器证书。仅当您要使用客户端证书身份验证时才需要初始化信任库。 (关于keymanager和trustmanager的区别,this question有更详细的介绍。)

如果没有配置任何密钥管理器,就没有可用的基于 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);

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


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

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

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

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

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

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

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

【讨论】:

  • 非常感谢。这解决了我的问题。就一个问题。我该怎么做才能为两者创建不同的密钥库。你能给我指出一篇相关的文章吗?感谢您的帮助。
  • 最简单的可能是导出证书并将其重新导入新的密钥库。使用keytool -list 查找别名,然后使用keytool -export -keystore ... -alias ... -file host.crtkeytool -import ...
  • 我认为投反对票的人觉得这个答案有点令人困惑。我觉得这让我自己很困惑,但你链接的问题有一个更好的答案。但是,我仍然感到困惑:(
  • @MelvinLai,我刚刚添加了一些文字,也许它可能会有所帮助。
  • 谢谢。这个解释绝对比网上的例子好! :D
猜你喜欢
  • 1970-01-01
  • 2011-04-09
  • 2018-01-31
  • 1970-01-01
  • 2015-12-06
  • 2020-01-07
  • 1970-01-01
  • 2015-07-27
  • 1970-01-01
相关资源
最近更新 更多