J'écris une application Android qui nécessite une authentification client SSL. Je sais comment créer un magasin de clés JKS pour une application Java de bureau, mais Android ne prend en charge que le format BKS. Chaque fois que j'ai essayé de créer le magasin de clés, l'erreur suivante est générée:handling exception: javax.net.ssl.SSLHandshakeException: null cert chain
Il semble donc que le client n'envoie jamais une chaîne de certificats appropriée, probablement parce que je ne crée pas correctement le magasin de clés. Je ne parviens pas à activer le débogage SSL comme je le peux sur le bureau, ce qui rend la tâche beaucoup plus difficile qu'elle ne devrait l'être.
Pour référence, voici la commande que IS travaille pour créer un fichier 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 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
Instructions détaillées étape par étape que j'ai suivies pour y parvenir
Configurez BouncyCastle pour Tomcat
Ouvrez D:\tools\Apache-Tomcat-6.0.35\conf\server.xml et ajoutez l'entrée suivante
Redémarrez le serveur après ces modifications.
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 appeler le code ci-dessus dans votre classe d'activité:
DefaultHttpClient client = new MyHttpClient(getApplicationContext());
HttpResponse response = client.execute(...);
J'utilise Portecle , et cela fonctionne comme un charme.
Je ne pense pas que votre problème concerne le magasin de clés BouncyCastle; Je pense que le problème est avec un paquet javax.net.ssl cassé dans Android. Le magasin de clés BouncyCastle est un désagrément suprême car Android a modifié un comportement Java par défaut sans le documenter nulle part et a supprimé le fournisseur par défaut, mais cela fonctionne.
Notez que pour l'authentification SSL, vous aurez peut-être besoin de 2 magasins de clés. Le magasin de clés "TrustManager", qui contient les certificats CA, et le magasin de clés "KeyManager", qui contient les clés publique/privée de votre site client. (La documentation est quelque peu vague sur ce qui doit être dans le magasin de clés KeyManager.) En théorie, vous ne devriez pas avoir besoin du magasin de clés TrustManager si tous vos certificats sont signés par des autorités de certification "bien connues", telles que Verisign, Thawte, etc. etc. Faites-moi savoir comment cela fonctionne pour vous. Votre serveur aura également besoin de l'autorité de certification pour tout ce qui a été utilisé pour signer votre client.
Je ne pouvais pas créer de connexion SSL avec javax.net.ssl. J'ai désactivé l'authentification SSL du client côté serveur et je ne pouvais toujours pas créer la connexion. Comme mon objectif final était un HTTPS GET, j'ai essayé d'utiliser le client HTTP Apache fourni avec Android. Ce genre de travail. Je pouvais établir la connexion HTTPS, mais je ne pouvais toujours pas utiliser l'authentification SSL. Si j'autorisais l'authentification SSL du client sur mon serveur, la connexion échouerait. Je n'ai pas vérifié le code du client HTTP Apache, mais je pense qu'ils utilisent leur propre implémentation SSL et qu'ils n'utilisent pas javax.net.ssl.
Je ne suis pas sûr que vous ayez résolu ce problème ou pas, mais voici comment je le fais et cela fonctionne sous Android:
ligne de commande:
keytool -genseckey -alias aliasName -keystore truststore.bks -providerclass org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath /path/to/jar/bcprov-jdk16-1.46.jar -storetype BKS
Votre commande de création du magasin de clés BKS me semble correcte.
Comment initialisez-vous le magasin de clés?.
Vous devez créer et transmettre votre propre SSLSocketFactory. Voici un exemple qui utilise le org.Apache.http.conn.ssl.SSLSocketFactory
d'Apache
Mais je pense que vous pouvez faire la même chose sur le 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);
}
}
S'il vous plaît laissez-moi savoir si cela a fonctionné.
Utilisez ce manuel http://blog.antoine.li/2010/10/22/Android-trusting-ssl-certificates/ Ce guide m'a vraiment aidé. Il est important d'observer une séquence de certificats dans le magasin. Par exemple: importez d'abord le certificat de l'autorité de certification intermédiaire le plus bas, puis jusqu'au certificat de l'autorité de certification racine.