Wie sollte ich das tun hostname-Validierung bei der Verwendung von JSSE?
Schreibe ich einen client in Java (arbeiten muss sowohl auf dem desktop als JRE und Android) für ein proprietäres Protokoll (spezifisch für meine Firma) übernommen, TLS. Ich versuche herauszufinden, der beste Weg zu schreiben, eine TLS-client in Java, und insbesondere, stellen Sie sicher, dass es nicht hostname-Validierung richtig. (Edit: Von denen, ich meine die Prüfung, dass der hostname entspricht dem X. 509-Zertifikat, um zu vermeiden, man-in-the-middle-Angriffe.)
JSSE ist die offensichtliche API für das schreiben eines TLS-client, aber ich bemerkte aus den "Die meisten Gefährlichen Code in die Welt" Papier (wie auch aus Experimenten), dass JSSE nicht bestätigen hostname wenn man mit der SSLSocketFactory-API. (Das ist, was ich verwenden, da mein Protokoll nicht HTTPS.)
So, es scheint, dass bei der Verwendung von JSSE, ich habe zu tun hostname Validierung mich. Und anstatt zu schreiben, dass der code von Grund auf neu (ich würde mich da fast sicher falsch), es scheint, dass ich "borgen" einige der bestehenden code, der funktioniert. So, der wahrscheinlichste Kandidat, die ich gefunden habe, ist den Apache HttpComponents-Bibliothek (ironisch, da bin ich nicht tatsächlich zu tun HTTP) , und verwenden Sie die org.apache.http.conn.ssl.SSLSocketFactory-Klasse anstelle der standard javax.net.ssl.SSLSocketFactory-Klasse.
Meine Frage ist: ist das ein vernünftiges Vorgehen? Oder habe ich das völlig missverstanden Dinge, Weg aus dem tiefen Ende, und es ist eigentlich ein viel einfacher Weg, um hostname-Validierung in JSSE, ohne ziehen in einem Drittanbieter-Bibliothek wie HttpComponents?
Ich auch angeschaut, BouncyCastle, auch die hat eine nicht-JSSE-API für TLS, aber es scheint noch mehr beschränkt, als dass Sie nicht auch tun certificate chain validation, viel weniger hostname-Validierung, so schien es wie ein non-starter.
Edit: Diese Frage beantwortet wurde für Java 7, aber ich bin immer noch neugierig, was die "best practice" für Java 6 und Android. (Insbesondere habe ich Unterstützung von Android für meine Anwendung.)
Wieder bearbeitet: Zu machen, mein Vorschlag "ausleihen von Apache HttpComponents" zu konkretisieren, habe ich einen kleine Bibliothek enthält die HostnameVerifier-Implementierungen (vor allem StrictHostnameVerifier und BrowserCompatHostnameVerifier) extrahiert aus Apache HttpComponents. (Ich erkannte alles, was ich brauche, sind die Teilnehmerinnen und brauche ich nicht, der Apache ist SSLSocketFactory, wie ich ursprünglich zu denken.) Wenn Links auf meine eigenen Geräte, das ist die Lösung, die ich verwende. Aber Erstens, gibt es einen Grund, mich sollte nicht tun es auf diese Weise? (Unter der Annahme, dass mein Ziel ist, mein hostname-Validierung die gleichen wie https funktioniert. Ich merke, dass sich zur Diskussion, und diskutiert wurde in dem thread auf den Kryptographie-Liste, aber jetzt ich ' m kleben mit HTTPS-wie hostname-Validierung, auch wenn ich es nicht mache HTTPS.)
Vorausgesetzt, es ist nichts "falsch" meine Lösung, meine Frage ist: gibt es eine "bessere" Art und Weise, es zu tun, während immer noch tragbar in Java 6, Java 7 und Android? (Wobei "besser" bedeutet, dass mehr Redewendungen, die bereits weit verbreitet, und/oder benötigen weniger externe code.)
- Ich kann nicht wirklich herausfinden, warum das so schwer downvoted und auf Eis gelegt, da off-topic. Wie die Umsetzung hostname-Validierung bei der Verwendung von JSSE ist ganz klar on-topic! (Die Frage auch klar zu erwähnen, welche APIs berücksichtigt wurden.)
- Wo würde der richtige Ort, diese Frage zu stellen? Ich habe nicht in der Lage zu finden, eine mailing-Liste für JSSE. Gibt es eine?
- Während der Wartezeit für diese Frage (hoffentlich) wieder eröffnet, die Sie interessieren könnten (das sollte Ihnen eine Antwort für Java 7): stackoverflow.com/a/17979954/372643
- Danke @Bruno! Das ist eigentlich eine nützliche Antwort auf meine Frage. (Leider hatte ich nicht gefunden, dass der thread vorher bei der Suche nach Antworten.) Das heißt, ich kann wahrscheinlich nicht nutzen, da, obwohl ich kann in der Lage sein, um Weg mit zu sagen, wir haben mit Java 7 statt Java 6 auf dem desktop, das problem ist, dass ich auch Unterstützung von Android, und ich bin Wetten, dass Android bietet keine Unterstützung für die neuen Java 7, was, da es nicht wirklich Java-kompatible jedenfalls. Aber jetzt vielleicht die Antwort ist, dass ich gehen sollte, daß diese Frage in einem Android-forum.
- Diese Frage ist nicht off-topic an alle. Dies ist eine wichtige Frage, die schlecht behandelt, die in der Dokumentation. Während die Frage bezieht sich auf einen code, die Frage ist nicht, über seine Umsetzung: es geht darum, wie lösen ein Allgemeines problem konfrontiert, durch Dritte unter Verwendung der Java-Implementierung von SSL.
- Wenn es ein proprietäres Protokoll ist, und Sie nur eine Verbindung zu Ihrem eigenen Server, können Sie überprüfen, cert, wie Sie wollen, aber es wäre im Allgemeinen: ist es von einer vertrauenswürdigen ZERTIFIZIERUNGSSTELLE ausgestellte oder verkettet zu einer vertrauenswürdigen Wurzel; und Sie tut das Zertifikat Namen der server, die Sie glauben, dass Sie sich verbinden (möglicherweise durch einen DNS-Namen, kann aber kein proprietäres Namensschema). Für ein solches Protokoll HTTPS-style-name matching (mit wildcards, etc.) ist wahrscheinlich übertrieben.
- Ich denke, im Grunde muss man das, was angedeutet wird durch die javax.net.ssl.HostnameVerifier-Schnittstelle. Zumindest dies würde Ihnen eine JDK-konforme Art und Weise der Verpackung, was die Umsetzung, Sie kommen mit. Ich Stimme vollkommen mit @Bruno und andere, die diese Frage zu 100% on-topic und ich gestimmt habe, zu öffnen. Sie nicht brauchen, Tim Dierk ersten zwei Punkte, da JSSE bereits überprüft, die, Sie benötigen jedoch die Dritte. Wo genau in der Bescheinigung der hostname sein soll und wie es passen kann variieren, aber im Allgemeinen, die Sie tun können, noch schlimmer als die Folgen der HTTPS-Spezifikation für dieses bit.
- Ich Plane auf tun, https-Stil Validierung, obwohl ich bin mir noch nicht sicher, ob ich brauche wildcards oder nicht. Ja, HostnameVerifier ziemlich viel tut, was ich will, aber es scheint, dass die JDK-nur hat die HostnameVerifier-Schnittstelle, und nicht öffentlich bieten konkrete Implementierungen von HostnameVerifier. Das ist, warum ich immer noch meine Augen auf Apache HttpComponents; es stellt konkrete Implementierungen wie StrictHostnameVerifier und BrowserCompatHostnameVerifier. Obwohl die einen leichten Nachteil HostnameVerifier ist, dass es nur gibt true oder false zurück, anstatt eine Fehlermeldung, wenn es fehlschlägt.
- Das JDK hat eine
HostNameVerifier
gebaut, natürlich, und Sie können es mit HttpsURLConnection.getDefaultHostnameVerifier(). Aber mein Punkt war eigentlich, dass diese Schnittstelle gibt genug die API, die Sie brauchen, um zu implementieren: d.h. alles, was Sie brauchen Zugriff auf den Hostnamen, und dieSSLSession
. Aber wenn Sie mehr als einen boolean-ich denke, das wird nicht funktionieren. - Für die weitere Diskussion um dieses, ich ' ll zeigen Sie auf diese mailing-Liste-thread: lists.randombit.net/pipermail/cryptography/2013-August/... : "best practices" für die hostname-Validierung bei der Verwendung von JSSE
- Suche an der Quelle für OpenJDK 6, HttpsURLConnection.getDefaultHostnameVerifier() gibt eine Instanz der Sonne.net.www.Protokoll.https.DefaultHostnameVerifier, die eine stub-Klasse, deren verify () - Methode immer false zurück. Ich bin immer noch durch den code, und es ist schwer zu Folgen, aber es sieht (zumindest einige von) die Magie passiert in der checkURLSpoofing () - Methode der Sonne.net.www.Protokoll.http.HttpsClient, und tatsächlich wird die Klasse sun.Sicherheit.util.HostnameChecker (die nicht implementiert HostnameVerifier interface) zu überprüfen, X509-Zertifikat...
- ...und er ruft nur die vom Benutzer angegebenen HostnameVerifier wenn HostnameChecker ausfällt. Es gibt auch einige böse Dinge in den afterConnect () - Methode, wo es überprüft, ob die HostnameVerifier ist ein instanceof-DefaultHostnameVerifier, und hat etwas besonderes in diesem Fall. Ich habe noch nicht herausgefunden, dass es alle schon, aber es scheint sehr hinterhältig. Auf der anderen Seite, Android (aka Harmonie) hat eine völlig andere (und einfachere) Architektur, wo getDefaultHostnameVerifier() eigentlich nicht geben Sie eine HostnameVerifier die das X509-Prüfung selbst.
- In Bezug auf "hostname-Validierung die gleichen wie https funktioniert [...]": das ist, warum es ist RFC 6125, zu vereinheitlichen das Verhalten über Protokolle. Konkret, es ist sehr ähnlich zu dem, was HTTPS sagt sowieso (außer es funktioniert nicht mit IP-Adressen).
Du musst angemeldet sein, um einen Kommentar abzugeben.
Java 7 (und höher)
Können Sie verwenden implizit die
X509ExtendedTrustManager
eingeführt in Java 7 mit diesem (siehe diese Antwort:Android
Ich bin weniger vertraut mit Android, aber der Apache HTTP-Client sollte gebündelt werden, so dass es nicht wirklich eine zusätzliche Bibliothek. Als solche, sollten Sie in der Lage sein zu verwenden
org.apache.http.conn.ssl.StrictHostnameVerifier
. (Ich habe nicht versucht mit diesem code.)Anderen
Leider, der Prüfstelle umgesetzt werden muss manuell. Die Oracle JRE hat offensichtlich einige host-name verifier Umsetzung, aber soweit ich weiß, ist es nicht verfügbar, über die öffentliche API.
Gibt es mehr details über die Regeln in diese neue Antwort.
Hier ist eine Implementierung, die ich geschrieben habe. Es könnte durchaus mit überprüft... die Kommentare und Rückmeldungen sind willkommen.
Gibt es zwei
getCommonName
Implementierungen: eine überjavax.naming.ldap
und eins, BouncyCastle, je nachdem, was verfügbar ist.Den wichtigsten Feinheiten sind etwa:
BEARBEITEN:
Ja, es gibt Gründe, es nicht zu tun auf diese Weise.
Erstens, Sie haben effektiv Gabel eine Bibliothek, und nun müssen Sie pflegen, je nach weiteren änderungen an diesen Klassen, die in der original-Apache HttpComponents. Ich bin nicht gegen die Schaffung einer Bibliothek (ich habe es selbst getan, und ich bin nicht entmutigt Sie, dies zu tun), aber Sie müssen dies berücksichtigen. Sind Sie wirklich versucht, etwas Platz zu sparen? Sicher, es gibt tools, entfernen Sie unbenutzte code für Ihr Finales Produkt, wenn Sie brauchen, um Platz frei zu machen (ProGuard in den Sinn kommt).
Zweitens, auch die StrictHostnameVerifier ist nicht kompatibel mit RFC 2818 oder RFC 6125. So weit wie ich kann sagen, aus seiner code:
CN=cn.example.com
und eine SAN fürwww.example.com
aber keine SAN fürcn.example.com
gültig fürcn.example.com
wenn es nicht sollte.Es ist schwer zu sehen, ein Allgemeines "besser Weg". Geben Sie diese Rückmeldung an die Apache HttpComponents-Bibliothek wäre eine Möglichkeit natürlich. Kopieren und einfügen der code, den ich schrieb oben sicherlich nicht klingen wie eine gute Möglichkeit, entweder (code-Schnipsel auf, ALSO in der Regel nicht gepflegt, sind nicht zu 100% getestet und kann anfällig für Fehler).
Bessere Weg sein könnte, um zu versuchen, um zu versuchen zu überzeugen, die Android development team unterstützen die gleichen
SSLParameters
undX509ExtendedTrustManager
als es fertig war für Java 7. Dies lässt immer noch die Frage der legacy-Geräte.Es gibt viele gute Gründe, für die die jsse-client (Sie), um Ihre eigenen StrictHostnameVerifier. Wenn Sie Vertrauen in Ihre Firma nameserver, schreiben man sollte ziemlich einfach sein.
für die Verwendung konfiguriert.
richtigen Namen.
wenn Sie es brauchen, werde ich Ihnen mit einem verifier.
Wenn Sie wollen, dass ich um die 'gute Gründe', ich kann das auch.