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" ?

3voto

Cesar Martinez Points 21

Nous avons récemment rencontré un problème similaire "No subject alternative DNS name matching found", c'était un cauchemar car nous n'avions pu le reproduire que sur les serveurs de Production, avec un accès au débogage quasi nul. Le reste des environnements fonctionnait très bien. Notre pile technologique était JDK 1.8.x+, JBoss EAP 7+, une application Java Spring Boot et Okta en tant que fournisseur d'identité (le handshake SSL échouait lors de la récupération de la configuration bien connue depuis Okta, où Okta est disponible sur AWS Cloud - serveurs virtuels).

Finalement, nous avons découvert que (personne ne sait pourquoi) le serveur d'application JBoss EAP que nous utilisions avait une propriété système JVM supplémentaire:

jsse.enableSNIExtension = false

Cela empêchait l'établissement d'une connexion TLS et nous avons pu reproduire le problème en ajoutant cette même propriété/valeur dans d'autres environnements. La solution était donc simple : supprimer cette propriété et cette valeur indésirables.

D'après le document sur la sécurité Java, cette propriété est définie par défaut sur true pour Java 7+ (voir https://docs.oracle.com/javase/7/docs/technotes/guides/security/jsse/JSSERefGuide.html#InstallationAndCustomization)

  • Property système jsse.enableSNIExtension. Server Name Indication (SNI) est une extension TLS, définie dans la RFC 4366. Elle permet des connexions TLS à des serveurs virtuels, où plusieurs serveurs pour différents noms de réseau sont hébergés à une seule adresse réseau sous-jacente. Certains fournisseurs SSL/TLS très anciens peuvent ne pas être en mesure de gérer les extensions SSL/TLS. Dans ce cas, définissez cette propriété sur false pour désactiver l'extension SNI.

2voto

animo3991 Points 59

J'ai également rencontré le même problème avec un certificat auto-signé. En me référant à quelques-unes des solutions ci-dessus, j'ai essayé de régénérer le certificat avec le bon CN, c'est-à-dire l'adresse IP du serveur. Mais cela n'a pas fonctionné pour moi. Enfin, j'ai essayé de régénérer le certificat en ajoutant l'adresse SAN avec la commande mentionnée ci-dessous

**keytool -genkey -keyalg RSA -keystore keystore.jks -keysize 2048 -alias  -ext san=ip:**

Ensuite, j'ai démarré mon serveur et téléchargé les certificats du client via la commande openssl mentionnée ci-dessous

**openssl s_client -showcerts -connect :443 < /dev/null | openssl x509 -outform PEM > myCert.pem**

Ensuite, j'ai importé ce certificat client dans le fichier keystore par défaut de Java (cacerts) de ma machine cliente avec la commande mentionnée ci-dessous

**keytool -import -trustcacerts -keystore /home/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.242.b08-1.el7.x86_64/jre/lib/security/cacerts -alias  -file ./mycert.pem**

1voto

manoj Points 56

Ajouter l'entrée hôte avec l'ip correspondant au CN dans le certificat

CN=someSubdomain.someorganisation.com

maintenant mettre à jour l'ip avec le nom CN là où vous essayez d'accéder à l'URL.

Ça a marché pour moi.

1voto

GKislin Points 254

Pour Spring Boot RestTemplate:

  • ajouter la dépendance org.apache.httpcomponents.httpcore
  • utiliser NoopHostnameVerifier pour l'usine SSL:

    SSLContext sslContext = new SSLContextBuilder()
            .loadTrustMaterial(new URL("file:pathToServerKeyStore"), storePassword)
    //        .loadKeyMaterial(new URL("file:pathToClientKeyStore"), storePassword, storePassword)
            .build();
    SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE);
    CloseableHttpClient client = HttpClients.custom().setSSLSocketFactory(socketFactory).build();
    HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(client);
    RestTemplate restTemplate = new RestTemplate(factory);

1voto

Nizam Mahammad Points 126

Ce code fonctionnera comme un charme et utilisera l'objet restTemple pour le reste du code.

  RestTemplate restTemplate = new RestTemplate();   
  TrustStrategy acceptingTrustStrategy = new TrustStrategy() {
            @Override
            public boolean isTrusted(java.security.cert.X509Certificate[] x509Certificates, String s) {
                return true;
            }

        };

        SSLContext sslContext = null;
        try {
            sslContext = org.apache.http.ssl.SSLContexts.custom().loadTrustMaterial(null, acceptingTrustStrategy)
                    .build();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (KeyManagementException e) {
            e.printStackTrace();
        } catch (KeyStoreException e) {
            e.printStackTrace();
        }
        SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext, new NoopHostnameVerifier());
        CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(csf).build();
        HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
        requestFactory.setHttpClient(httpClient);

        restTemplate.setRequestFactory(requestFactory);
}

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