Received fatal alert: bad_certificate
Asked Answered
C

4

24

I am trying to setup a SSL Socket connection (and am doing the following on the client)

  1. I generate a Certificte Signing Request to obtain a signed client certificate

  2. Now I have a private key (used during the CSR), a signed client certificate and root certificate (obtained out of band).

  3. I add the private key and signed client certificate to a cert chain and add that to the key manager. and the root cert to the trust manager. But I get a bad certificate error.

I am pretty sure I am using the right certs. Should I add the signed client cert to the trust manager as well? Tried that, no luck still.

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

Is there some sort of certificate chain that I need to create here?
I had a p12 with these components as well and upon using pretty similar code, adding the private key to the keymanager and the root cert from p12 to the trust manager I could make it work. But now I need to make it work without the p12.

EDIT: Stack trace was requested. Hope this should suffice. (NOTE: I masked the filenames)

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
Chemisette answered 3/8, 2012 at 16:31 Comment(0)
C
19

You need to add the root cert to the keystore as well.

bad_certificate means:

A certificate was corrupt, contained signatures that did not verify correctly, etc.

[RFC 2246].

Chockfull answered 3/8, 2012 at 21:54 Comment(2)
Thanks again. I added the client cert and private key to the first keystore (k1) and then k1 to the keymanagerfactory. Then the root cert to keystore (k2) and k2 to the trustmanagerfactoryChemisette
Might you have any other suggestions? I am getting the same error. With -Djavax.net.debug=ssl. I also see Empty client certificate chain. But the issue isn't resolved when I add the root cert to my keystore or truststore.Frivolous
A
0

You will also get this error if one of the certificates used within the JKS has passed its expiry date.

In my specific test, I was using JRE8 to IBM MQ over SSL and the personal certificate had passed its expiration date. I only mention this in case that bad_certificate is symptomatic of the JRE version or an IBM MQ SSL handshake.

This doesn't necessarily answer the question, in that the author said that they had just generated a certificate, however if someone Google's the error (as I did) you may end up here.

Anderegg answered 9/3, 2023 at 5:10 Comment(0)
N
-2

I got this error when I removed these 2 lines. If you know your keystore has the right certs, make sure your code is looking at the right keystore.

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

I also needed this VM argument: -Djavax.net.ssl.trustStore=/app/certs/keystore.jk See here for more details: https://mcmap.net/q/474165/-pass-vm-argument-to-apache-tomcat-duplicate

Nertie answered 29/6, 2017 at 22:38 Comment(2)
Well of course you did. There is no default keystore. Not an answer to this question.Chockfull
I agree this is not an answer to the question, however it is related and may help those who stumble on this post by way of general "bad_certificate" search. That's exactly my case.Sullins
C
-4

Provided that the server certificate is signed and valid, you only need to open the connection as usual:

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

Note that the URL has the HTTPS schema to indicate the use of SSL.

If the server's certificate is signed but you are accessing using a different IP address/domain name than the one in the certificate, you can bypass hostname verification with this:

HostnameVerifier hv = new HostnameVerifier() {
    public boolean verify(String urlHostName,SSLSession session) {
        return true;
    }
};

HttpsURLConnection.setDefaultHostnameVerifier(hv);

If the certificate is not signed then you need to add it to the keystore used by the JVM (useful commands).

Constipation answered 3/8, 2012 at 18:0 Comment(1)
He is 'opening the connection as usual' and it isn't working. He isn't using HTTPS. You don't need a HostnameVerifier for SSL, only for HTTPS. The one you have posted is radically insecure and should be so noted, or not posted at all. Not an answer.Chockfull

© 2022 - 2024 — McMap. All rights reserved.