A récemment posté une question concernant la HttpClient
sur Https ( trouvée ici ). J'ai fait des progrès, mais j'ai rencontré de nouveaux problèmes. Comme avec mon dernier problème, je n'arrive pas à trouver un exemple où que ce soit qui fonctionne pour moi. En gros, je veux que mon client accepte n'importe quel certificat (car je ne pointe jamais qu'un seul serveur) mais je continue à recevoir un javax.net.ssl.SSLException: Not trusted server certificate exception.
Donc voici ce que j'ai:
public void connect() throws A_WHOLE_BUNCH_OF_EXCEPTIONS {
HttpPost post = new HttpPost(new URI(PROD_URL));
post.setEntity(new StringEntity(BODY));
KeyStore trusted = KeyStore.getInstance("BKS");
trusted.load(null, "".toCharArray());
SSLSocketFactory sslf = new SSLSocketFactory(trusted);
sslf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
SchemeRegistry schemeRegistry = new SchemeRegistry();
schemeRegistry.register(new Scheme ("https", sslf, 443));
SingleClientConnManager cm = new SingleClientConnManager(post.getParams(),
schemeRegistry);
HttpClient client = new DefaultHttpClient(cm, post.getParams());
HttpResponse result = client.execute(post);
}
Et voici l'erreur que j'obtiens:
W/System.err( 901): javax.net.ssl.SSLException: Not trusted server certificate
W/System.err( 901): at org.Apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.Java:360)
W/System.err( 901): at org.Apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.Java:92)
W/System.err( 901): at org.Apache.http.conn.ssl.SSLSocketFactory.connectSocket(SSLSocketFactory.Java:321)
W/System.err( 901): at org.Apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.Java:129)
W/System.err( 901): at org.Apache.http.impl.conn.AbstractPoolEntry.open(AbstractPoolEntry.Java:164)
W/System.err( 901): at org.Apache.http.impl.conn.AbstractPooledConnAdapter.open(AbstractPooledConnAdapter.Java:119)
W/System.err( 901): at org.Apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.Java:348)
W/System.err( 901): at org.Apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.Java:555)
W/System.err( 901): at org.Apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.Java:487)
W/System.err( 901): at org.Apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.Java:465)
W/System.err( 901): at me.harrisonlee.test.ssl.MainActivity.connect(MainActivity.Java:129)
W/System.err( 901): at me.harrisonlee.test.ssl.MainActivity.access$0(MainActivity.Java:77)
W/System.err( 901): at me.harrisonlee.test.ssl.MainActivity$2.run(MainActivity.Java:49)
W/System.err( 901): Caused by: Java.security.cert.CertificateException: Java.security.InvalidAlgorithmParameterException: the trust anchors set is empty
W/System.err( 901): at org.Apache.harmony.xnet.provider.jsse.TrustManagerImpl.checkServerTrusted(TrustManagerImpl.Java:157)
W/System.err( 901): at org.Apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.Java:355)
W/System.err( 901): ... 12 more
W/System.err( 901): Caused by: Java.security.InvalidAlgorithmParameterException: the trust anchors set is empty
W/System.err( 901): at Java.security.cert.PKIXParameters.checkTrustAnchors(PKIXParameters.Java:645)
W/System.err( 901): at Java.security.cert.PKIXParameters.<init>(PKIXParameters.Java:89)
W/System.err( 901): at org.Apache.harmony.xnet.provider.jsse.TrustManagerImpl.<init>(TrustManagerImpl.Java:89)
W/System.err( 901): at org.Apache.harmony.xnet.provider.jsse.TrustManagerFactoryImpl.engineGetTrustManagers(TrustManagerFactoryImpl.Java:134)
W/System.err( 901): at javax.net.ssl.TrustManagerFactory.getTrustManagers(TrustManagerFactory.Java:226)W/System.err( 901): at org.Apache.http.conn.ssl.SSLSocketFactory.createTrustManagers(SSLSocketFactory.Java:263)
W/System.err( 901): at org.Apache.http.conn.ssl.SSLSocketFactory.<init>(SSLSocketFactory.Java:190)
W/System.err( 901): at org.Apache.http.conn.ssl.SSLSocketFactory.<init>(SSLSocketFactory.Java:216)
W/System.err( 901): at me.harrisonlee.test.ssl.MainActivity.connect(MainActivity.Java:107)
W/System.err( 901): ... 2 more
Remarque: N'implémentez pas ceci dans le code de production que vous utiliserez un jour sur un réseau auquel vous ne faites pas entièrement confiance. Surtout tout ce qui se passe sur l'internet public.
Votre question est juste ce que je veux savoir. Après quelques recherches, la conclusion est la suivante.
De manière HttpClient, vous devriez créer une classe personnalisée à partir de org.Apache.http.conn.ssl.SSLSocketFactory, et non celle-ci. lui-même. Vous trouverez quelques indices dans cet article La gestion SSL personnalisée a cessé de fonctionner sous Android 2.2 FroYo .
Un exemple est comme ...
import Java.io.IOException;
import Java.net.Socket;
import Java.net.UnknownHostException;
import Java.security.KeyManagementException;
import Java.security.KeyStore;
import Java.security.KeyStoreException;
import Java.security.NoSuchAlgorithmException;
import Java.security.UnrecoverableKeyException;
import Java.security.cert.CertificateException;
import Java.security.cert.X509Certificate;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import org.Apache.http.conn.ssl.SSLSocketFactory;
public class MySSLSocketFactory extends SSLSocketFactory {
SSLContext sslContext = SSLContext.getInstance("TLS");
public MySSLSocketFactory(KeyStore truststore) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
super(truststore);
TrustManager tm = new X509TrustManager() {
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
public X509Certificate[] getAcceptedIssuers() {
return null;
}
};
sslContext.init(null, new TrustManager[] { tm }, null);
}
@Override
public Socket createSocket(Socket socket, String Host, int port, boolean autoClose) throws IOException, UnknownHostException {
return sslContext.getSocketFactory().createSocket(socket, Host, port, autoClose);
}
@Override
public Socket createSocket() throws IOException {
return sslContext.getSocketFactory().createSocket();
}
}
et utiliser cette classe lors de la création d'une instance de HttpClient.
public HttpClient getNewHttpClient() {
try {
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
trustStore.load(null, null);
MySSLSocketFactory sf = new MySSLSocketFactory(trustStore);
sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
HttpParams params = new BasicHttpParams();
HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);
SchemeRegistry registry = new SchemeRegistry();
registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
registry.register(new Scheme("https", sf, 443));
ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, registry);
return new DefaultHttpClient(ccm, params);
} catch (Exception e) {
return new DefaultHttpClient();
}
}
BTW, le lien ci-dessous est destiné à quelqu'un qui cherche une solution HttpURLConnection . Https Connection Android
J'ai testé les deux types de solutions ci-dessus sur Froyo, et ils fonctionnent tous comme un charme dans mes cas. Enfin, l'utilisation de HttpURLConnection peut poser des problèmes de redirection, mais cela dépasse le sujet.
Remarque: avant de décider de faire confiance à tous les certificats, vous devez probablement bien connaître le site et ne pas en nuire aux utilisateurs.
En effet, le risque que vous prenez devrait être considéré avec soin, y compris l’effet du site factice du pirate informatique mentionné dans les commentaires suivants que j’ai profondément apprécié. Dans certaines situations, bien qu'il puisse être difficile de gérer tous les certificats, vous feriez mieux de connaître les inconvénients implicites de leur faire confiance.
Vous avez essentiellement quatre solutions potentielles pour corriger une exception "Pas de confiance" sous Android à l'aide de httpclient:
Cette réponse utilise la solution n ° 4, qui me semble la plus robuste.
La solution consiste à utiliser un SSLSocketFactory pouvant accepter plusieurs magasins de clés, ce qui vous permet de fournir votre propre magasin de clés avec vos propres certificats. Cela vous permet de charger des certificats de niveau supérieur supplémentaires, tels que Thawte, qui sont peut-être manquants sur certains appareils Android. Il vous permet également de charger vos propres certificats auto-signés. Il utilisera d'abord les certificats de périphérique intégrés par défaut, puis utilisera vos certificats supplémentaires uniquement si nécessaire.
Tout d’abord, vous voudrez déterminer le certificat qui vous manque dans votre KeyStore. Exécutez la commande suivante:
openssl s_client -connect www.yourserver.com:443
Et vous verrez la sortie comme suit:
Certificate chain
0 s:/O=www.yourserver.com/OU=Go to
https://www.thawte.com/repository/index.html/OU=Thawte SSL123
certificate/OU=Domain Validated/CN=www.yourserver.com
i:/C=US/O=Thawte, Inc./OU=Domain Validated SSL/CN=Thawte DV SSL CA
1 s:/C=US/O=Thawte, Inc./OU=Domain Validated SSL/CN=Thawte DV SSL CA
i:/C=US/O=thawte, Inc./OU=Certification Services Division/OU=(c)
2006 thawte, Inc. - For authorized use only/CN=thawte Primary Root CA
Comme vous pouvez le constater, notre certificat racine provient de Thawte. Accédez au site Web de votre fournisseur et recherchez le certificat correspondant. Pour nous, c’était ici , et vous pouvez voir que celui dont nous avions besoin était celui Copyright 2006.
Si vous utilisez un certificat auto-signé, vous n'avez pas besoin de passer à l'étape précédente puisque vous avez déjà votre certificat de signature.
Créez ensuite un fichier de clés contenant le certificat de signature manquant. Crazybob a détaille comment faire cela sur Android , mais l’idée est la suivante:
Si vous ne l'avez pas déjà, téléchargez la bibliothèque du fournisseur de château gonflable à l'adresse: http://www.bouncycastle.org/latest_releases.html . Cela ira sur votre chemin de classe ci-dessous.
Exécutez une commande pour extraire le certificat du serveur et créer un fichier pem. Dans ce cas, mycert.pem.
echo | openssl s_client -connect ${MY_SERVER}:443 2>&1 | \
sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > mycert.pem
Ensuite, exécutez les commandes suivantes pour créer le magasin de clés.
export CLASSPATH=/path/to/bouncycastle/bcprov-jdk15on-155.jar
CERTSTORE=res/raw/mystore.bks
if [ -a $CERTSTORE ]; then
rm $CERTSTORE || exit 1
fi
keytool \
-import \
-v \
-trustcacerts \
-alias 0 \
-file <(openssl x509 -in mycert.pem) \
-keystore $CERTSTORE \
-storetype BKS \
-provider org.bouncycastle.jce.provider.BouncyCastleProvider \
-providerpath /path/to/bouncycastle/bcprov-jdk15on-155.jar \
-storepass some-password
Vous remarquerez que le script ci-dessus place le résultat dans res/raw/mystore.bks
. Vous avez maintenant un fichier que vous allez charger dans votre application Android qui fournit le ou les certificats manquants.
Pour ce faire, enregistrez votre SSLSocketFactory pour le schéma SSL:
final SchemeRegistry schemeRegistry = new SchemeRegistry();
schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
schemeRegistry.register(new Scheme("https", createAdditionalCertsSSLSocketFactory(), 443));
// and then however you create your connection manager, I use ThreadSafeClientConnManager
final HttpParams params = new BasicHttpParams();
...
final ThreadSafeClientConnManager cm = new ThreadSafeClientConnManager(params,schemeRegistry);
Pour créer votre SSLSocketFactory:
protected org.Apache.http.conn.ssl.SSLSocketFactory createAdditionalCertsSSLSocketFactory() {
try {
final KeyStore ks = KeyStore.getInstance("BKS");
// the bks file we generated above
final InputStream in = context.getResources().openRawResource( R.raw.mystore);
try {
// don't forget to put the password used above in strings.xml/mystore_password
ks.load(in, context.getString( R.string.mystore_password ).toCharArray());
} finally {
in.close();
}
return new AdditionalKeyStoresSSLSocketFactory(ks);
} catch( Exception e ) {
throw new RuntimeException(e);
}
}
Et enfin, le code AdditionalKeyStoresSSLSocketFactory, qui accepte votre nouveau KeyStore et vérifie si le KeyStore intégré ne parvient pas à valider un certificat SSL:
/**
* Allows you to trust certificates from additional KeyStores in addition to
* the default KeyStore
*/
public class AdditionalKeyStoresSSLSocketFactory extends SSLSocketFactory {
protected SSLContext sslContext = SSLContext.getInstance("TLS");
public AdditionalKeyStoresSSLSocketFactory(KeyStore keyStore) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
super(null, null, null, null, null, null);
sslContext.init(null, new TrustManager[]{new AdditionalKeyStoresTrustManager(keyStore)}, null);
}
@Override
public Socket createSocket(Socket socket, String Host, int port, boolean autoClose) throws IOException {
return sslContext.getSocketFactory().createSocket(socket, Host, port, autoClose);
}
@Override
public Socket createSocket() throws IOException {
return sslContext.getSocketFactory().createSocket();
}
/**
* Based on http://download.Oracle.com/javase/1.5.0/docs/guide/security/jsse/JSSERefGuide.html#X509TrustManager
*/
public static class AdditionalKeyStoresTrustManager implements X509TrustManager {
protected ArrayList<X509TrustManager> x509TrustManagers = new ArrayList<X509TrustManager>();
protected AdditionalKeyStoresTrustManager(KeyStore... additionalkeyStores) {
final ArrayList<TrustManagerFactory> factories = new ArrayList<TrustManagerFactory>();
try {
// The default Trustmanager with default keystore
final TrustManagerFactory original = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
original.init((KeyStore) null);
factories.add(original);
for( KeyStore keyStore : additionalkeyStores ) {
final TrustManagerFactory additionalCerts = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
additionalCerts.init(keyStore);
factories.add(additionalCerts);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
/*
* Iterate over the returned trustmanagers, and hold on
* to any that are X509TrustManagers
*/
for (TrustManagerFactory tmf : factories)
for( TrustManager tm : tmf.getTrustManagers() )
if (tm instanceof X509TrustManager)
x509TrustManagers.add( (X509TrustManager)tm );
if( x509TrustManagers.size()==0 )
throw new RuntimeException("Couldn't find any X509TrustManagers");
}
/*
* Delegate to the default trust manager.
*/
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
final X509TrustManager defaultX509TrustManager = x509TrustManagers.get(0);
defaultX509TrustManager.checkClientTrusted(chain, authType);
}
/*
* Loop over the trustmanagers until we find one that accepts our server
*/
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
for( X509TrustManager tm : x509TrustManagers ) {
try {
tm.checkServerTrusted(chain,authType);
return;
} catch( CertificateException e ) {
// ignore
}
}
throw new CertificateException();
}
public X509Certificate[] getAcceptedIssuers() {
final ArrayList<X509Certificate> list = new ArrayList<X509Certificate>();
for( X509TrustManager tm : x509TrustManagers )
list.addAll(Arrays.asList(tm.getAcceptedIssuers()));
return list.toArray(new X509Certificate[list.size()]);
}
}
}
Ajoutez ce code avant la HttpsURLConnection
et ce sera fait. J? ai compris.
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'espère que ceci vous aide.
C'est une mauvaise idée. Faire confiance à un certificat n’est que (très) légèrement meilleur que de ne pas utiliser de SSL du tout. Lorsque vous dites "Je veux que mon client accepte n'importe quel certificat (car je ne pointe jamais qu'un seul serveur)", vous supposez que cela signifie en quelque sorte que désigner un "serveur" est sûr, ce qui n'est pas sur un réseau public.
Vous êtes totalement ouvert à une attaque de type "man-in-the-middle" en faisant confiance à n'importe quel certificat. Tout le monde peut utiliser un proxy pour établir votre connexion en établissant une connexion SSL distincte avec vous et avec le serveur final. Le MITM a alors accès à l'intégralité de votre demande et de votre réponse. A moins que vous n'ayez d'abord pas besoin de SSL (votre message n'a rien de sensible et ne fait pas d'authentification), vous ne devriez pas faire confiance à tous les certificats à l'aveuglette.
Vous devriez envisager d'ajouter le cert public à un jks à l'aide de keytool, et de l'utiliser pour construire votre fabrique de sockets, comme ceci:
KeyStore ks = KeyStore.getInstance("JKS");
// get user password and file input stream
char[] password = ("mykspassword")).toCharArray();
ClassLoader cl = this.getClass().getClassLoader();
InputStream stream = cl.getResourceAsStream("myjks.jks");
ks.load(stream, password);
stream.close();
SSLContext sc = SSLContext.getInstance("TLS");
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
kmf.init(ks, password);
tmf.init(ks);
sc.init(kmf.getKeyManagers(), tmf.getTrustManagers(),null);
return sc.getSocketFactory();
Cela a une mise en garde à surveiller. Le certificat expirera éventuellement et le code cessera de fonctionner à ce moment. Vous pouvez facilement déterminer quand cela se produira en consultant le certificat.
Vous pouvez désactiver de cette manière la vérification SSL HttpURLConnection à des fins de test depuis l'API 8:
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
if (conn instanceof HttpsURLConnection) {
HttpsURLConnection httpsConn = (HttpsURLConnection) conn;
httpsConn.setSSLSocketFactory(SSLCertificateSocketFactory.getInsecure(0, null));
httpsConn.setHostnameVerifier(new AllowAllHostnameVerifier());
}
L'API de HttpComponents a été modifié. Cela fonctionne avec le code ci-dessous.
public static HttpClient getTestHttpClient() {
try {
SSLSocketFactory sf = new SSLSocketFactory(new TrustStrategy(){
@Override
public boolean isTrusted(X509Certificate[] chain,
String authType) throws CertificateException {
return true;
}
}, new AllowAllHostnameVerifier());
SchemeRegistry registry = new SchemeRegistry();
registry.register(new Scheme("https",8444, sf));
ClientConnectionManager ccm = new ThreadSafeClientConnManager(registry);
return new DefaultHttpClient(ccm);
} catch (Exception e) {
e.printStackTrace();
return new DefaultHttpClient();
}
}
Le code ci-dessus dans https://stackoverflow.com/a/6378872/1553004 est correct, sauf qu'il DOIT également appeler le vérificateur du nom d'hôte
@Override
public Socket createSocket(Socket socket, String Host, int port, boolean autoClose) throws IOException {
SSLSocket sslSocket = (SSLSocket)sslContext.getSocketFactory().createSocket(socket, Host, port, autoClose);
getHostnameVerifier().verify(Host, sslSocket);
return sslSocket;
}
Je me suis inscrit à stackoverflow expressément pour ajouter ce correctif. Écoutez mon avertissement!
J'ajoute une réponse pour ceux qui utilisent le httpclient-4.5, et fonctionne probablement pour 4.4 également.
import Java.security.cert.CertificateException;
import Java.security.cert.X509Certificate;
import org.Apache.http.HttpResponse;
import org.Apache.http.client.HttpClient;
import org.Apache.http.client.HttpResponseException;
import org.Apache.http.client.fluent.ContentResponseHandler;
import org.Apache.http.client.methods.HttpPost;
import org.Apache.http.conn.ssl.NoopHostnameVerifier;
import org.Apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.Apache.http.conn.ssl.TrustStrategy;
import org.Apache.http.impl.client.CloseableHttpClient;
import org.Apache.http.impl.client.HttpClients;
import org.Apache.http.ssl.SSLContextBuilder;
public class HttpClientUtils{
public static HttpClient getHttpClientWithoutSslValidation_UsingHttpClient_4_5_2() {
try {
SSLContextBuilder builder = new SSLContextBuilder();
builder.loadTrustMaterial(null, new TrustStrategy() {
@Override
public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
return true;
}
});
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(builder.build(), new NoopHostnameVerifier());
CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
return httpclient;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
Faire confiance à tous les certificats n’était pas une réelle alternative pour moi, j’ai donc fait ce qui suit pour que HttpsURLConnection fasse confiance à un nouveau certificat (voir aussi http://nelenkov.blogspot.jp/2011/12/using-custom-certificate-trust- store-on.html ).
Obtenez le certificat; Cela a été fait en exportant le certificat dans Firefox (cliquez sur la petite icône de verrou, obtenez les détails du certificat, cliquez sur Exporter), puis utilisé portecle pour exporter un fichier de clés certifiées (BKS).
Chargez le magasin de clés depuis /res/raw/geotrust_cert.bks avec le code suivant:
final KeyStore trustStore = KeyStore.getInstance("BKS");
final InputStream in = context.getResources().openRawResource(
R.raw.geotrust_cert);
trustStore.load(in, null);
final TrustManagerFactory tmf = TrustManagerFactory
.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(trustStore);
final SSLContext sslCtx = SSLContext.getInstance("TLS");
sslCtx.init(null, tmf.getTrustManagers(),
new Java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sslCtx
.getSocketFactory());
Je l'ai utilisé et cela fonctionne pour moi sur tous les systèmes d'exploitation.
/**
* Disables the SSL certificate checking for new instances of {@link HttpsURLConnection} This has been created to
* aid testing on a local box, not for use on production.
*/
private static void disableSSLCertificateChecking() {
TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {
return null;
}
@Override
public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
// Not implemented
}
@Override
public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
// Not implemented
}
} };
try {
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(null, trustAllCerts, new Java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
} catch (KeyManagementException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
Je regarde la réponse de "emmby" (réponse du 16 juin à 21h29), élément n ° 4: "Créez une SSLSocketFactory personnalisée qui utilise le certificat intégré KeyStore, mais utilise un autre magasin KeyStore pour tout ce qui échoue pour vérifier avec le défaut. "
Ceci est une implémentation simplifiée. Chargez le magasin de clés du système et fusionnez-le avec le magasin de clés de l'application.
public HttpClient getNewHttpClient() {
try {
InputStream in = null;
// Load default system keystore
KeyStore trusted = KeyStore.getInstance(KeyStore.getDefaultType());
try {
in = new BufferedInputStream(new FileInputStream(System.getProperty("javax.net.ssl.trustStore"))); // Normally: "/system/etc/security/cacerts.bks"
trusted.load(in, null); // no password is "changeit"
} finally {
if (in != null) {
in.close();
in = null;
}
}
// Load application keystore & merge with system
try {
KeyStore appTrusted = KeyStore.getInstance("BKS");
in = context.getResources().openRawResource(R.raw.mykeystore);
appTrusted.load(in, null); // no password is "changeit"
for (Enumeration<String> e = appTrusted.aliases(); e.hasMoreElements();) {
final String alias = e.nextElement();
final KeyStore.Entry entry = appTrusted.getEntry(alias, null);
trusted.setEntry(System.currentTimeMillis() + ":" + alias, entry, null);
}
} finally {
if (in != null) {
in.close();
in = null;
}
}
HttpParams params = new BasicHttpParams();
HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);
SSLSocketFactory sf = new SSLSocketFactory(trusted);
sf.setHostnameVerifier(SSLSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
SchemeRegistry registry = new SchemeRegistry();
registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
registry.register(new Scheme("https", sf, 443));
ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, registry);
return new DefaultHttpClient(ccm, params);
} catch (Exception e) {
return new DefaultHttpClient();
}
}
Un mode simple pour convertir de JKS en BKS:
keytool -importkeystore -destkeystore cacerts.bks -deststoretype BKS -providerclass org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath bcprov-jdk16-141.jar -deststorepass changeit -srcstorepass changeit -srckeystore $Java_HOME/jre/lib/security/cacerts -srcstoretype JKS -noprompt
* Remarque: dans Android 4.0 (ICS), le Trust Store a été modifié. Pour plus d'informations: http://nelenkov.blogspot.com.es/2011/12/ics-trust-store-implementation.html
Voici une version beaucoup plus simple utilisant le code 4.1.2 httpclient. Cela peut ensuite être modifié pour n'importe quel algorithme de confiance que vous jugez utile.
public static HttpClient getTestHttpClient() {
try {
SSLSocketFactory sf = new SSLSocketFactory(new TrustStrategy(){
@Override
public boolean isTrusted(X509Certificate[] chain,
String authType) throws CertificateException {
return true;
}
});
SchemeRegistry registry = new SchemeRegistry();
registry.register(new Scheme("https", 443, sf));
ClientConnectionManager ccm = new ThreadSafeClientConnManager(registry);
return new DefaultHttpClient(ccm);
} catch (Exception e) {
return new DefaultHttpClient();
}
}
Pour ceux qui souhaitent autoriser tous les certificats à fonctionner (à des fins de test) sur OAuth, procédez comme suit:
1) Téléchargez le code source de l’API Android OAuth ici: https://github.com/kaeppler/signpost
2) Trouver le fichier "CommonsHttpOAuthProvider"
3) Changez-le comme ci-dessous:
public class CommonsHttpOAuthProvider extends AbstractOAuthProvider {
private static final long serialVersionUID = 1L;
private transient HttpClient httpClient;
public CommonsHttpOAuthProvider(String requestTokenEndpointUrl, String accessTokenEndpointUrl,
String authorizationWebsiteUrl) {
super(requestTokenEndpointUrl, accessTokenEndpointUrl, authorizationWebsiteUrl);
//this.httpClient = new DefaultHttpClient();//Version implemented and that throws the famous "javax.net.ssl.SSLException: Not trusted server certificate" if the certificate is not signed with a CA
this.httpClient = MySSLSocketFactory.getNewHttpClient();//This will work with all certificates (for testing purposes only)
}
Le "MySSLSocketFactory" ci-dessus est basé sur la réponse acceptée. Pour rendre cela encore plus facile, voici la classe complète:
package com.netcomps.oauth_example;
import Java.io.IOException;
import Java.net.Socket;
import Java.net.UnknownHostException;
import Java.security.KeyManagementException;
import Java.security.KeyStore;
import Java.security.KeyStoreException;
import Java.security.NoSuchAlgorithmException;
import Java.security.UnrecoverableKeyException;
import Java.security.cert.CertificateException;
import Java.security.cert.X509Certificate;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import org.Apache.http.HttpVersion;
import org.Apache.http.client.HttpClient;
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.tsccm.ThreadSafeClientConnManager;
import org.Apache.http.params.BasicHttpParams;
import org.Apache.http.params.HttpParams;
import org.Apache.http.params.HttpProtocolParams;
import org.Apache.http.protocol.HTTP;
//http://stackoverflow.com/questions/2642777/trusting-all-certificates-using-httpclient-over-https
public class MySSLSocketFactory extends SSLSocketFactory {
SSLContext sslContext = SSLContext.getInstance("TLS");
public MySSLSocketFactory(KeyStore truststore) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
super(truststore);
TrustManager tm = new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return null;
}
};
sslContext.init(null, new TrustManager[] { tm }, null);
}
@Override
public Socket createSocket(Socket socket, String Host, int port, boolean autoClose) throws IOException, UnknownHostException {
return sslContext.getSocketFactory().createSocket(socket, Host, port, autoClose);
}
@Override
public Socket createSocket() throws IOException {
return sslContext.getSocketFactory().createSocket();
}
public static HttpClient getNewHttpClient() {
try {
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
trustStore.load(null, null);
SSLSocketFactory sf = new MySSLSocketFactory(trustStore);
sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
HttpParams params = new BasicHttpParams();
HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);
SchemeRegistry registry = new SchemeRegistry();
registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
registry.register(new Scheme("https", sf, 443));
ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, registry);
return new DefaultHttpClient(ccm, params);
} catch (Exception e) {
return new DefaultHttpClient();
}
}
}
J'espère que ça aide quelqu'un.
Tout organisme qui se débat encore avec les certificats SSL StartCom sur Android 2.1 visitez https://www.startssl.com/certs/ et téléchargez le fichier ca.pem, maintenant dans le answer fourni par @emmby remplacer
`export CLASSPATH=bcprov-jdk16-145.jar
CERTSTORE=res/raw/mystore.bks
if [ -a $CERTSTORE ]; then
rm $CERTSTORE || exit 1
fi
keytool \
-import \
-v \
-trustcacerts \
-alias 0 \
-file <(openssl x509 -in mycert.pem) \
-keystore $CERTSTORE \
-storetype BKS \
-provider org.bouncycastle.jce.provider.BouncyCastleProvider \
-providerpath /usr/share/Java/bcprov.jar \
-storepass some-password`
avec
`export CLASSPATH=bcprov-jdk16-145.jar
CERTSTORE=res/raw/mystore.bks
if [ -a $CERTSTORE ]; then
rm $CERTSTORE || exit 1
fi
keytool \
-import \
-v \
-trustcacerts \
-alias 0 \
-file <(openssl x509 -in ca.pem) \
-keystore $CERTSTORE \
-storetype BKS \
-provider org.bouncycastle.jce.provider.BouncyCastleProvider \
-providerpath /usr/share/Java/bcprov.jar \
-storepass some-password`
Devrait fonctionner hors de la boîte. Je me débattais depuis plus d'un jour, même après une réponse parfaite de @emmby .. J'espère que cela aidera quelqu'un ...
entrez la description de l'image ici
Un sspi a échoué dans xamarin Android.
J'ai trouvé cette solution. mettre ce code avant de cliquer sur un lien HTTPS
const SslProtocols _Tls12 = (SslProtocols)0x00000C00;
const SecurityProtocolType Tls12 = (SecurityProtocolType)_Tls12;
ServicePointManager.SecurityProtocol = Tls12;
utiliser cette classe
public class WCFs
{
// https://192.168.30.8/myservice.svc?wsdl
private static final String NAMESPACE = "http://tempuri.org/";
private static final String URL = "192.168.30.8";
private static final String SERVICE = "/myservice.svc?wsdl";
private static String SOAP_ACTION = "http://tempuri.org/iWCFserviceMe/";
public static Thread myMethod(Runnable rp)
{
String METHOD_NAME = "myMethod";
SoapObject request = new SoapObject(NAMESPACE, METHOD_NAME);
request.addProperty("Message", "Https WCF Running...");
return _call(rp,METHOD_NAME, request);
}
protected static HandlerThread _call(final RunProcess rp,final String METHOD_NAME, SoapObject soapReq)
{
final SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
int TimeOut = 5*1000;
envelope.dotNet = true;
envelope.bodyOut = soapReq;
envelope.setOutputSoapObject(soapReq);
final HttpsTransportSE httpTransport_net = new HttpsTransportSE(URL, 443, SERVICE, TimeOut);
try
{
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() // use this section if crt file is handmake
{
@Override
public boolean verify(String hostname, SSLSession session)
{
return true;
}
});
KeyStore k = getFromRaw(R.raw.key, "PKCS12", "password");
((HttpsServiceConnectionSE) httpTransport_net.getServiceConnection()).setSSLSocketFactory(getSSLSocketFactory(k, "SSL"));
}
catch(Exception e){}
HandlerThread thread = new HandlerThread("wcfTd"+ Generator.getRandomNumber())
{
@Override
public void run()
{
Handler h = new Handler(Looper.getMainLooper());
Object response = null;
for(int i=0; i<4; i++)
{
response = send(envelope, httpTransport_net , METHOD_NAME, null);
try
{if(Thread.currentThread().isInterrupted()) return;}catch(Exception e){}
if(response != null)
break;
ThreadHelper.threadSleep(250);
}
if(response != null)
{
if(rp != null)
{
rp.setArguments(response.toString());
h.post(rp);
}
}
else
{
if(Thread.currentThread().isInterrupted())
return;
if(rp != null)
{
rp.setExceptionState(true);
h.post(rp);
}
}
ThreadHelper.stopThread(this);
}
};
thread.start();
return thread;
}
private static Object send(SoapSerializationEnvelope envelope, HttpTransportSE androidHttpTransport, String METHOD_NAME, List<HeaderProperty> headerList)
{
try
{
if(headerList != null)
androidHttpTransport.call(SOAP_ACTION + METHOD_NAME, envelope, headerList);
else
androidHttpTransport.call(SOAP_ACTION + METHOD_NAME, envelope);
Object res = envelope.getResponse();
if(res instanceof SoapPrimitive)
return (SoapPrimitive) envelope.getResponse();
else if(res instanceof SoapObject)
return ((SoapObject) envelope.getResponse());
}
catch(Exception e)
{}
return null;
}
public static KeyStore getFromRaw(@RawRes int id, String algorithm, String filePassword)
{
try
{
InputStream inputStream = ResourceMaster.openRaw(id);
KeyStore keystore = KeyStore.getInstance(algorithm);
keystore.load(inputStream, filePassword.toCharArray());
inputStream.close();
return keystore;
}
catch(Exception e)
{}
return null;
}
public static SSLSocketFactory getSSLSocketFactory(KeyStore trustKey, String SSLAlgorithm)
{
try
{
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(trustKey);
SSLContext context = SSLContext.getInstance(SSLAlgorithm);//"SSL" "TLS"
context.init(null, tmf.getTrustManagers(), null);
return context.getSocketFactory();
}
catch(Exception e){}
return null;
}
}
Il suffit d’ajouter -Dtrust_all_cert=true
à VM arguments. Cet argument indique à Java d’ignorer les vérifications de certificats.