web-dev-qa-db-fra.com

Alerte irrécupérable reçue: bad_certificate

J'essaie de configurer une connexion SSL Socket (et je fais ce qui suit sur le client)

  1. Je génère une demande de signature de certificat pour obtenir un certificat de client signé

  2. Maintenant, j'ai une clé privée (utilisée pendant la CSR), un certificat client signé et un certificat racine (obtenus hors bande).

  3. J'ajoute la clé privée et le certificat client signé à une chaîne de certificats, puis au gestionnaire de clés. et le certificat racine au gestionnaire de confiance . Mais je reçois une mauvaise erreur de certificat.

Je suis à peu près sûr que j'utilise les bons certificats. Devrais-je également ajouter le certificat de client signé au gestionnaire de confiance? Essayé que, pas de chance encore.

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

Y at-il une sorte de chaîne de certificats que je dois créer ici?
J'ai eu une p12 avec ces composants également et après avoir utilisé un code assez similaire, en ajoutant la clé privée au gestionnaire de clés et le certificat racine de p12 au gestionnaire de confiance, je pouvais le faire fonctionner. Mais maintenant, je dois le faire fonctionner sans la p12.

EDIT: Une trace de pile a été demandée. J'espère que cela devrait suffire. (NOTE: j'ai masqué les noms de fichiers)

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
12
highflyer

Vous devez également ajouter le certificat racine au magasin de clés.

11
user207421

Sous réserve que le certificat de serveur soit signé et valide, il vous suffit d'ouvrir la connexion comme d'habitude:

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

Notez que l'URL contient le schéma HTTPS pour indiquer l'utilisation de SSL.

Si le certificat du serveur est signé mais que vous y accédez en utilisant une adresse IP/nom de domaine différent de celui du certificat, vous pouvez contourner la vérification du nom d'hôte avec ceci:

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

HttpsURLConnection.setDefaultHostnameVerifier(hv);

Si le certificat n'est pas signé, vous devez l'ajouter au magasin de clés utilisé par la machine virtuelle Java ( commandes utiles ).

1
German

J'ai eu cette erreur quand j'ai enlevé ces 2 lignes. Si vous savez que votre magasin de clés contient les bons certificats, assurez-vous que votre code examine le magasin de clés approprié.

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

J'avais également besoin de cet argument VM: -Djavax.net.ssl.trustStore=/app/certs/keystore.jk Voir ici pour plus de détails: https://stackoverflow.com/a/34311797/1308453

0
Philip Rego