Un module que j'ajoute à notre grande application Java doit converser avec le site Web sécurisé SSL d'une autre entreprise. Le problème est que le site utilise un certificat auto-signé. J'ai une copie du certificat pour vérifier que je ne suis pas confronté à une attaque de type man-in-the-middle, et je dois intégrer ce certificat dans notre code de manière à ce que la connexion au serveur réussisse.
Voici le code de base:
void sendRequest(String dataPacket) {
String urlStr = "https://host.example.com/";
URL url = new URL(urlStr);
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
conn.setMethod("POST");
conn.setRequestProperty("Content-Length", data.length());
conn.setDoOutput(true);
OutputStreamWriter o = new OutputStreamWriter(conn.getOutputStream());
o.write(data);
o.flush();
}
Sans aucune gestion supplémentaire en place pour le certificat auto-signé, cela meurt à conn.getOutputStream () avec l'exception suivante:
Exception in thread "main" javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
....
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
....
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
Idéalement, mon code doit apprendre à Java à accepter ce certificat auto-signé, pour celui-ci dans l'application, et nulle part ailleurs.
Je sais que je peux importer le certificat dans le magasin d'autorité de certification du JRE, ce qui permettra à Java de l'accepter. Ce n'est pas une approche que je veux adopter si je peux aider; cela semble très invasif à faire sur toutes les machines de nos clients pour un module qu'ils ne peuvent pas utiliser; cela affecterait toutes les autres applications Java utilisant le même JRE, et je n'aime pas cela même si les chances pour toute autre application Java d'accéder à ce site sont nulles. Ce n'est pas non plus une opération triviale: sous UNIX, je dois obtenir des droits d'accès pour modifier le JRE de cette manière.
J'ai également vu que je peux créer une instance de TrustManager qui effectue une vérification personnalisée. Il semble que je pourrais même être en mesure de créer un TrustManager qui délègue au vrai TrustManager dans toutes les instances sauf celui-ci. Mais il semble que TrustManager soit installé globalement, et je suppose que cela affecterait toutes les autres connexions de notre application, et cela ne me semble pas tout à fait normal non plus.
Quelle est la méthode préférée, standard ou la meilleure pour configurer une application Java pour accepter un certificat auto-signé? Puis-je atteindre tous les objectifs que j'ai en tête ci-dessus ou vais-je devoir faire des compromis? Existe-t-il une option impliquant des fichiers et des répertoires et des paramètres de configuration, et peu ou pas de code?
Réponses:
Créez
SSLSocket
vous-même une usine et définissez-la sur leHttpsURLConnection
avant de vous connecter.Vous voudrez en créer un
SSLSocketFactory
et le conserver. Voici un schéma de l'initialisation:Si vous avez besoin d'aide pour créer le magasin de clés, veuillez commenter.
Voici un exemple de chargement du magasin de clés:
Pour créer le magasin de clés avec un certificat au format PEM, vous pouvez écrire votre propre code en utilisant
CertificateFactory
, ou simplement l'importer avec àkeytool
partir du JDK (keytool ne fonctionnera pas pour une «entrée de clé», mais convient parfaitement pour une «entrée de confiance» ).la source
J'ai lu BEAUCOUP d'endroits en ligne pour résoudre ce problème. Voici le code que j'ai écrit pour le faire fonctionner:
app.certificateString est une chaîne qui contient le certificat, par exemple:
J'ai testé que vous pouvez mettre n'importe quel caractère dans la chaîne de certificat, si elle est auto-signée, à condition de conserver la structure exacte ci-dessus. J'ai obtenu la chaîne de certificat avec la ligne de commande Terminal de mon ordinateur portable.
la source
Si la création d'un
SSLSocketFactory
n'est pas une option, importez simplement la clé dans la JVMRécupérez la clé publique :,
$openssl s_client -connect dev-server:443
puis créez un fichier dev-server.pem qui ressemble àImporter la clé:
#keytool -import -alias dev-server -keystore $JAVA_HOME/jre/lib/security/cacerts -file dev-server.pem
. Mot de passe: changeitRedémarrez la JVM
Source: Comment résoudre javax.net.ssl.SSLHandshakeException?
la source
Nous copions le truststore du JRE et ajoutons nos certificats personnalisés à ce truststore, puis demandons à l'application d'utiliser le truststore personnalisé avec une propriété système. De cette façon, nous laissons le truststore JRE par défaut seul.
L'inconvénient est que lorsque vous mettez à jour le JRE, vous ne fusionnez pas automatiquement son nouveau truststore avec votre personnalisé.
Vous pouvez peut-être gérer ce scénario en ayant un programme d'installation ou une routine de démarrage qui vérifie le truststore / jdk et vérifie une incompatibilité ou met à jour automatiquement le truststore. Je ne sais pas ce qui se passe si vous mettez à jour le truststore pendant que l'application est en cours d'exécution.
Cette solution n'est ni 100% élégante ni infaillible, mais elle est simple, fonctionne et ne nécessite aucun code.
la source
J'ai dû faire quelque chose comme ça lors de l'utilisation de commons-httpclient pour accéder à un serveur https interne avec un certificat auto-signé. Oui, notre solution était de créer un TrustManager personnalisé qui passait simplement tout (journalisation d'un message de débogage).
Cela revient à avoir notre propre SSLSocketFactory qui crée des sockets SSL à partir de notre SSLContext local, qui est configuré pour n'avoir que notre TrustManager local associé. Vous n'avez pas du tout besoin de vous approcher d'un keystore / certstore.
C'est donc dans notre LocalSSLSocketFactory:
Avec d'autres méthodes mettant en œuvre SecureProtocolSocketFactory. LocalSSLTrustManager est l'implémentation du gestionnaire de confiance factice susmentionnée.
la source