收到致命警报:坏证书。

23

我正在尝试建立一个SSL套接字连接(并在客户端上执行以下操作)

  1. 我生成了一个证书签名请求以获得已签名的客户端证书

  2. 现在我有一个私钥(在CSR期间使用),一个已签名的客户端证书和根证书(在带外获取)。

  3. 我将私钥和已签名的客户端证书添加到证书链中,并将其添加到密钥管理器。将根证书添加到信任管理器。 但是我遇到了一个坏证书错误。

我很确定我正在使用正确的证书。我应该也将已签名的客户端证书添加到信任管理器吗?尝试了这个,仍然没有成功。

//I add the private key and the client cert to KeyStore ks
FileInputStream certificateStream = new FileInputStream(clientCertFile);
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
java.security.cert.Certificate[] chain = {};
chain = certificateFactory.generateCertificates(certificateStream).toArray(chain);
certificateStream.close();
String privateKeyEntryPassword = "123";
ks.setEntry("abc", new KeyStore.PrivateKeyEntry(privateKey, chain),
        new KeyStore.PasswordProtection(privateKeyEntryPassword.toCharArray()));

//Add the root certificate to keystore jks
FileInputStream is = new FileInputStream(new File(filename));
CertificateFactory cf = CertificateFactory.getInstance("X.509");
java.security.cert.X509Certificate cert = (X509Certificate) cf.generateCertificate(is);
System.out.println("Certificate Information: ");
System.out.println(cert.getSubjectDN().toString());
jks.setCertificateEntry(cert.getSubjectDN().toString(), cert);

//Initialize the keymanager and trustmanager and add them to the SSL context
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(ks, "123".toCharArray());
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
tmf.init(jks);

我需要创建一些证书链吗?
我之前也有一个包含这些组件的p12文件,使用相似的代码,将私钥添加到keymanager和p12中的根证书添加到trust manager后,我可以使其正常工作。但现在我需要在没有p12的情况下让它工作。

编辑:请求堆栈跟踪。希望这足够了。(注意:我掩盖了文件名)

Caused by: javax.net.ssl.SSLHandshakeException: Received fatal alert: bad_certificate
at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Alerts.java:174)
at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Alerts.java:136)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.recvAlert(SSLSocketImpl.java:1720)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:954)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1138)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1165)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1149)
at client.abc2.openSocketConnection(abc2.java:33)
at client.abc1.runClient(abc1.java:63)
at screens.app.abc.validateLogin(abc.java:197)
... 32 more
4个回答

19
您需要将根证书添加到密钥库中。

4
再次感谢。我已将客户端证书和私钥添加到第一个密钥库(k1)中,然后将k1添加到密钥管理器工厂中。接着将根证书添加到密钥库(k2)中,再将k2添加到信任管理器工厂中。 - highflyer

0

如果JKS中使用的证书已过期,您也会收到此错误。

在我的具体测试中,我正在使用JRE8通过SSL连接到IBM MQ,而个人证书已过期。我提到这一点只是为了说明bad_certificate可能是JRE版本或IBM MQ SSL握手的症状。

这并不能完全回答问题,因为作者说他们刚刚生成了一个证书,但如果有人谷歌这个错误(就像我一样),他们可能会在这里找到答案。


-2

当我删除这两行时,出现了这个错误。如果您知道您的密钥库具有正确的证书,请确保您的代码正在查看正确的密钥库。

System.setProperty("javax.net.ssl.keyStore", <keystorePath>));
System.setProperty("javax.net.ssl.keyStorePassword",<keystorePassword>));

我也需要这个虚拟机参数:-Djavax.net.ssl.trustStore=/app/certs/keystore.jk 更多细节请参见: https://dev59.com/v2ct5IYBdhLWcg3wCJB-#34311797


1
当然,你做了。没有默认密钥库。这不是对这个问题的答案。 - user207421
1
我同意这不是对问题的回答,但它与之相关,可能会帮助那些通过一般的“bad_certificate”搜索偶然发现这篇文章的人。这正是我的情况。 - Jaroslav Záruba

-4

只要服务器证书被签名,并且有效,您只需要像往常一样打开连接:

import java.net.*;
import java.io.*;

public class URLConnectionReader {
    public static void main(String[] args) throws Exception {
        URL google = new URL("https://www.google.com/");
        URLConnection yc = google.openConnection();
        BufferedReader in = new BufferedReader(new InputStreamReader(
                                    yc.getInputStream()));
        String inputLine;
        while ((inputLine = in.readLine()) != null) 
            System.out.println(inputLine);
        in.close();
    }
}

请注意,URL使用HTTPS模式表示使用SSL。
如果服务器的证书已签名,但您正在使用与证书中不同的IP地址/域名访问,则可以使用以下方法绕过主机名验证:
HostnameVerifier hv = new HostnameVerifier() {
    public boolean verify(String urlHostName,SSLSession session) {
        return true;
    }
};

HttpsURLConnection.setDefaultHostnameVerifier(hv);

如果证书未签名,则需要将其添加到JVM使用的密钥库中(有用的命令)。

5
他正在像往常一样“打开连接”,但并没有成功。他没有使用HTTPS。对于SSL,您不需要HostnameVerifier,只需要对于HTTPS。您发布的那个HostnameVerifier存在极大的安全漏洞,应该加以说明或者根本不要发布。这不是一个答案。 - user207421

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接