53 votes

Comment créer un magasin de clés Java au format BKS (BouncyCastle) qui contient une chaîne de certificats client

J'écris une application Android qui nécessite une authentification client SSL. Je sais comment créer un keystore JKS pour une application Java de bureau, mais Android ne prend en charge que le format BKS. Toutes les méthodes que j'ai utilisées pour créer le keystore aboutissent à l'erreur suivante :
handling exception: javax.net.ssl.SSLHandshakeException: null cert chain

Il semble donc que le client n'envoie jamais une chaîne de certificats correcte, probablement parce que je ne crée pas le keystore correctement. Je ne suis pas en mesure d'activer le débogage SSL comme je peux le faire sur le bureau, ce qui rend les choses beaucoup plus difficiles qu'elles ne devraient l'être.

Pour référence, voici la commande qui fonctionne pour créer un BKS. truststore :
keytool -importcert -v -trustcacerts -file "cacert.pem" -alias ca -keystore "mySrvTruststore.bks" -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath "bcprov-jdk16-145.jar" -storetype BKS -storepass testtest


Voici la commande que j'ai essayée et qui ne fonctionne PAS pour créer un client BKS. keystore :

cat clientkey.pem clientcert.pem cacert.pem > client.pem

keytool -import -v -file <(openssl x509 -in client.pem) -alias client -keystore "clientkeystore" -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath "bcprov-jdk16-145.jar" -storetype BKS -storepass testtest

19 votes

Sérieusement, personne n'a d'expérience avec le format BKS ? Argh, pourquoi Android ne pourrait-il pas simplement utiliser le format standard JKS, ou au moins documenter ce format puisque c'est tout ce qu'ils supportent ? Cela devrait être simple...

62voto

Vipul Points 329

Instructions détaillées étape par étape que j'ai suivies pour y parvenir

  • Téléchargez le JAR de bouncycastle à partir de http://repo2.maven.org/maven2/org/bouncycastle/bcprov-ext-jdk15on/1.46/bcprov-ext-jdk15on-1.46.jar ou prenez-le dans le dossier "doc".

  • Configurez BouncyCastle pour PC en utilisant l'une des méthodes ci-dessous.

    • Ajout statique du fournisseur de la Colombie-Britannique (recommandé)
      • Copiez le fichier bcprov-ext-jdk15on-1.46.jar sur chaque ordinateur.
        • D:\tools\jdk1.5.0_09\jre\lib\ext (JDK (JRE groupé)
        • D:\tools\jre1.5.0_09\lib\ext (JRE)
        • C:\ (emplacement à utiliser dans la variable env)
      • Modifiez le fichier java.security sous
        • D:\tools\jdk1.5.0_09\jre\lib\security
        • D:\tools\jre1.5.0_09\lib\security
        • et ajoutez l'entrée suivante
          • security.provider.7=org.bouncycastle.jce.provider.BouncyCastleProvider
      • Ajoutez la variable d'environnement suivante dans la section "Variables utilisateur".
        • CLASSPATH=%CLASSPATH%;c : \bcprov -ext-jdk15on-1.46.jar
    • Ajoutez bcprov-ext-jdk15on-1.46.jar au CLASSPATH de votre projet et ajoutez la ligne suivante dans votre code
      • Security.addProvider(new BouncyCastleProvider()) ;
  • Générer le Keystore en utilisant Bouncy Castle

    • Exécutez la commande suivante
      • keytool -genkey -alias myproject -keystore C:/myproject.keystore -storepass myproject -storetype BKS -provider org.bouncycastle.jce.provider.BouncyCastleProvider
    • Cela génère le fichier C:\myproject.keystore
    • Exécutez la commande suivante pour vérifier si elle est correctement générée ou non
      • keytool -list -keystore C:\myproject.keystore -storetype BKS
  • Configurer BouncyCastle pour TOMCAT

    • Ouvrir D:\tools\apache -tomcat-6.0.35 \conf\server.xml et ajoutez l'entrée suivante

      • <Connecteur port="8443" keystorePass="monprojet" alias="monprojet" keystore="c:/myproject.keystore" keystoreType="BKS" SSLEnabled="true" clientAuth="false" protocol="HTTP/1.1" scheme="https" secure="true" sslProtocol="TLS" sslImplementationName="org.bouncycastle.jce.provider.BouncyCastleProvider"/>
    • Redémarrez le serveur après ces modifications.
  • Configurer le client BouncyCastle pour Android

    • Il n'est pas nécessaire de le configurer car Android prend en charge la version 1.46 de Bouncy Castle en interne dans le fichier "Android.jar" fourni.
    • Il suffit d'implémenter votre version du client HTTP (MyHttpClient.java peut être trouvé ci-dessous) et de définir ce qui suit dans le code
      • SSLSocketFactory.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER) ;
    • Si vous ne faites pas cela, il donne une exception comme ci-dessous
      • javax.net.ssl.SSLException : le nom d'hôte dans le certificat ne correspondait pas : <192.168.104.66> !=
    • En mode production, changez le code ci-dessus en
      • SSLSocketFactory.setHostnameVerifier(SSLSocketFactory.STRICT_HOSTNAME_VERIFIER) ;

MyHttpClient.java

package com.arisglobal.aglite.network;

import java.io.InputStream;
import java.security.KeyStore;

import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.SingleClientConnManager;

import com.arisglobal.aglite.activity.R;

import android.content.Context;

public class MyHttpClient extends DefaultHttpClient {

    final Context context;

    public MyHttpClient(Context context) {
        this.context = context;
    }

    @Override
    protected ClientConnectionManager createClientConnectionManager() {
        SchemeRegistry registry = new SchemeRegistry();

        registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));

        // Register for port 443 our SSLSocketFactory with our keystore to the ConnectionManager
        registry.register(new Scheme("https", newSslSocketFactory(), 443));
        return new SingleClientConnManager(getParams(), registry);
    }

    private SSLSocketFactory newSslSocketFactory() {
        try {
            // Get an instance of the Bouncy Castle KeyStore format
            KeyStore trusted = KeyStore.getInstance("BKS");

            // Get the raw resource, which contains the keystore with your trusted certificates (root and any intermediate certs)
            InputStream in = context.getResources().openRawResource(R.raw.aglite);
            try {
                // Initialize the keystore with the provided trusted certificates.
                // Also provide the password of the keystore
                trusted.load(in, "aglite".toCharArray());
            } finally {
                in.close();
            }

            // Pass the keystore to the SSLSocketFactory. The factory is responsible for the verification of the server certificate.
            SSLSocketFactory sf = new SSLSocketFactory(trusted);

            // Hostname verification from certificate
            // http://hc.apache.org/httpcomponents-client-ga/tutorial/html/connmgmt.html#d4e506
            sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
            return sf;
        } catch (Exception e) {
            throw new AssertionError(e);
        }
    }
}

Comment invoquer le code ci-dessus dans votre classe Activity :

DefaultHttpClient client = new MyHttpClient(getApplicationContext());
HttpResponse response = client.execute(...);

0 votes

Je suis la procédure ci-dessus, je l'ai bien fait jusqu'à l'étape "Générer le Keystore en utilisant Bouncy Castle", ici, lorsque vous exécutez la commande keytool, il donne l'erreur suivante, keytool erreur : java.lang.ClassNotFoundException : org.bouncycastle.jce.provider.BouncyCastleProvider, s'il vous plaît suggérer

0 votes

J'ai réussi cette étape, en spécifiant le chemin de la bibliothèque BouncyCastle également dans la commande keytool, merci.

0 votes

Je ne travaille plus sur ce projet et je n'ai donc pas vérifié moi-même, mais j'accepte cette réponse car vous méritez quelques points pour une réponse aussi détaillée et, d'après les bonbons et vos expériences, il semble que ce soit une solution qui fonctionne. Merci de votre contribution.

26voto

gnclmorais Points 2288

J'utilise Portecle et ça marche comme sur des roulettes.

0 votes

Je l'utilise, puisque je travaille aussi avec KeyStores, et ça marche. Ok, ce n'est pas parfait, mais ça aide beaucoup.

2 votes

J'ai également utilisé Portecle et je confirme qu'il a réglé mon problème ! !! J'ai dû utiliser HTTPSUrlConnection dans mon projet Android et modifier un peu TrustManagerFactory.

1 votes

Je veux juste noter que Portecle 1.7 qui, au moment où j'écris ces lignes, est le plus récent, a un bouncycastle obsolète qui n'est pas capable d'ouvrir des keystores bks plus récents.

4voto

Cthulhu Points 21

Je ne pense pas que votre problème soit lié au keystore de BouncyCastle ; je pense que le problème est lié à un paquet javax.net.ssl défectueux dans Android. Le keystore de BouncyCastle est un ennui suprême parce qu'Android a changé un comportement par défaut de Java sans le documenter nulle part -- et a supprimé le fournisseur par défaut -- mais il fonctionne.

Notez que pour l'authentification SSL, vous pouvez avoir besoin de 2 keystores. Le keystore "TrustManager", qui contient les certificats de l'autorité de certification, et le keystore "KeyManager", qui contient les clés publiques/privées de votre site client. (La documentation est quelque peu vague sur ce qui doit se trouver dans le keystore "KeyManager"). En théorie, vous ne devriez pas avoir besoin du keystore TrustManager si tous vos certificats sont signés par des autorités de certification "bien connues", par exemple Verisign, Thawte, etc. Faites-moi savoir comment cela fonctionne pour vous. Votre serveur aura également besoin de l'autorité de certification pour ce qui a été utilisé pour signer votre client.

Je n'ai pas du tout pu créer une connexion SSL en utilisant javax.net.ssl. J'ai désactivé l'authentification SSL du client côté serveur, et je n'ai toujours pas pu créer la connexion. Comme mon objectif final était un GET HTTPS, j'ai tenté d'utiliser le client HTTP Apache fourni avec Android. Cela a en quelque sorte fonctionné. Je pouvais établir la connexion HTTPS, mais je ne pouvais toujours pas utiliser l'authentification SSL. Si j'activais l'authentification SSL du client sur mon serveur, la connexion échouait. Je n'ai pas vérifié le code du client HTTP Apache, mais je soupçonne qu'il utilise sa propre implémentation SSL et n'utilise pas javax.net.ssl.

0 votes

Oui, j'utilise un truststore séparé (créé à l'aide de la commande dans ma question) ainsi qu'un keystore que j'ai créé à l'aide de la deuxième commande dans ma question (j'ai également essayé de le créer de plusieurs autres façons, sans succès). Je n'avais pas réalisé que le paquet ssl dans Android pouvait être le coupable. Je vais voir si je peux obtenir une réponse des responsables d'Android.

4voto

Fei Points 21

Je ne sais pas si vous avez résolu ce problème ou non, mais voici comment je procède et cela fonctionne sur Android :

  1. Utilisez openssl pour fusionner le cert du client (le cert doit être signé par un CA qui est accepté par le serveur) et la clé privée dans une paire de clés au format PCKS12 : openssl pkcs12 -export -in clientcert.pem -inkey clientkey.pem -out client.p12
  2. Il se peut que vous deviez patcher votre JRE pour umlimited strength encryption depends on your key strength : copiez les fichiers jar de Fichiers de politique de compétence JCE 5.0 à force illimitée et remplacez ceux de votre JRE (par exemple. C:\Program Archivos \Java\jre6\lib\security )
  3. Utilisez l'outil Portecle mentionné ci-dessus et créez un nouveau keystore au format BKS.
  4. Importez la paire de clés PCKS12 générée à l'étape 1 et enregistrez-la en tant que keystore BKS. Ce keystore fonctionne avec l'authentification du client Android.
  5. Si vous avez besoin de faire une chaîne de certificats, vous pouvez utiliser cet outil IBM : KeyMan pour fusionner la paire de clés PCKS12 du client avec le certificat de l'AC. Mais il ne génère qu'un keystore JKS, vous aurez donc besoin de Protecle pour le convertir au format BKS.

1voto

saxos Points 1494

Votre commande pour créer le keystore BKS me semble correcte.

Comment initialiser le keystore.

Vous devez créer et passer votre propre SSLSocketFactory. Voici un exemple qui utilise l'outil Apache org.apache.http.conn.ssl.SSLSocketFactory

Mais je pense que vous pouvez faire à peu près la même chose sur la javax.net.ssl.SSLSocketFactory

    private SSLSocketFactory newSslSocketFactory() {
    try {
        // Get an instance of the Bouncy Castle KeyStore format
        KeyStore trusted = KeyStore.getInstance("BKS");
        // Get the raw resource, which contains the keystore with
        // your trusted certificates (root and any intermediate certs)
        InputStream in = context.getResources().openRawResource(R.raw.mykeystore);
        try {
            // Initialize the keystore with the provided trusted certificates
            // Also provide the password of the keystore
            trusted.load(in, "testtest".toCharArray());
        } finally {
            in.close();
        }
        // Pass the keystore to the SSLSocketFactory. The factory is responsible
        // for the verification of the server certificate.
        SSLSocketFactory sf = new SSLSocketFactory(trusted);
        // Hostname verification from certificate
        // http://hc.apache.org/httpcomponents-client-ga/tutorial/html/connmgmt.html#d4e506
        sf.setHostnameVerifier(SSLSocketFactory.STRICT_HOSTNAME_VERIFIER);
        return sf;
    } catch (Exception e) {
        throw new AssertionError(e);
    }
}

Faites-moi savoir si ça a marché.

0 votes

La commande que j'ai postée est correcte pour créer un magasin de confiance. Et elle fonctionne à cette fin. J'essaie de créer un magasin de clés pour utiliser l'authentification du client, mais cela ne fonctionne pas. J'ai mis à jour ma question pour inclure la commande que j'ai essayée et qui ne fonctionne pas.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X