Wie werden SSL-Zertifikatservernamen aufgelöst / Kann ich mithilfe von Keytool alternative Namen hinzufügen?

78

Diese können aus Gründen der Klarheit als separate Fragen formuliert werden, beziehen sich jedoch alle auf dasselbe Thema.

Wie werden SSL-Zertifikatservernamen aufgelöst?

Warum scheinen Browser das CN-Feld des Zertifikats zu verwenden, aber der Java-Mechanismus scheint nur "alternative Betreffnamen" zu betrachten?

Ist es möglich, mit keytool alternative Namen zu einem SSL-Zertifikat hinzuzufügen? Wenn nicht, ist die Verwendung von openSSL stattdessen eine gute Option?

Nur ein kleiner Hintergrund: Ich brauche einen Hauptserver, um mit mehreren Servern über HTTPS zu kommunizieren. Natürlich möchten wir nicht für jeden Server SSL-Zertifikate kaufen (es können viele sein), daher möchte ich selbstsignierte Zertifikate verwenden (ich habe Keytool verwendet, um sie zu generieren). Nachdem ich die Zertifikate als vertrauenswürdig im Betriebssystem hinzugefügt habe, akzeptieren die Browser (IE und Chrome) die Verbindung gerne als vertrauenswürdig. Selbst nach dem Hinzufügen der Zertifikate zu Javas Cacerts akzeptiert Java die Verbindung nicht als vertrauenswürdig und löst die folgende Ausnahme aus:

Auslöser: java.security.cert.CertificateException: Bei sun.security.util.HostnameChecker.matchIP (HostnameChecker.java:142) bei sun.security.util.HostnameChecker.match (HostnameChecker.java:75) sind keine alternativen Betreffnamen vorhanden. at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkIdentity (X509T rustManagerImpl.java:264) at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkServerTrustr. sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate (Clien tHandshaker.java:1185) ... 14 weitere

Ich habe festgestellt, dass ich Java das Zertifikat vertrauen lassen kann, das meinen eigenen HostNameVerifier implementiert, den ich von hier kopiert habe: com.sun.jbi.internal.security.https.DefaultHostnameVerifier, nur um zu testen (übrigens, der Hostname wurde als Argument an den übergeben HostnameVerifier ist korrekt, daher denke ich, dass es hätte akzeptiert werden sollen.

Ich habe das Zertifikatfeld CN als Hostnamen verwendet (normalerweise die IP-Adresse).

Kann mir bitte jemand sagen, ob ich etwas falsch mache und mich in die richtige Richtung weisen?

Renato
quelle
1
Wählen Sie Ihre Alternative: grepcode.com/search?query=DefaultHostnameVerifier&n=
Renato

Antworten:

100

Wie die Überprüfung des Hostnamens durchgeführt werden sollte, ist in RFC 6125 definiert , das relativ neu ist und die Praxis auf alle Protokolle verallgemeinert und RFC 2818 ersetzt , das für HTTPS spezifisch war. (Ich bin mir nicht einmal sicher, ob Java 7 RFC 6125 verwendet, was möglicherweise zu aktuell ist.)

Aus RFC 2818 (Abschnitt 3.1) :

Wenn eine subjectAltName-Erweiterung vom Typ dNSName vorhanden ist, MUSS diese als Identität verwendet werden. Andernfalls MUSS das Feld (spezifischster) Common Name im Feld Subject des Zertifikats verwendet werden. Obwohl die Verwendung des allgemeinen Namens bereits üblich ist, wird sie nicht mehr empfohlen, und Zertifizierungsstellen werden aufgefordert, stattdessen den dNSName zu verwenden.

[...]

In einigen Fällen wird der URI als IP-Adresse und nicht als Hostname angegeben. In diesem Fall muss der iPAddress subjectAltName im Zertifikat vorhanden sein und genau mit der IP im URI übereinstimmen.

Das spezifische Problem besteht im Wesentlichen darin, dass Sie in Ihrem CN IP-Adressen und keinen Hostnamen verwenden. Einige Browser funktionieren möglicherweise, weil nicht alle Tools dieser Spezifikation genau folgen, insbesondere weil "am spezifischsten" in RFC 2818 nicht klar definiert ist (siehe Erläuterungen in RFC 6215).

Wenn Sie verwenden keytool, wie von Java 7,keytool hat eine Option , einen Subject Alternative Namen enthält (siehe die Tabelle in der Dokumentation -ext): Sie nutzen könnten -ext san=dns:www.example.comoder -ext san=ip:10.0.0.1.

BEARBEITEN:

Sie können ein SAN in OpenSSL anfordern, indem Sie es ändern openssl.cnf(es wählt die Kopie im aktuellen Verzeichnis aus, wenn Sie die globale Konfiguration, soweit ich mich erinnere, nicht bearbeiten möchten, oder Sie können mithilfe der OPENSSL_CONFUmgebungsvariablen einen expliziten Speicherort auswählen ).

Stellen Sie die folgenden Optionen ein (suchen Sie zuerst die entsprechenden Abschnitte in Klammern):

[req]
req_extensions = v3_req

[ v3_req ]
subjectAltName=IP:10.0.0.1
# or subjectAltName=DNS:www.example.com

Es gibt auch einen guten Trick, eine Umgebungsvariable dafür zu verwenden (anstatt sie in einer Konfigurationsdatei zu reparieren): http://www.crsr.net/Notes/SSL.html

Bruno
quelle
Dies ist genau das, was ich wissen musste ... es scheint jedoch, dass Java 6 diese -ext-Option nicht hat. Ich werde versuchen, meine VM auf Java 7 zu ändern und dies zu testen.
Renato
2
Beachten Sie, dass Sie Keytool von Java 7 auf einem anderen Computer verwenden und den Keystore später kopieren können (Sie müssen Java 7 nicht ausführen). Alternativ habe ich meine Antwort für OpenSSL bearbeitet. Vor diesem Hintergrund ist es möglicherweise auf lange Sicht flexibler, Hostnamen anstelle von IP-Adressen zu verwenden (die Verwendung von SANs ist ohnehin eine gute Idee).
Bruno
Die Option -ext funktioniert in Java6 nicht! Ich werde zu Java 7 wechseln und sehen, ob ich es nur mit Keytool schaffen kann ... Vielen Dank für die Antwort. (PS. Akzeptiert die Antwort, sobald ich sie getestet habe.)
Renato
5
Die Tatsache, dass Sie IP-Adressen in Ihren Zertifikaten verwenden, wird in diesem Fall noch problematischer. Möglicherweise ist das Ändern von IP-Adressen das Ziel von DNS. Sie haben eine bessere Chance, Ihr Problem zu lösen, indem Sie einen dynamischen DNS-Dienst verwenden und Zertifikate mit diesem Namen (oder einen beliebigen CNAME, der in diesen dynamischen Namen aufgelöst werden würde) verwenden.
Bruno
1
Nur um einige Verwirrung zu stiften, akzeptieren viele Browser SANs wie DNS: 10.0.0.1, aber nicht IP: 10.0.0.1, aber die gute Nachricht ist, dass Sie beides haben können
KCD