2014-04-17 7 views
3

Laissez-moi vous expliquer rapidement ce que j'essaie de faire. J'essaie de créer le service de notification push de mon propre Apple en Java (à des fins de test). Ce service fonctionne grâce à la prise TLS.Socket Java TLS: aucun certificat de confiance trouvé

J'ai un client java pour créer un socket TLS pour envoyer des notifications push aux APN. J'ai changé l'URL de l'hôte pour rediriger le socket vers localhost: 2195. Maintenant j'essaye d'écrire un serveur de douille de java pour obtenir la demande de notification. Cependant, je reçois une exception lors de l'établissement de la liaison et je n'arrive pas à résoudre le problème.

Remarque: J'utilise le même certificat des deux côtés, c'est un fichier .p12 standard qui fonctionne pour envoyer des notifications push aux APN.

Voici le client (simplifié):

KeyStore ks = KeyStore.getInstance("PKCS12"); 
ks.load(new FileInputStream(certificatePath), password.toCharArray()); 

KeyManagerFactory kmf = KeyManagerFactory.getInstance("sunx509"); 
kmf.init(ks, password.toCharArray()); 

TrustManagerFactory tmf = TrustManagerFactory.getInstance("sunx509"); 
tmf.init((KeyStore)null); 

SSLContext sc = SSLContext.getInstance("TLS"); 
sc.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); 

SSLSocketFactory ssf = sc.getSocketFactory(); 
SSLSocket socket = (SSLSocket) ssf.createSocket(InetAddress.getLocalHost(), 2195); 
socket.startHandshake(); 

Voici le serveur:

KeyStore ks = KeyStore.getInstance("PKCS12"); 
ks.load(new FileInputStream(certificatePath), password.toCharArray()); 

KeyManagerFactory kmf = KeyManagerFactory.getInstance("sunx509"); 
kmf.init(ks, password.toCharArray()); 

SSLContext context = SSLContext.getInstance("TLS"); 
context.init(kmf.getKeyManagers(), null, null); 

SSLServerSocketFactory ssf = context.getServerSocketFactory(); 
serverSocket = (SSLServerSocket) ssf.createServerSocket(2195); 

Et voici l'exception:

javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: No trusted certificate found 

Je suppose que le client n » t faire confiance au certificat du serveur. J'ai essayé de configurer TrustManager du client pour accepter le p12 du serveur et cela a fonctionné, cependant j'ai besoin que cela fonctionne sans éditer le client (puisqu'il fonctionne de cette façon avec les vrais APNs).

Quel type de certificat nécessite la confiance du serveur par le client?

Merci d'avance.

Répondre

2

ÉDITER: J'AI FAUT! tmf.init (null) UTILISE le keystore par défaut comme sslctx.init (, null,)! Ce fichier par défaut est normalement le fichier cacerts dans JRE/lib/security qui fait confiance à beaucoup de CAs établies alors maintenant je pense que nous pouvons être sûr que le vrai serveur utilise un cert sous une autorité de certification établie (et donc est approuvé par votre client) alors que le CERT de votre p12 ne le semble pas; mais il y a deux possibilités:

  • il est SELFSIGNED, ou émis par un inconnu, obscur ou non prouvée CA

  • il est émis par une autorité de certification « réel » sous un CA « intermédiaire » cela nécessite un cert de chaîne (ou plusieurs) et vous n'avez pas le ou les cert de la chaîne dans votre p12. Notez que cela peut toujours fonctionner pour le client auth sur le serveur réel, car le vrai serveur peut facilement avoir le ou les certificats de chaîne «préchargés» dans son fichier de clés certifiées, même s'ils ne sont pas dans Java.

Distinguer ces derniers, regardez keytool -keystore file -storetype pkcs12 -list -v et voir ce que cert ou d'une séquence de certs que vous avez là.

Ensuite, il peut y avoir plusieurs approches de solution:

  1. si vous êtes un CA établi cert de la chaîne il ne manque que (s) obtenir et de les ajouter.L'outil à clé vous permet uniquement de remplacer toute la chaîne, vous devez donc obtenir tous les certificats nécessaires; Opensl (si vous l'avez ou l'obtenez) peut sortir la clé et le (s) certificat (s) d'un pkcs12, remplacer ou ajouter des certificats individuels, et les joindre ensemble.

  2. Créer une banque et une clé différentes pour le serveur et obtenir un certificat (chaîne) auprès d'une autorité de certification établie. Cela coûte généralement de l'argent et nécessite de prouver le contrôle du nom de domaine du serveur. (Votre client peut et doit toujours utiliser ce p12. Les deux parties ne doivent pas être les mêmes.)

  3. localiser le point d'ancrage de confiance (de la p12, ou d'ailleurs comme l'autorité de certification) et l'ont en un fichier de clés certifiées que le client charge explicitement. Vous avez effectivement essayé ceci en utilisant le p12 comme truststore et dites que vous ne le voulez pas.

  4. Mettez l'ancre de confiance dans le magasin de clés de confiance par défaut du client, afin que le client continue en utilisant la valeur par défaut. Si cela ne vous dérange pas de modifier votre JRE (et aucun autre utilisateur ou application sur votre système est dérangé) il suffit d'ajouter à JRE/lib/security/cacerts. Ou, en supposant que vous pouvez définir les propriétés du système , placez l'ancre dans un magasin ou laissez-le simplement dans le p12 et définissez javax.net.ssl.trustStore {, Password, Type} pour qu'il pointe vers ce magasin. (Si vous copiez, vous ne devez prendre que le cert; un p12 est un KEY et un cert non seulement un certificat Ne pas seulement -importkeystore; -importcert un fichier cert, créé avec -exportcert si nécessaire.) (Vous pouvez System.setProperty dans votre code, mais cela change votre code. Si vous exécutez de commandline vous pouvez utiliser 'java -Dname = valeur ...'. Pour les autres cas YMMV.)

Il est possible une 'type': si le certificat a été émis avec l'extension ExtendedKeyUsage et que cette valeur spécifie uniquement TLSclient et non TLSserver (que l'autorité de certification peut choisir) puis l'utiliser pour le serveur probablement ne fonctionnera pas - il semble que JSSE applique les restrictions EKU. Mais si c'est un problème, vous aurez une exception très différente. Et vous pouvez le voir aussi dans le keytool -list -v ci-dessus.

Étant donné que vous souhaitez (à juste titre) utiliser cette p12 pour votre client, votre logique serveur a également besoin de pour lui faire confiance. (L'utiliser pour l'authentification sortante ne le rend pas automatiquement approuvé pour l'authentification entrante.) Mais seulement si/quand clientAuth est réellement fait, ce qui n'est pas la valeur par défaut; votre code serveur .setNeedClientAuth (true) sur SSLServerSocket avant d'accepter la connexion? Les approches possibles sont équivalentes à ce qui précède, sauf le saut n ° 2 comme inapplicable. Si le client et le serveur utilisent le même JRE, cela facilite un peu la procédure de cacerts.

Enfin, oui TrustManager 'PKIX' est plus récent et généralement plus riche en fonctionnalités que 'SunX509'. Mais pour le test de base "est l'ancre de confiance dans notre truststore", ils sont équivalents.

Désolé encore pour la tromperie.

+0

Je voudrais également utiliser TrustManagerFactory.getInstance (TrustManagerFactory.getDefaultAlgorithm()) '(d'autant plus que c'est' PKIX' par défaut, pas 'SunX509'). – Bruno

Questions connexes