Erreur "No peer certificate" dans Android 2.3 mais pas dans 4
obtenir le "javax.net.ssl.SSLPeerUnverifiedException: No peer certificate error"
dans un émulateur fonctionnant Android 2.3 mais pas dans 4. En 4, ça marche parfaitement. J'essaie de me connecter à un serveur en direct via https. Il utilise un certificat de dégel valide, fonctionne très bien dans tous les navigateurs et Android 3 et 4.
si quelqu'un a un code d'aide, S'il vous plaît et merci. Aussi, si quelqu'un a des suggestions sur une solution de contournement sécurisée, Je l'apprécierais. J'apprends encore, et je suis sur ce problème depuis une semaine. Il faut que ça cesse, pour que je puisse continuer de travail et d'apprentissage. Urgh.
voici le code HttpCLient, avec la permission D'Antoine Hauck ( http://blog.antoine.li/2010/10/22/android-trusting-ssl-certificates / ):
import java.io.InputStream;
import java.security.KeyStore;
import java.security.cert.CertificateException;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import javax.security.cert.X509Certificate;
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 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.my_cert);
try {
// Initialize the keystore with the provided trusted certificates
// Also provide the password of the keystore
trusted.load(in, "my_pass".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);
}
}
}
et voici le code qui l'instancie:
DefaultHttpClient client = new MyHttpClient(getApplicationContext());
HttpPost post = new HttpPost(server_login_url);
List <NameValuePair> parameters = new ArrayList <NameValuePair>();
parameters.add(new BasicNameValuePair("username", user));
parameters.add(new BasicNameValuePair("password", pass));
try {
post.setEntity(new UrlEncodedFormEntity(parameters, HTTP.UTF_8));
} catch (UnsupportedEncodingException e2) {
// TODO Auto-generated catch block
Log.d(DEBUG_TAG, "in UnsupportedEncodingException - " + e2.getMessage());
e2.printStackTrace();
}
// Execute the GET call and obtain the response
HttpResponse getResponse = null;
try {
getResponse = client.execute(post);
} catch (ClientProtocolException e) {
// TODO Auto-generated catch block
// Toast.makeText(getBaseContext(),message,Toast.LENGTH_LONG).show();
Log.d(DEBUG_TAG, "in ClientProtocolException - " + e.getMessage());
} catch (IOException e) {
// TODO Auto-generated catch block
// Toast.makeText(getBaseContext(),message,Toast.LENGTH_LONG).show();
Log.d(DEBUG_TAG, "in client.execute IOException - " + e.getMessage());
e.printStackTrace();
}
l'erreur est détectée dans le bloc IOException. Voici la pile:
javax.net.ssl.SSLPeerUnverifiedException: No peer certificate
org.apache.harmony.xnet.provider.jsse.SSLSessionImpl.getPeerCertificates(SSLSessionImpl.java:258)
org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java:93)
org.apache.http.conn.ssl.SSLSocketFactory.createSocket(SSLSocketFactory.java:381)
org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:164)
org.apache.http.impl.conn.AbstractPoolEntry.open(AbstractPoolEntry.java:164)
org.apache.http.impl.conn.AbstractPooledConnAdapter.open(AbstractPooledConnAdapter.java:119)
org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:359)
org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:555)
org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:487)
org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:465)
org.ffb.tools.SplashActivity$LoginTask.makeConnection(SplashActivity.java:506)
org.ffb.tools.SplashActivity$LoginTask.doLogin(SplashActivity.java:451)
org.ffb.tools.SplashActivity$LoginTask.doInBackground(SplashActivity.java:439)
org.ffb.tools.SplashActivity$LoginTask.doInBackground(SplashActivity.java:1)
android.os.AsyncTask.call(AsyncTask.java:185)
java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:306)
java.util.concurrent.FutureTask.run(FutureTask.java:138)
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1088)
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:581)
java.lang.Thread.run(Thread.java:1019)
Voici l'ordre en chaîne (de la commande openssl):
la chaîne semble bonne je pense.
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
2 s:/C=US/O=thawte, Inc./OU=Certification Services Division/OU=(c) 2006 thawte, Inc. - For authorized use only/CN=thawte Primary Root CA
i:/C=ZA/ST=Western Cape/L=Cape Town/O=Thawte Consulting cc/OU=Certification Services Division/CN=Thawte Premium Server CA/emailAddress=premium-server@thawte.com
5 réponses
Ce thread a été très utile quand j'ai débogué un problème similaire.
résumé Android 2.3 HTTPS / SSL checklist:
- si votre CA est dans Android 2.3 liste des CA de confiance -- et Thawte est -- il n'est pas nécessaire d'inclure le certificat dans l'application.
- Android 2.3 ne supporte pas Indication du nom du serveur donc si votre serveur est en comptant sur elle pour la handshaking SSL, Android peut ne pas obtenir les certificats que vous attendez.
- avez-vous installé une chaîne de certificats sur le serveur, et la commande est-elle correcte? La plupart des navigateurs gèrent des chaînes de certificats hors-service, mais Android 2.3 ne le fait pas. la réponse de bdc dans le thread que j'ai mentionné ci-dessus décrit comment vérifier la validité de votre certificat SSL et de votre chaîne avec "openssl s_client-connect yourserver.com: 443".
- en déterrant l'ancien appareil 2.3 que vous avez dans votre tiroir du bas, veuillez vous assurer que sa date et son heure sont réglées correctement après avoir été impuissant pendant trop longtemps.
j'avais exactement le même problème que vous. Tout allait bien avec android >3.X mais quand j'ai essayé avec certains (mais pas tous !) 2.3.X devices j'ai eu cette fameuse exception "pas d'erreur de certificat Pair".
j'ai creusé beaucoup à travers stackoverflow et d'autres blogs mais je n'ai rien trouvé qui a fonctionné sur ces appareils" voyous " (dans mon cas: utilisation correcte de truststore; pas de sni nécessaire; commande cert chaîne correcte sur le serveur; etc...).
on dirait que Apache HttpClient d'android n'était que ne fonctionnait pas correctement sur quelque 2.3.X appareils. L'exception " no peer certificates "se produisait trop tôt pour même atteindre un code de vérification personnalisé de nom d'hôte, donc solution comme qu'un ne fonctionnait pas pour moi.
Voici mon code:
KeyStore trustStore = KeyStore.getInstance("BKS");
InputStream is = this.getAssets().open("discretio.bks");
trustStore.load(is, "discretio".toCharArray());
is.close();
SSLSocketFactory sockfacto = new SSLSocketFactory(trustStore);
sockfacto.setHostnameVerifier(SSLSocketFactory.STRICT_HOSTNAME_VERIFIER);
SchemeRegistry schemeRegistry = new SchemeRegistry();
schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
schemeRegistry.register(new Scheme("https", sockfacto, 443));
SingleClientConnManager mgr = new SingleClientConnManager(httpParameters, schemeRegistry);
HttpClient client = new DefaultHttpClient(mgr, httpParameters);
HttpGet request = new HttpGet(url);
HttpResponse response = client.execute(request);
donc j'ai tout réécrit avec javax.net.ssl.HttpsURLConnection et maintenant il fonctionne sur tous les appareils que j'ai testées (2.3.3 à 4.X).
Voici mon nouveau code:
KeyStore trustStore = KeyStore.getInstance("BKS");
InputStream is = this.getAssets().open("discretio.bks");
trustStore.load(is, "discretio".toCharArray());
is.close();
TrustManagerFactory tmf = TrustManagerFactory.getInstance("X509");
tmf.init(trustStore);
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, tmf.getTrustManagers(), null);
URL request = new URL(url);
HttpsURLConnection urlConnection = (HttpsURLConnection) request.openConnection();
//ensure that we are using a StrictHostnameVerifier
urlConnection.setHostnameVerifier(new StrictHostnameVerifier());
urlConnection.setSSLSocketFactory(context.getSocketFactory());
urlConnection.setConnectTimeout(15000);
InputStream in = urlConnection.getInputStream();
//I don't want to change my function's return type (laziness) so I'm building an HttpResponse
BasicHttpEntity res = new BasicHttpEntity();
res.setContent(in);
HttpResponse resp = new BasicHttpResponse(HttpVersion.HTTP_1_1, urlConnection.getResponseCode(), "");
resp.setEntity(res);
La chaîne de certificats et la validation du nom d'hôte fonctionnent (je les ai testés). Si quelqu'un veut prendre un meilleur regard sur le changement, voici un diff
commentaires sont les bienvenus, j'espère que cela aidera certaines personnes.
une autre source de ce message peut être un réglage de date/heure invalide, par exemple lors de l'utilisation d'un appareil qui a été de quelques mois sans courant. Tout à fait insignifiant, mais il peut être difficile à repérer.
Quels certificats chargez-vous de R.raw.my_cert
? Cette erreur concerne soit le fait d'avoir un serveur mal configuré -- de ne pas installer le cas principal de Thawte et le CAs Intermédiaire de Thawte -- soit le fait de ne pas charger et de ne pas faire confiance à la chaîne de certificats correcte.
vérification de certificat (ou plus précisément - chaîne de construction) logique dans (au moins) Android 2.3 est défectueux.
voici ce que j'ai observé:
-
si le serveur TLS dans sa chaîne de certificats ne fournit que le certificat du serveur (non signé ou signé), alors vous pouvez mettre le certificat du serveur dans keystore et la vérification réussira.
-
si le serveur TLS la chaîne de certificat fournit aussi le certificat CA intermédiaire, alors dans le keystore vous devez mettre seulement le certificat CA racine et s'assurer que le keystore ne contient pas les certificats CA du serveur et intermédiaire (sinon la vérification échouera au hasard).
-
si le serveur TLS dans sa chaîne de certificats fournit des certificats CA intermédiaires et root dans le bon ordre, alors vous devez juste vous assurer que le certificat CA root est dans le bon ordre. keystore (peu importe si le serveur et les certificats CA intermédiaires sont là).
ainsi, la façon "correcte/fiable" de gérer ceci est d'inclure dans le keystore uniquement les certificats d'AC racine et de blâmer la configuration du serveur pour "aucun certificat de pair" - dans le cas où la chaîne de certificats du serveur ne fournit pas les certificats d'AC intermédiaires ou les certificats sont dans l'ordre incorrect. Vous pouvez tester le serveur en utilisant https://www.ssllabs.com/ssltest/ .