it-swarm.com.de

Wie behebt man den Fehler "Java.security.cert.CertificateException: Keine alternativen Antragstellernamen vorhanden"?

Ich habe einen Java-Web-Service-Client, der einen Web-Service über HTTPS benötigt.

import javax.xml.ws.Service;

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

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

Wenn ich mich mit der Service-URL (https://AAA.BBB.CCC.DDD:9443/ISomeService) verbinde, erhalte ich die Ausnahme Java.security.cert.CertificateException: No subject alternative names present.

Um das Problem zu beheben, habe ich zuerst openssl s_client -showcerts -connect AAA.BBB.CCC.DDD:9443 > certs.txt ausgeführt und folgenden Inhalt in der Datei certs.txt erhalten:

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

AFAIK, jetzt muss ich

  1. extrahiere den Teil von certs.txt zwischen -----BEGIN CERTIFICATE----- und -----END CERTIFICATE-----,
  2. Ändern Sie es so, dass der Zertifikatsname gleich AAA.BBB.CCC.DDD und ist
  3. importieren Sie dann das Ergebnis mit keytool -importcert -file fileWithModifiedCertificate (wobei fileWithModifiedCertificate das Ergebnis der Operationen 1 und 2 ist).

Ist das richtig?

Wenn ja, wie kann ich das Zertifikat von Schritt 1 aus mit IP-basierter Adresse (AAA.BBB.CCC.DDD) arbeiten lassen?

Update 1 (23.10.2013 15:37 MSK): In einer Antwort auf eine ähnliche Frage habe ich Folgendes gelesen:

Wenn Sie nicht die Kontrolle über diesen Server haben, verwenden Sie seinen Hostnamen (vorausgesetzt, dass Dass mindestens ein CN mit diesem Hostnamen im vorhandenen Cert übereinstimmt).

Was genau bedeutet "verwenden"?

77
DP_

Ich habe das Problem behoben, indem HTTPS-Überprüfungen mit dem hier vorgestellten Ansatz deaktiviert wurden: here :

Ich habe folgenden Code in die ISomeService-Klasse eingefügt:

static {
    disableSslVerification();
}

private static void disableSslVerification() {
    try
    {
        // Create a trust manager that does not validate certificate chains
        TrustManager[] trustAllCerts = new TrustManager[] {new X509TrustManager() {
            public Java.security.cert.X509Certificate[] getAcceptedIssuers() {
                return null;
            }
            public void checkClientTrusted(X509Certificate[] certs, String authType) {
            }
            public void checkServerTrusted(X509Certificate[] certs, String authType) {
            }
        }
        };

        // Install the all-trusting trust manager
        SSLContext sc = SSLContext.getInstance("SSL");
        sc.init(null, trustAllCerts, new Java.security.SecureRandom());
        HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());

        // Create all-trusting Host name verifier
        HostnameVerifier allHostsValid = new HostnameVerifier() {
            public boolean verify(String hostname, SSLSession session) {
                return true;
            }
        };

        // Install the all-trusting Host verifier
        HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    } catch (KeyManagementException e) {
        e.printStackTrace();
    }
}

Da ich den https://AAA.BBB.CCC.DDD:9443/ISomeService nur zu Testzwecken verwende, ist dies eine gute Lösung.

126
DP_

Ich habe das gleiche Problem und mit diesem Code gelöst ... Ich habe diesen Code vor dem ersten Aufruf meiner Webservices eingefügt.

javax.net.ssl.HttpsURLConnection.setDefaultHostnameVerifier(
new javax.net.ssl.HostnameVerifier(){

    public boolean verify(String hostname,
            javax.net.ssl.SSLSession sslSession) {
        return hostname.equals("localhost");
    }
});

Es ist einfach und funktioniert gut.

Hier ist die Originalquelle.

26
juanmhidalgo

Die Überprüfung der Zertifikatidentität wird anhand der Anforderungen des Clients durchgeführt.

Wenn Ihr Client https://xxx.xxx.xxx.xxx/something verwendet (wobei xxx.xxx.xxx.xxx eine IP-Adresse ist), wird die Zertifikatidentität gegen diese IP-Adresse geprüft (theoretisch nur unter Verwendung einer IP-Erweiterung SAN).

Wenn Ihr Zertifikat kein IP-SAN, aber DNS-SANs (oder kein DNS-SAN, ein allgemeiner Name im Betreff-DN) hat, können Sie dies erreichen, indem Sie Ihren Client dazu veranlassen, stattdessen eine URL mit diesem Hostnamen (oder einem Hostnamen) zu verwenden für das das Zertifikat gültig wäre, wenn mehrere mögliche Werte vorhanden sind). Wenn Sie für cert beispielsweise einen Namen für www.example.com haben, verwenden Sie https://www.example.com/something.

Natürlich benötigen Sie diesen Hostnamen, um diese IP-Adresse aufzulösen.

Wenn DNS-SANs vorhanden sind, wird der CN im Betreff-DN ignoriert. Verwenden Sie in diesem Fall einen Namen, der mit einem der DNS-SANs übereinstimmt.

19
Bruno

So importieren Sie das Zertifikat:

  1. Extrahieren Sie das Zertifikat vom Server, z. openssl s_client -showcerts -connect AAA.BBB.CCC.DDD:9443 > certs.txt Hiermit werden Zertifikate im PEM-Format extrahiert.
  2. Konvertieren Sie das cert in das DER-Format, da keytool dies erwartet, z. openssl x509 -in certs.txt -out certs.der -outform DER
  3. Jetzt möchten Sie dieses Zertifikat in die standardmäßige "cacert" -Datei des Systems importieren. Suchen Sie nach der Systemstandarddatei "cacerts" für Ihre Java-Installation. Werfen Sie einen Blick auf Wie erhalte ich den Speicherort der Zertifikate der Standard-Java-Installation?
  4. Importieren Sie die Zertifikate in diese Datei: Sudo keytool -importcert -file certs.der -keystore <path-to-cacerts> Das Standard-Kennwort für die Zertifikate lautet 'changeit'.

Wenn das Zertifikat für einen vollqualifizierten Domänennamen ausgestellt wird und Sie versuchen, über die IP-Adresse in Ihrem Java-Code eine Verbindung herzustellen, sollte dies wahrscheinlich in Ihrem Code behoben sein, anstatt sich mit dem Zertifikat selbst zu beschäftigen. Ändern Sie Ihren Code, um eine Verbindung per FQDN herzustellen. Wenn FQDN auf Ihrem Dev-Computer nicht auflösbar ist, fügen Sie ihn einfach Ihrer Hosts-Datei hinzu oder konfigurieren Sie Ihren Computer mit einem DNS-Server, der diesen FQDN auflösen kann.

10
AndreyT

Dies ist eine alte Frage, aber ich hatte das gleiche Problem, als ich von JDK 1.8.0_144 zu Jdk 1.8.0_191 wechselte

Wir haben im Änderungsprotokoll einen Hinweis gefunden:

Änderungsprotokoll

wir haben die folgenden zusätzlichen Systemeigenschaften hinzugefügt, die in unserem Fall zur Lösung dieses Problems beigetragen haben:

-Dcom.Sun.jndi.ldap.object.disableEndpointIdentification=true
9
Markus

Möglicherweise möchten Sie nicht die gesamte SSL-Verifizierung deaktivieren, und Sie können die Überprüfung des Hostnamens einfach über diese Option deaktivieren, was etwas weniger beängstigend ist als die Alternative:

HttpsURLConnection.setDefaultHostnameVerifier(
    SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);

[BEARBEITEN]

Wie von conapart3 bereits erwähnt, ist SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER jetzt veraltet, daher kann es in einer späteren Version entfernt werden, so dass Sie in Zukunft gezwungen sein werden, selbst zu rollen, obwohl ich immer noch sagen würde, dass ich mich von Lösungen abwenden würde, bei denen die gesamte Überprüfung deaktiviert ist .

3
lifesoordinary

mein Problem beim Abrufen dieses Fehlers wurde behoben, indem die vollständige URL "qatest.ourCompany.com/webService" anstelle von "qatest/webService" verwendet wurde. Grund war, dass unser Sicherheitszertifikat einen Platzhalter hatte, d. H. "* .OurCompany.com". Nachdem ich die vollständige Adresse eingegeben hatte, verschwand die Ausnahme. Hoffe das hilft.

3
Shahriar

Ich habe dieses Problem richtig gelöst, indem ich die Betreffsalternamen im Zertifikat hinzufügte, anstatt Codeänderungen vorzunehmen oder SSL zu deaktivieren, im Gegensatz zu den anderen Antworten, die hier vorgeschlagen werden. Wenn Sie deutlich sehen, dass die Ausnahme "Suject-Alt-Namen fehlt" angezeigt wird, sollten Sie sie hinzufügen 

Bitte schauen Sie sich diesen Link an, um Schritt für Schritt zu verstehen .

Der obige Fehler bedeutet, dass in Ihrer JKS-Datei die erforderliche Domäne fehlt, über die Sie auf die Anwendung zugreifen möchten. Sie müssen Open SSL und das Schlüsseltool verwenden, um mehrere Domänen hinzuzufügen

  1. Kopieren Sie die openssl.cnf in ein aktuelles Verzeichnis
  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/[email protected]' -days 365
  5. Exportieren Sie die öffentliche Schlüsseldatei (.pem) in das PKS12-Format. Dadurch werden Sie zur Eingabe eines Kennworts aufgefordert

    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. Erstellen Sie ein.JKS aus einem selbstsignierten PEM (Keystore)

    keytool -importkeystore -destkeystore keystore.jks -deststoretype PKCS12 -srcstoretype PKCS12 -srckeystore keystore.p12
    
  7. Generieren Sie ein Zertifikat über der Keystore- oder JKS-Datei

    keytool -export -keystore keystore.jks -alias myalias -file selfsigned.crt
    
  8. Da das oben genannte Zertifikat selbstsigniert ist und nicht von CA validiert wurde, muss es in Truststore hinzugefügt werden (Cacerts-Datei unterhalb von MAC, bei Windows finden Sie heraus, wo Ihr JDK installiert ist.)

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

Originalantwort gepostet auf diesen Link hier

1
Abhishek Galoda

Ich habe diese Frage nach dieser Fehlermeldung erhalten. In meinem Fall hatten wir jedoch zwei URLs mit unterschiedlichen Subdomains ( http://example1.xxx.com/someservice und http://example2.yyy.com/someservice ), die an dieselbe weitergeleitet wurden Server. Dieser Server hatte nur ein Platzhalterzertifikat für die Domäne * .xxx.com. Wenn der Dienst über die zweite Domäne verwendet wird, stimmt das gefundene Zertifikat (* .xxx.com) nicht mit der angeforderten Domäne (* .yyy.com) überein, und der Fehler tritt auf.

In diesem Fall sollten wir nicht versuchen, eine solche Fehlermeldung durch Verringerung der SSL-Sicherheit zu beheben, sondern den Server und die Zertifikate darauf prüfen.

0
Gert

Habe es schon in https://stackoverflow.com/a/53491151/1909708 beantwortet.

Dies schlägt fehl, da weder der allgemeine Name des Zertifikats (CN in Zertifizierung Subject) noch die alternativen Namen (Subject Alternative Name im Zertifikat) mit dem Zielhostnamen oder der IP-Adresse übereinstimmen. 

Wenn Sie beispielsweise von einer JVM aus versuchen, eine Verbindung zu einer IP-Adresse (WW.XX.YY.ZZ) und nicht zum DNS-Namen ( https://stackoverflow.com ) herzustellen, schlägt die HTTPS-Verbindung fehl, da das Zertifikat im Java Truststore gespeichert ist cacerts erwartet, dass der allgemeine Name (oder ein alternativer Zertifikatsname wie stackexchange.com oder * .stackoverflow.com usw.) der Zieladresse entspricht. 

Bitte überprüfen Sie: 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();

Oben übergeben ein implementiertes HostnameVerifier-Objekt, das immer true zurückgibt:

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

Ich war durch 2-Wege-SSL im Springboot. Ich habe alle korrekten Konfigurationsdienst Tomcat-Server und Dienstanrufer RestTemplate gemacht. Aber ich habe den Fehler als "Java.security.cert.CertificateException: Keine alternativen Antragstellernamen vorhanden" erhalten

Nachdem ich die Lösungen durchgearbeitet hatte, stellte ich fest, dass JVM dieses Zertifikat benötigt, da sonst ein Handshake-Fehler auftritt.

Nun, wie man dies zu JVM hinzufügt.

gehen Sie zur Datei jre/lib/security/cacerts. Wir müssen unsere Server-Zertifikatsdatei zu dieser Cacerts-Datei von JVM hinzufügen.

Befehl zum Hinzufügen eines Server-Zertifikats zur Cacerts-Datei über die Befehlszeile in Windows.

C:\Programme\Java\jdk1.8.0_191\jre\lib\security> keytool -import -noprompt -trustcacerts -alias sslserver -datei E:\spring_cloud_sachin\ssl_keys\sslserver.cer -keystore cacerts -storepass changeit

Überprüfen Sie, ob das Server-Zertifikat installiert ist oder nicht:

C:\Programme\Java\jdk1.8.0_191\jre\lib\security> keytool -list -keystore cacerts

sie können eine Liste der installierten Zertifikate sehen:

weitere Informationen: https://sachin4Java.blogspot.com/2019/08/javasecuritycertcertificateexception-no.html

0
Sachin Rane
public class RESTfulClientSSL {

    static TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() {
        @Override
        public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            // TODO Auto-generated method stub
        }

        @Override
        public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            // TODO Auto-generated method stub
        }

        @Override
        public X509Certificate[] getAcceptedIssuers() {
            // TODO Auto-generated method stub
            return null;
        }
    }};

    public class NullHostNameVerifier implements HostnameVerifier {
        /*
         * (non-Javadoc)
         *
         * @see javax.net.ssl.HostnameVerifier#verify(Java.lang.String,
         * javax.net.ssl.SSLSession)
         */
        @Override
        public boolean verify(String arg0, SSLSession arg1) {
            // TODO Auto-generated method stub
            return true;
        }
    }

    public static void main(String[] args) {

        HttpURLConnection connection = null;
        try {

            HttpsURLConnection.setDefaultHostnameVerifier(new RESTfulwalkthroughCer().new NullHostNameVerifier());
            SSLContext sc = SSLContext.getInstance("SSL");
            sc.init(null, trustAllCerts, new SecureRandom());
            HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());


            String uriString = "https://172.20.20.12:9443/rest/hr/exposed/service";
            URL url = new URL(uriString);
            connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("GET");
            //connection.setRequestMethod("POST");

            BASE64Encoder encoder = new BASE64Encoder();
            String username = "admin";
            String password = "admin";
            String encodedCredential = encoder.encode((username + ":" + password).getBytes());
            connection.setRequestProperty("Authorization", "Basic " + encodedCredential);

            connection.connect();
            BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
            int responseCode = connection.getResponseCode();
            if (responseCode == HttpURLConnection.HTTP_OK) {
                StringBuffer stringBuffer = new StringBuffer();
                String line = "";
                while ((line = reader.readLine()) != null) {
                    stringBuffer.append(line);
                }
                String content = stringBuffer.toString();
                System.out.println(content);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (connection != null) {
                connection.disconnect();
            }
        }
    }
}
0
user11888077

fügen Sie den Host-Eintrag mit der IP hinzu, die dem CN im Zertifikat entspricht

CN = someSubdomain.someorganisation.com

aktualisieren Sie nun die IP-Adresse mit dem CN-Namen, unter dem Sie auf die URL zugreifen möchten.

Es hat bei mir funktioniert.

0
manoj