SSL Issue with Android
I have a very specific SSL issue on my Android. If I try to visit a particular website via code, I get the following error:
SSL handshake failure: Failure in SSL library, usually a protocol error
error:140773F2:SSL routines:SSL23_GET_SERVER_HELLO: sslv3 alert unexpected message (external/openssl/ssl/s23_cInt.c:500 0xaf076228:0x00000000)
I get this regardless of the build... I've tried it on API levels 1.5, 1.6, 2.2, and 4.0 and gotten the same result each time.
After some troubleshooting I tried to visit the website through a browser and I got the following error:
Data connectivity problem
A secure connection could not be established.
Here's the thing, though... the website opens just fine on Windows browsers (tested on Firefox, IE, and Chrome). It also opens just fine on iOS devices which uses the same webkit as the Android, which is odd. The website also works without problems on the Opera Mini browser.
Here's the website.
I've tried workarounds by adding the client certificate to the keystore and by ignoring invalid client certificates, with no results. However it appears that the certificate itself is not the issue.
I'm at an impasse. Can anybody provide any guidance on how I can get this to work?
How are you accessing this site? Through the Android browser? WebView? Or HttpClient/HTTPSURLConnection? It seems it only responds to SSL3, you have to force your client to use it.
I found the solution (thank you Nikolay for pointing me in the right direction).
The problem was two-fold... one, it was returning a site certificate that Android didn't like, and two, it had only SSLv3 (and not TLS) enabled.
Here was my solution. First I had to create a custom socket factory class:
public class MySSLSocketFactory extends SSLSocketFactory {
SSLContext sslContext = SSLContext.getInstance("SSLv3");
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 {
SSLSocket S = (SSLSocket) sslContext.getSocketFactory().createSocket(socket, host, port, autoClose);
S.setEnabledProtocols(new String[] {"SSLv3"});
return S;
}
@Override
public Socket createSocket() throws IOException {
SSLSocket S = (SSLSocket) sslContext.getSocketFactory().createSocket();
S.setEnabledProtocols(new String[] {"SSLv3"});
return S;
}
}
Second, I had this custom HttpClient defined in my code:
public HttpClient getNewHttpClient() {
try {
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
trustStore.load(null, null);
MySSLSocketFactory sf = new MySSLSocketFactory(trustStore);
sf.setHostnameVerifier(MySSLSocketFactory.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();
}
}
Third, I called the custom HttpClient and parsed the results:
public String test(String URIString) {
HttpClient httpClient = getNewHttpClient();
String result = "";
URI uri;
try {
uri = new URI(URIString);
} catch (URISyntaxException e1) {
return "ERROR";
}
HttpHost host = new HttpHost(uri.getHost(), 443, uri.getScheme());
HttpPost httppost = new HttpPost(uri.getPath());
try {
HttpResponse response = httpClient.execute(host, httppost);
BufferedReader reader = new BufferedReader(
new InputStreamReader(
response.getEntity().getContent()
)
);
String line = null;
while ((line = reader.readLine()) != null){
result += line + "n";
}
return result;
} catch (ClientProtocolException e) {
return "ERROR";
} catch (IOException e) {
return "ERROR";
}
}
Use this and call this method HttpsTrustManager.allowAllSSL()
It will solves the problem and its working fine for me.
public class HttpsTrustManager implements X509TrustManager {
private static TrustManager[] trustManagers;
private static final X509Certificate[] _AcceptedIssuers = new X509Certificate[]{};
@Override
public void checkClientTrusted(
java.security.cert.X509Certificate[] x509Certificates, String s)
throws java.security.cert.CertificateException {
}
@Override
public void checkServerTrusted(
java.security.cert.X509Certificate[] x509Certificates, String s)
throws java.security.cert.CertificateException {
}
public boolean isClientTrusted(X509Certificate[] chain) {
return true;
}
public boolean isServerTrusted(X509Certificate[] chain) {
return true;
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return _AcceptedIssuers;
}
public static void allowAllSSL() {
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String arg0, SSLSession arg1) {
return true;
}
});
SSLContext context = null;
if (trustManagers == null) {
trustManagers = new TrustManager[]{new HttpsTrustManager()};
}
try {
context = SSLContext.getInstance("TLS");
context.init(null, trustManagers, new SecureRandom());
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (KeyManagementException e) {
e.printStackTrace();
}
HttpsURLConnection.setDefaultSSLSocketFactory(context
.getSocketFactory());
}
}
链接地址: http://www.djcxy.com/p/60758.html上一篇: 为什么DateTime.Now.Year是int而不是ushort
下一篇: Android的SSL问题