SSL:400 没有发送所需的证书

Posted

技术标签:

【中文标题】SSL:400 没有发送所需的证书【英文标题】:SSL: 400 no required certificate was sent 【发布时间】:2016-07-17 00:32:57 【问题描述】:

代码和输入

我正在尝试建立 SSL 连接,并且收到来自服务器的 400 No required SSL certificate was sent 响应。我正在以标准方式执行此操作,例如here;我运行 Java 8。

我的代码示例是:

    OkHttpClient client     = new OkHttpClient();
    KeyStore keyStoreClient = getClientKeyStore();
    KeyStore keyStoreServer = getServerKeyStore();
    String algorithm        = ALGO_DEFAULT;//this is defined as "PKIX"

    KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(algorithm);
    keyManagerFactory.init(keyStoreClient, PASSWORD_SERVER.toCharArray());

    SSLContext sslContext = SSLContext.getInstance("TLS");

    TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(algorithm);
    trustManagerFactory.init(keyStoreServer);

    sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), new SecureRandom());

    client.setSslSocketFactory(sslContext.getSocketFactory());
    client.setConnectTimeout(32, TimeUnit.SECONDS); // connect timeout
    client.setReadTimeout(32, TimeUnit.SECONDS);    // socket timeout

    return client;

这是我用来发送 GET 请求的代码:

public String get(String url) throws IOException

    String callUrl  = URL_LIVE.concat(url);
    Request request = new Request.Builder()
            .url(callUrl)
            .build();
    Response response = this.client.newCall(request).execute();
    return response.body().string();

我启用了:

System.setProperty("javax.net.debug", "ssl");

所以要查看调试消息 - 但那里没有错误/警告/警报,唯一的事情是在最后:

main, called close()
main, called closeInternal(true)
main, SEND TLSv1.2 ALERT:  warning, description = close_notify
main, WRITE: TLSv1.2 Alert, length = 26
main, called closeSocket(true)

这是我唯一可以视为“异常”的东西(但我怀疑它是)。

我试过了:

不同的协议禁用/启用。例如,对套接字强制 TLSv1 / TLSv1.1 没有成功。为了尝试,我将我的 ssl 工厂封装到另一个禁用/启用某些协议的工厂中。 禁用SSLv2Hello - 但它不会改变图片。 安装Oracle policies,因为之前有关于一些跳过算法的通知,并且此安装“解决”了该问题,但总体结果仍然相同。 设置System.setProperty("sun.security.ssl.allowUnsafeRenegotiation", "true"); - 蹩脚,但除了日志中的消息Allow unsafe renegotiation: true 之外没有任何改变(显然是“假”) 将KeyManagerFactory.getDefaultAlgorithm() 用作algorithm 用于KeyManagerFactory.getInstance() 呼叫。这引发了异常Get Key failed: Given final block not properly padded,据我所知,这是因为使用了错误的算法(阅读this)。我的默认算法是:SunX509 使用keytool -importcert -file /path/to/certificate -keystore keystore.jks -alias "Alias" 将证书直接添加到我的机器,然后通过System.setProperty("javax.net.ssl.trustStore","/path/to/keystore.jks"); 在我的程序中使用它并设置密码:System.setProperty("javax.net.ssl.trustStorePassword","mypassword");(我在keytool 中设置密码,然后使用yes 确认受信任的证书) ;请参阅 this 和 this 帖子。没有出现任何错误 - 但问题仍然存在。 将证书添加到全局密钥库(位于JAVA_PATH/jre/lib/security/cacerts 中的那个):keytool -import -trustcacerts -keystore cacerts -storepass changeit -noprompt -alias Alias -file /path/to/certificate(参见this);看起来导入操作成功了,但没有改变图片

调试中还有通知:ssl: KeyMgr: choosing key: 1 (verified: OK) - 由于我不是 SSL 专家,因此不确定它是否相关。

很遗憾,我看不到服务器端的日志,所以我无法观察到全貌。

问题

这个 400 错误的原因是什么,我该如何进一步发展(调试/尝试其他方法)?任何提示将不胜感激。

【问题讨论】:

除非您已创建 (1) 密钥对和 (2) 自签名证书或 (3) CSR 和 (4) 由 CA 签名,否则您没有客户端证书,并且这些操作都不能在 JDK 的信任库中有效地执行。 我有一个 pkcs12 格式的客户端证书。我尝试导出它,它实际上是用keytool -importkeystore -destkeystore -srckeystore cert -srcstoretype PKCS12 -srcstorepass certPass -alias 1 成功的,我知道certPass 这是有效的,因为我能够用它解锁 pkcs12 证书并在那里看到证书+私钥 我发现了问题。就像@EJP 说的,自签名证书。我现在都修好了。 【参考方案1】:

您的服务器正在请求客户端证书。您需要在客户端计算机上安装证书(由受信任的机构签署),并让您的程序知道它。

通常,您的服务器有一个由您组织的 IT 安全部门或某个全球受信任的 CA 签署的证书(密钥用途设置为“TLS 服务器身份验证”)。您应该从同一来源获得“TLS 客户端身份验证”证书。

如果您在 Wireshark 中捕获 TLS 握手,您将看到 ServerHello 消息后跟“客户端证书请求”,您会回复长度为 0 的“客户端证书”消息。您的服务器选择拒绝此操作。

【讨论】:

我有一个证书,我做到了:keytool -importcert -file /path/here -keystore keystore.jks -alias "Alias" 之后我设置了密码。在我的程序中,我做了:System.setProperty("javax.net.ssl.trustStore","/path/to/jks");System.setProperty("javax.net.ssl.trustStorePassword","password");,但我仍然有同样的问题。我做的导入正确吗?从here 和here 尝试过

以上是关于SSL:400 没有发送所需的证书的主要内容,如果未能解决你的问题,请参考以下文章

HTTP 400 - 错误请求

ACM 中缺少中间证书

HTTP 400 错误的请求!解决追加悬赏!

通过 httpget 发送 cookie 值但没有得到所需的响应

使用 apache httpClient 客户端在没有 ssl 证书验证的情况下将发布数据发送到 https

当服务器发送 SSL 证书副本时,连接是不是被加密?