Acceptation SSL auto-signée sur Android
Comment accepter un certificat auto-signé en Java sur Android?
un échantillon de code serait parfait.
j'ai cherché partout sur Internet et alors que certaines personnes prétendent avoir trouvé la solution, soit il ne fonctionne pas ou il n'y a pas de code échantillon pour le sauvegarder.
5 réponses
j'ai cette fonctionnalité dans exchangeIt, qui se connecte à Microsoft exchange via WebDav. Voici un peu de code pour créer un HttpClient qui se connectera aux certificats auto signés via SSL:
SchemeRegistry schemeRegistry = new SchemeRegistry();
// http scheme
schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
// https scheme
schemeRegistry.register(new Scheme("https", new EasySSLSocketFactory(), 443));
HttpParams params = new BasicHttpParams();
params.setParameter(ConnManagerPNames.MAX_TOTAL_CONNECTIONS, 30);
params.setParameter(ConnManagerPNames.MAX_CONNECTIONS_PER_ROUTE, new ConnPerRouteBean(30));
params.setParameter(HttpProtocolParams.USE_EXPECT_CONTINUE, false);
HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
ClientConnectionManager cm = new ThreadSafeClientConnManager(params, schemeRegistry);
le EasySSLSocketFactory est ici , et le EasyX509TrustManager est ici .
le code pour exchangeIt est open source, et hébergé sur googlecode ici , si vous avez toutes les questions. Je n'y travaille plus activement, mais le code devrait marcher.
notez que depuis Android 2.2 le processus a changé un peu, donc cochez ce pour faire fonctionner le code ci-dessus.
comme EJP a correctement commenté, " les lecteurs devraient noter que cette technique est radicalement insécurité. SSL n'est pas sécurisé à moins qu'au moins un pair ne soit authentifié. Voir RFC 2246."
ayant dit cela, Voici une autre façon, sans aucune classe supplémentaire:
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.X509TrustManager;
private void trustEveryone() {
try {
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier(){
public boolean verify(String hostname, SSLSession session) {
return true;
}});
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, new X509TrustManager[]{new X509TrustManager(){
public void checkClientTrusted(X509Certificate[] chain,
String authType) throws CertificateException {}
public void checkServerTrusted(X509Certificate[] chain,
String authType) throws CertificateException {}
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}}}, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(
context.getSocketFactory());
} catch (Exception e) { // should never happen
e.printStackTrace();
}
}
j'ai fait face à ce problème hier, lors de la migration de L'API RESTful de notre société vers HTTPS, mais en utilisant des certificats SSL auto-signés.
j'ai cherché partout, mais toutes les réponses "correctes" que j'ai trouvées consistaient à désactiver la validation du certificat, dépassant clairement tout le sens de SSL.
j'ai finalement trouvé une solution:
-
Créer Le KeyStore Local
pour permettre à votre application de valider vos certificats auto-signés, vous devez fournir un keystore personnalisé avec les certificats D'une manière que Android peut faire confiance à votre endpoint.
le format pour ces keystores personnalisés est "BKS" de BouncyCastle , donc vous avez besoin de la version 1.46 de BouncyCastleProvider que vous pouvez télécharger ici .
vous aussi j'ai besoin de votre certificat auto-signé, je suppose qu'il s'appelle self_cert.pem
.
maintenant la commande pour créer votre keystore est:
<!-- language: lang-sh -->
$ keytool -import -v -trustcacerts -alias 0 \
-file *PATH_TO_SELF_CERT.PEM* \
-keystore *PATH_TO_KEYSTORE* \
-storetype BKS \
-provider org.bouncycastle.jce.provider.BouncyCastleProvider \
-providerpath *PATH_TO_bcprov-jdk15on-146.jar* \
-storepass *STOREPASS*
PATH_TO_KEYSTORE
pointe vers un fichier où votre keystore sera créé. ne DOIT PAS EXISTER .
PATH_TO_bcprov-jdk15on-146.jar.JAR
est le chemin d'accès au téléchargement .jar bibliothèque.
STOREPASS
est votre nouvelle clé de voûte le mot de passe.
- Inclure le fichier de clés dans votre Application
Copiez votre keystore nouvellement créé de PATH_TO_KEYSTORE
à res/raw/certs.bks
( certs.bks est juste le nom de fichier; vous pouvez utiliser n'importe quel nom que vous voulez)
créer une clé dans res/values/strings.xml
avec
<!-- language: lang-xml -->
<resources>
...
<string name="store_pass">*STOREPASS*</string>
...
</resources>
-
créer une classe qui hérite
DefaultHttpClient
import android.content.Context; import android.util.Log; 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.params.HttpParams; import java.io.IOException; import java.io.InputStream; import java.security.*; public class MyHttpClient extends DefaultHttpClient { private static Context appContext = null; private static HttpParams params = null; private static SchemeRegistry schmReg = null; private static Scheme httpsScheme = null; private static Scheme httpScheme = null; private static String TAG = "MyHttpClient"; public MyHttpClient(Context myContext) { appContext = myContext; if (httpScheme == null || httpsScheme == null) { httpScheme = new Scheme("http", PlainSocketFactory.getSocketFactory(), 80); httpsScheme = new Scheme("https", mySSLSocketFactory(), 443); } getConnectionManager().getSchemeRegistry().register(httpScheme); getConnectionManager().getSchemeRegistry().register(httpsScheme); } private SSLSocketFactory mySSLSocketFactory() { SSLSocketFactory ret = null; try { final KeyStore ks = KeyStore.getInstance("BKS"); final InputStream inputStream = appContext.getResources().openRawResource(R.raw.certs); ks.load(inputStream, appContext.getString(R.string.store_pass).toCharArray()); inputStream.close(); ret = new SSLSocketFactory(ks); } catch (UnrecoverableKeyException ex) { Log.d(TAG, ex.getMessage()); } catch (KeyStoreException ex) { Log.d(TAG, ex.getMessage()); } catch (KeyManagementException ex) { Log.d(TAG, ex.getMessage()); } catch (NoSuchAlgorithmException ex) { Log.d(TAG, ex.getMessage()); } catch (IOException ex) { Log.d(TAG, ex.getMessage()); } catch (Exception ex) { Log.d(TAG, ex.getMessage()); } finally { return ret; } } }
utilisez simplement une instance de **MyHttpClient**
comme vous le feriez avec **DefaultHttpClient**
pour faire vos requêtes HTTPS, et il utilisera et validera correctement vos certificats SSL auto-signés.
HttpResponse httpResponse;
HttpPost httpQuery = new HttpPost("https://yourserver.com");
... set up your query ...
MyHttpClient myClient = new MyHttpClient(myContext);
try{
httpResponse = myClient.(peticionHttp);
// Check for 200 OK code
if (httpResponse.getStatusLine().getStatusCode() == HttpURLConnection.HTTP_OK) {
... do whatever you want with your response ...
}
}catch (Exception ex){
Log.d("httpError", ex.getMessage());
}
sauf si j'ai manqué quelque chose, les autres réponses sur cette page sont dangereuses, et sont fonctionnellement équivalentes à ne pas utiliser SSL du tout. Si vous faites confiance aux certificats auto-signés sans faire d'autres vérifications pour vous assurer que les certificats sont ceux que vous attendez, alors n'importe qui peut créer un certificat auto-signé et peut prétendre être votre serveur. À ce stade, vous n'avez aucune sécurité réelle.
Le seulement moyen légitime pour ce faire (sans écrire une pile SSL complète) est d'ajouter une ancre de confiance supplémentaire à faire confiance pendant le processus de vérification du certificat. Les deux impliquent le codage dur le certificat d'ancrage de confiance dans votre application et de l'ajouter à tous les ancrages de confiance que le système D'exploitation fournit (ou bien vous ne serez pas en mesure de vous connecter à votre site si vous obtenez un vrai certificat).
je suis conscient de deux façons de le faire:
-
créer un magasin en fiducie personnalisé comme décrit à http://www.ibm.com/developerworks/java/library/j-customssl/#8
-
créez une instance personnalisée de X509TrustManager et outrepassez la méthode getAcceptedIssuers pour retourner un tableau qui contient votre certificat:
public X509Certificate[] getAcceptedIssuers() { X509Certificate[] trustedAnchors = super.getAcceptedIssuers(); /* Create a new array with room for an additional trusted certificate. */ X509Certificate[] myTrustedAnchors = new X509Certificate[trustedAnchors.length + 1]; System.arraycopy(trustedAnchors, 0, myTrustedAnchors, 0, trustedAnchors.length); /* Load your certificate. Thanks to /q/x509trustmanager-override-without-allowing-all-certs-45139/"fileName-of-cert"); CertificateFactory cf = CertificateFactory.getInstance("X.509"); X509Certificate cert = (X509Certificate)cf.generateCertificate(inStream); inStream.close(); /* Add your anchor cert as the last item in the array. */ myTrustedAnchors[trustedAnchors.length] = cert; return myTrustedAnchors; }
notez que ce code n'a pas été testé et ne peut même pas être compilé, mais devrait au moins vous orienter dans la bonne direction.