让"javax.net.ssl.SSLPeerUnverifiedException: No peer certificate error"
在运行Android 2.3但不在4中的模拟器中获取。在4中它完美无缺。我正在尝试通过https连接到实时服务器。它使用有效的Thawte证书,适用于所有浏览器以及Android 3和4。
如果有人有代码帮助,请谢谢。此外,如果有人对安全的解决方法有任何建议,我会很感激。我还在学习,我已经解决了这个问题一个星期了。它必须结束,所以我可以继续工作和学习。 Urgh。
这是HttpCLient代码,礼貌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);
}
}
}
以下是实例化它的代码:
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();
}
错误在IOException块中捕获。这是堆栈:
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$2.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)
这是链顺序(来自openssl命令):
我认为链条看起来很好。
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
答案 0 :(得分:28)
当我调试类似的问题时,这个thread非常有用。
摘要Android 2.3 HTTPS / SSL核对清单:
答案 1 :(得分:3)
我和你有完全相同的问题。 Android&gt; 3.X一切都运行良好但是当我尝试使用一些(但不是全部!)2.3.X设备时,我得到了那个着名的“无对等证书错误”例外。
我通过stackoverflow和其他博客挖了很多东西,但我没有找到任何可用于那些“流氓”设备的东西(在我的情况下:正确使用信任库;不需要sni;服务器上的正确证书链顺序等等。 ..)。
看起来Android的Apache HttpClient在某些2.3.X设备上只是无法正常工作。 “无对等证书”异常发生得太早,甚至无法达到自定义主机名验证程序代码,因此像that one这样的解决方案对我不起作用。
这是我的代码:
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);
所以我用javax.net.ssl.HttpsURLConnection重写了一切,现在它正在我测试的所有设备上工作(从2.3.3到4.X)。
这是我的新代码:
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);
证书链和主机名验证正在运行(我对它们进行了测试)。 如果有人想要更好地了解更改,请参阅diff
欢迎提出意见,我希望它能帮助一些人。
答案 2 :(得分:1)
此消息的另一个来源可能是无效的日期/时间设置,例如当使用一个没有电源几个月的设备。相当微不足道,但很难发现。
答案 3 :(得分:0)
您从R.raw.my_cert
加载了哪些证书?此错误或者说服务器配置错误 - 不安装Thawte的主和辅助中间CA - 或者您没有加载和信任正确的证书链。
答案 4 :(得分:0)
(至少)Android 2.3中的证书验证(或更确切地说 - 链式构建)逻辑是错误的。
这是我观察到的:
如果其证书链中的TLS服务器仅提供服务器证书(非自签名或自签名),则可以将服务器证书放入密钥库,验证将成功。
如果其证书链中的TLS服务器也提供中间CA证书,则在密钥库中您必须只放置根CA证书并确保密钥库不包含服务器和中间CA证书(否则验证将失败了。
如果其证书链中的TLS服务器以正确的顺序提供中间和根CA证书,那么您只需确保根CA证书在密钥库中(如果服务器和中间CA无关紧要)证书在那里。
所以如何处理这个“正确/可靠”的方法是在密钥库中只包含根CA证书和非服务器配置“无对等证书” - 如果服务器的证书链不提供中间CA证书或证书是不正确的顺序。您可以使用https://www.ssllabs.com/ssltest/测试服务器。