152 votes

Comment corriger l'erreur "java.security.cert.CertificateException: aucun nom alternatif de sujet présent"?

Je dispose d'un client de service Web Java, qui consomme un service Web via HTTPS.

import javax.xml.ws.Service;

@WebServiceClient(name = "ISomeService", targetNamespace = "http://tempuri.org/", wsdlLocation = "...")
public class ISomeService
    extends Service
{

    public ISomeService() {
        super(__getWsdlLocation(), ISOMESERVICE_QNAME);
    }

Lorsque je me connecte à l'URL du service (https://AAA.BBB.CCC.DDD:9443/ISomeService), je reçois l'exception java.security.cert.CertificateException: Aucun nom alternatif de sujet présent.

Pour corriger cela, j'ai d'abord exécuté openssl s_client -showcerts -connect AAA.BBB.CCC.DDD:9443 > certs.txt et obtenu le contenu suivant dans le fichier certs.txt :

CONNECTED(00000003)
---
Certificate chain
 0 s:/CN=someSubdomain.someorganisation.com
   i:/CN=someSubdomain.someorganisation.com
-----BEGIN CERTIFICATE-----
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
-----END CERTIFICATE-----
---
Server certificate
subject=/CN=someSubdomain.someorganisation.com
issuer=/CN=someSubdomain.someorganisation.com
---
No client certificate CA names sent
---
SSL handshake has read 489 bytes and written 236 bytes
---
New, TLSv1/SSLv3, Cipher is RC4-MD5
Server public key is 512 bit
Compression: NONE
Expansion: NONE
SSL-Session:
    Protocol  : TLSv1
    Cipher    : RC4-MD5            
    Session-ID: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
    Session-ID-ctx:                 
    Master-Key: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
    Key-Arg   : None
    Start Time: 1382521838
    Timeout   : 300 (sec)
    Verify return code: 21 (unable to verify the first certificate)
---

À ma connaissance, je dois maintenant

  1. extraire la partie de certs.txt entre -----BEGIN CERTIFICATE----- et -----END CERTIFICATE-----,
  2. le modifier afin que le nom du certificat soit égal à AAA.BBB.CCC.DDD et
  3. puis importer le résultat en utilisant keytool -importcert -file fileWithModifiedCertificate (où fileWithModifiedCertificate est le résultat des opérations 1 et 2).

Est-ce correct ?

Si oui, comment puis-je faire fonctionner le certificat de l'étape 1 avec une adresse IP (AAA.BBB.CCC.DDD) ?

Mise à jour 1 (23.10.2013 15:37 MSK) : Dans une réponse à une question similaire, j'ai lu ce qui suit :

Si vous n'avez pas le contrôle de ce serveur, utilisez son nom d'hôte (à condition qu'il y ait au moins un CN correspondant à ce nom d'hôte dans le certificat existant).

Que signifie exactement "utiliser" ?

25voto

Abhishek Galoda Points 589

J'ai résolu ce problème de la bonne manière en ajoutant les noms alternatifs de sujet dans le certificat plutôt que de faire des changements dans le code ou de désactiver SSL contrairement à ce que suggèrent d'autres réponses ici. Si vous regardez clairement l'exception dit que les "noms alternatifs de sujet manquent" alors la bonne façon devrait être de les ajouter

Veuillez consulter ce lien pour comprendre étape par étape.

L'erreur ci-dessus signifie que votre fichier JKS ne contient pas le domaine requis sur lequel vous essayez d'accéder à l'application. Vous devrez utiliser OpenSSL et l'outil clé pour ajouter plusieurs domaines

  1. Copiez le openssl.cnf dans un répertoire en cours

  2. echo '[ subject_alt_name ]' >> openssl.cnf

  3. echo 'subjectAltName = DNS:example.mydomain1.com, DNS:example.mydomain2.com, DNS:example.mydomain3.com, DNS: localhost'>> openssl.cnf

  4. openssl req -x509 -nodes -newkey rsa:2048 -config openssl.cnf -extensions subject_alt_name -keyout private.key -out self-signed.pem -subj '/C=gb/ST=edinburgh/L=edinburgh/O=mygroup/OU=servicing/CN=www.example.com/emailAddress=postmaster@example.com' -days 365

  5. Exporter le fichier clé publique (.pem) au format PKS12. Cela vous demandera un mot de passe

    openssl pkcs12 -export -keypbe PBE-SHA1-3DES -certpbe PBE-SHA1-3DES -export -in
    self-signed.pem -inkey private.key -name myalias -out keystore.p12
  6. Créer un .JKS à partir du PEM auto-signé (Keystore)

    keytool -importkeystore -destkeystore keystore.jks -deststoretype PKCS12 -srcstoretype PKCS12 -srckeystore keystore.p12
  7. Générer un certificat à partir du Keystore ou fichier JKS ci-dessus

    keytool -export -keystore keystore.jks -alias myalias -file selfsigned.crt
  8. Puisque le certificat ci-dessus est auto-signé et n'est pas validé par une AC, il doit être ajouté au Truststore (fichier Cacerts à l'emplacement ci-dessous pour MAC, pour Windows, découvrez où votre JDK est installé.)

    sudo keytool -importcert -file selfsigned.crt -alias myalias -keystore /Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/jre/lib/security/cacerts

Réponse originale publiée sur ce lien ici.

7voto

lifesoordinary Points 73

Vous ne voudrez peut-être pas désactiver toutes les vérifications SSL et donc vous pouvez simplement désactiver la vérification du nom d'hôte via ceci, ce qui est un peu moins effrayant que l'alternative :

HttpsURLConnection.setDefaultHostnameVerifier(
    SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);

[MODIFICATION]

Comme mentionné par conapart3 SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER est désormais obsolète, il pourrait donc être supprimé dans une version ultérieure. Vous pourriez ainsi être amené à créer votre propre solution à l'avenir, bien que je recommande toujours d'éviter toutes solutions où toutes les vérifications sont désactivées.

4voto

Shahriar Points 88

Mon problème avec cette erreur a été résolu en utilisant l'URL complète "qatest.ourCompany.com/webService" au lieu de simplement "qatest/webService". La raison était que notre certificat de sécurité avait un joker, c'est-à-dire "*.ourCompany.com". Une fois que j'ai mis l'adresse complète, l'exception a disparu. J'espère que cela vous aide.

4voto

Himadri Pant Points 85

J'ai déjà répondu à cela dans https://stackoverflow.com/a/53491151/1909708.

Cela échoue car ni le nom commun du certificat (CN dans le certificat Sujet) ni aucun des noms alternatifs (Subject Alternative Name dans le certificat) ne correspondent à l'adresse ou à l'IP cible.

Par exemple, à partir d'une JVM, lors de la tentative de connexion à une adresse IP (WW.XX.YY.ZZ) et non au nom DNS (https://stackoverflow.com), la connexion HTTPS échouera car le certificat stocké dans le magasin de confiance Java cacerts s'attend à ce que le nom commun (ou un nom alternatif de certificat comme stackexchange.com ou *.stackoverflow.com, etc.) corresponde à l'adresse cible.

Veuillez vérifier : https://docs.oracle.com/javase/8/docs/technotes/guides/security/jsse/JSSERefGuide.html#HostnameVerifier

    HttpsURLConnection urlConnection = (HttpsURLConnection) new URL("https://WW.XX.YY.ZZ/api/verify").openConnection();
    urlConnection.setSSLSocketFactory(socketFactory());
    urlConnection.setDoOutput(true);
    urlConnection.setRequestMethod("GET");
    urlConnection.setUseCaches(false);
    urlConnection.setHostnameVerifier(new HostnameVerifier() {
        @Override
        public boolean verify(String hostname, SSLSession sslSession) {
            return true;
        }
    });
    urlConnection.getOutputStream();

Au-dessus, nous avons passé un objet HostnameVerifier implémenté qui retourne toujours true :

new HostnameVerifier() {
        @Override
        public boolean verify(String hostname, SSLSession sslSession) {
            return true;
        }
    }

3voto

Shehan Simen Points 31

Comme quelqu'un l'a mentionné précédemment, j'ai ajouté le code suivant (avec lambda) juste avant de créer l'objet RestTemplate, et cela fonctionne bien. Ce n'est que pour ma classe de test interne, donc je trouverai une meilleure solution pour le code de production.

javax.net.ssl.HttpsURLConnection.setDefaultHostnameVerifier(
            (hostname, sslSession) -> true);

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X