Comment dois-je faire validation de nom de domaine lors de l'utilisation de JSSE?
Je suis en train d'écrire un client en Java (besoin de travailler à la fois sur le bureau JRE et sur Android) pour un protocole propriétaire (spécifique à mon entreprise) réalisée sur TLS. Je suis à essayer de comprendre la meilleure façon de rédiger un client TLS en Java, et en particulier, assurez-vous qu'il n'validation de nom de domaine correctement. (Edit: Par qui, je veux dire la vérification que le nom d'hôte correspond à l'X. 509 certificate, pour éviter man-in-the-middle attaques.)
JSSE est l'évidence de l'API pour la rédaction d'un client TLS, mais j'ai remarqué de la "La plupart de Code Dangereux dans le Monde" papier (ainsi que de l'expérimentation) que JSSE ne valide pas le nom de l'hôte lorsque l'on est à l'aide de la SSLSocketFactory API. (Qui est ce que j'ai à utiliser, car mon protocole n'est pas en HTTPS.)
Ainsi, il apparaît que lors de l'utilisation de JSSE, j'ai à faire, validation de nom de domaine me. Et, plutôt que d'écrire le code à partir de zéro (puisque j'aurais presque certainement se tromper), il semble que je doit "emprunter" le code existant qui fonctionne. Ainsi, le candidat le plus probable que j'ai trouvé est d'utiliser Apache HttpComponents bibliothèque (ironique, puisque je ne suis pas en train de faire HTTP) et l'utilisation de l'org.apache.http.conn.le protocole ssl.SSLSocketFactory classe en lieu et place de la norme javax.net.le protocole ssl.SSLSocketFactory classe.
Ma question est: est-ce raisonnable de cours de l'action? Ou ai-je complètement mal compris les choses, disparu à la fin de profondeur, et il y a effectivement un moyen beaucoup plus facile d'obtenir la validation de nom de domaine en JSSE, sans tirer dans une bibliothèque tierce, comme HttpComponents?
J'ai aussi regardé BouncyCastle, trop, ce qui est un non-JSSE API pour TLS, mais il semble être encore plus limité, en ce qu'il n'a même pas de faire un certificat de la chaîne de validation, beaucoup moins de validation de nom de domaine, de sorte qu'il semblait être un non-starter.
Edit: Cette question a été répondu pour Java 7, mais je suis toujours curieux de savoir ce que les "meilleures pratiques" pour la version 6 de Java et Android. (J'ai en particulier à l'appui de Android pour mon application.)
Réédité: Pour faire ma proposition de "emprunter de Apache HttpComponents" plus concrète, j'ai créé un petite bibliothèque qui contient le HostnameVerifier implémentations (notamment StrictHostnameVerifier et BrowserCompatHostnameVerifier) extrait de Apache HttpComponents. (J'ai réalisé tout ce que je besoin sont les vérificateurs, et je n'ai pas besoin d'Apache SSLSocketFactory comme je l'ai été à l'origine de la pensée.) Si la gauche pour moi, c'est la solution que je vais utiliser. Mais tout d'abord, est-il une raison je ne devrait pas faire de cette façon? (En supposant que mon objectif est de faire de mon nom d'hôte de la validation de la même façon https n'. Je me rends compte que lui-même est ouvert au débat, et qui a été discuté dans le fil sur la cryptographie liste, mais pour l'instant je suis coller avec HTTPS-comme la validation de nom de domaine, même si je ne le fais pas HTTPS.)
En supposant que il n'y a rien de "mal" avec ma solution, ma question est la suivante: est-il une "meilleure" façon de le faire, tout en restant portable à travers la version 6 de Java, Java 7, et Android? (Où "plus" signifie plus idiomatiques, déjà largement en cours d'utilisation, et/ou ayant besoin de moins de code externe.)
- Je ne peux pas vraiment comprendre pourquoi cela a été si souvent downvoted et mis en attente comme hors-sujet. Comment mettre en œuvre validation de nom de domaine lors de l'utilisation de JSSE est clairement sur le sujet! (La question aussi clairement mention Api ont été pris en compte.)
- Où aurait être le bon endroit pour poser cette question? Je n'ai pas été en mesure de trouver une liste de diffusion pour JSSE. Est-il un?
- En attendant, pour cette question, pour être (espérons-le), a de nouveau ouvert, vous pourriez être intéressé par ce qui devrait vous donner une réponse pour Java 7): stackoverflow.com/a/17979954/372643
- Merci @Bruno! C'est en fait une réponse utile à ma question. (Malheureusement, je n'avais pas trouvé ce thread avant lors de la recherche de réponses.) Cela dit, j'ai probablement ne peut pas l'utiliser car bien que j'ai pourrait être en mesure de s'en tirer en disant que nous devons utiliser Java 7 au lieu de Java 6 sur le bureau, le problème est que j'ai aussi besoin de soutien Android, et je parie que Android ne prend pas en charge le nouveau Java 7 chose, car il n'est pas vraiment Java compatible de toute façon. Mais maintenant, peut-être que la réponse est que je devrais aller poser cette question par Android-forum spécifique.
- Cette question n'est pas hors-sujet du tout. C'est une question importante qui est mal abordé dans la documentation. Alors que la question fait référence à son code, la question n'est pas à propos de sa mise en œuvre: c'est sur la façon de résoudre un problème général rencontrées par la personne qui utilise le Java SSL mise en œuvre.
- Si c'est un protocole propriétaire, et vous ne vous connectez vos propres serveurs, vous pouvez valider le cert de la manière que vous voulez, mais il comprend généralement: est-il émis par une autorité de certification de confiance, ou d'une chaîne à une racine de confiance; et le nom de certificat du serveur, vous croyez que vous êtes connecté à (peut-être par un nom DNS, mais pourrait être n'importe quel propriétaire de nommage). Pour un tel protocole HTTPS style de correspondance de nom (avec des caractères génériques, etc.) est probablement excessif.
- Je pense que vous avez essentiellement besoin de ce qui est impliqué par la javax.net.le protocole ssl.HostnameVerifier interface. Au moins ce serait vous donner un JDK-conformité de l'emballage quel que soit mise en œuvre que vous venez avec. Je suis entièrement d'accord avec @Bruno et d'autres personnes que cette question est de 100% sur le sujet et j'ai voté pour la réouverture. Vous n'avez pas besoin de Tim Dierk les deux premiers points, comme JSSE déjà de vérifier ceux-ci, mais vous avez besoin de la troisième. Où exactement dans le certificat, le nom d'hôte doit être et comment il peut correspondre peut varier, mais en général, vous pourriez faire pire que de suivre la HTTPS spécification de ce bit.
- Je vais la faire https style de validation, même si je ne suis pas encore sûr si j'ai besoin de caractères génériques ou pas. Oui, HostnameVerifier assez bien fait ce que je veux, mais il semble que le JDK seulement le HostnameVerifier interface, et n'a pas publiquement fournir des implémentations de HostnameVerifier. C'est pourquoi j'ai toujours mon oeil sur Apache HttpComponents; il fournit des implémentations concrètes comme StrictHostnameVerifier et BrowserCompatHostnameVerifier. Bien qu'un léger inconvénient à HostnameVerifier c'est qu'il ne retourne true ou false, plutôt qu'un message d'erreur en cas d'échec.
- Le JDK n'ont
HostNameVerifier
construit dans, bien sûr, et vous pouvez obtenir avec HttpsURLConnection.getDefaultHostnameVerifier(). Mais mon point est vraiment que cette interface indique suffisamment l'API que vous avez besoin pour mettre en œuvre: c'est à dire tout ce que vous devez accès, le nom d'hôte et leSSLSession
. Mais si vous avez besoin de plus qu'un booléen je suppose que ça ne fonctionne pas, soit. - Pour plus de discussion autour de cela, je vais point à cette liste de diffusion thread: lists.randombit.net/pipermail/cryptography/2013-August/... : "meilleures pratiques" pour la validation de nom de domaine lors de l'utilisation de JSSE
- en regardant la source pour OpenJDK 6, HttpsURLConnection.getDefaultHostnameVerifier() renvoie une instance de soleil.net.www.le protocole.https.DefaultHostnameVerifier, qui est un stub de classe dont le vérifier() la méthode renvoie toujours false. Je travaille toujours à travers le code, et c'est dur à suivre, mais il ressemble (au moins une partie de) la magie qui se passe dans le checkURLSpoofing() la méthode de soleil.net.www.le protocole.http.HttpsClient, et il utilise la classe soleil.de sécurité.util.HostnameChecker (ce qui ne veut pas mettre en œuvre la HostnameVerifier interface) pour vérifier le certificat X509...
- ...et il ne l'invoque fourni par l'utilisateur HostnameVerifier si HostnameChecker échoue. Il y a aussi de mauvaises choses qui se passent dans le afterConnect() la méthode, où il vérifie si le HostnameVerifier est un instanceof DefaultHostnameVerifier, et fait quelque chose de spécial dans ce cas. Je n'ai pas compris tous les encore sorti, mais il semble très sournois. D'autre part, Android (aka Harmonie) est totalement différent (et plus simple) de l'architecture, où getDefaultHostnameVerifier() ne fait de vous donner un HostnameVerifier qui ne l'X509 la vérification elle-même.
- Concernant le "nom d'hôte de la validation de la même façon https ne [...]": c'est pourquoi il est le RFC 6125, d'unifier le comportement à l'échelle des protocoles. Concrètement, c'est très similaire à ce que HTTPS dit de toute façon (sauf qu'il ne gère pas les adresses IP).
Vous devez vous connecter pour publier un commentaire.
Java 7 (et ci-dessus)
Vous pouvez implicitement utiliser le
X509ExtendedTrustManager
introduit dans Java 7 à l'aide de ce (voir cette réponse:Android
Je suis moins familier avec Android, mais Apache HTTP Client devrait être fourni avec, donc il n'est pas vraiment une bibliothèque supplémentaire. En tant que tel, vous devriez être en mesure d'utiliser
org.apache.http.conn.ssl.StrictHostnameVerifier
. (Je n'ai pas essayé ce code.)Autres
Malheureusement, le vérificateur doit être mis en œuvre manuellement. L'Oracle JRE a évidemment certains nom d'hôte vérificateur de mise en œuvre, mais autant que je sache, il n'est pas disponible via l'API publique.
Il y a plus de détails sur les règles de cette récente réponse.
Voici une application que j'ai écrit. Il pourrait certainement être revu... des Commentaires et de la rétroaction de bienvenue.
Il y a deux
getCommonName
implémentations: à l'aide d'javax.naming.ldap
et une à l'aide de BouncyCastle, selon ce qui est disponible.Les principales subtilités sont sur:
MODIFIER:
Oui, il y a des raisons de ne pas le faire de cette façon.
Tout d'abord, vous avez effectivement fourchue, une bibliothèque, et vous aurez maintenant à maintenir, selon d'autres modifications apportées à ces classes à l'origine, Apache HttpComponents. Je ne suis pas contre la création d'une bibliothèque (je l'ai fait moi-même, et je ne suis pas vous décourager, vous de le faire), mais vous devez prendre cela en compte. Êtes-vous vraiment essayer de gagner de l'espace? Certes, il existe des outils qui peuvent supprimer les code final de votre produit si vous avez besoin de récupérer de l'espace (ProGuard vient à l'esprit).
D'autre part, même les StrictHostnameVerifier n'est pas conforme aux RFC 2818 ou RFC 6125. Aussi loin que je peux dire à partir de son code:
CN=cn.example.com
et un saint pourwww.example.com
mais pas SAN pourcn.example.com
être valable pourcn.example.com
quand il ne devrait pas.C'est dur de voir un général "meilleure façon". En donnant cette rétroaction de l'Apache HttpComponents bibliothèque serait un moyen de cours. De copier et de coller le code que j'ai écrit plus tôt ci-dessus n'est certainement pas une bonne manière ou d'une autre (extraits de code, DONC, ne sont généralement pas conservés, ne sont pas 100% testés et peuvent être sujettes à des erreurs).
Une meilleure façon peut-être pour tenter de convaincre l'équipe de développement Android à l'appui de la même
SSLParameters
etX509ExtendedTrustManager
comme cela a été fait pour Java 7. Cela laisse tout de même la question de l'héritage des appareils.Il y a de nombreuses bonnes raisons pour exiger la jsse client (vous) afin de fournir votre propre StrictHostnameVerifier. Si vous faites confiance à votre entreprise de serveur de noms, l'écriture de l'un devrait être assez simple.
configuré pour utiliser.
nom correct.
si vous en avez besoin, je vais vous fournir un vérificateur.
Si vous voulez me donner les "bonnes raisons", je peux le faire aussi.