android获取https证书
最近碰到一个问题, 有朋友问android这边能不能拿到服务器下发的证书,意思就是 自签名证书的https接口,在请求的时候,也没有添加自签名证书进信任列表,直接去发https请求,按照正常https步骤去理解,服务器会返回一个证书,这个证书由于客户端没有添加进信任列表,会导致https请求失败,提示没有找到信任的证书。 朋友疑问: 在服务器返回证书的时候,能不能强行信任呢?
针对这个问题, 我在这边做了测试:
1,由ca签发的ssl证书,部署一个服务
不设置请求的SSLSocketFactory,直接进行https请求
验证结果:ok
2,自签名的ssl证书,部署一个服务
不设置请求的SSLSocketFactory,直接进行https请求
验证结果:error,报错信息 java.security.cert.CertPathValidatorException: Trust anchor for certification path not found,大体意思是没有找打信任的证书,也就是说这个证书没有被当前的请求对象所信任。
看报错信息是android系统已经封装好的证书校验规则。
但是如果不想本地预埋自签名证书,也不想让后台暴露证书下载的接口,该怎么办呢?
我这边提供一个比较简陋的方法,通过webview访问目标https地址,通过webview的onReceivedSslError方法,拿到SslError中的SslCertificate,然后再根据SslCertificate中的saveState方法获取Bundle,从bundle中拿到"x509-certificate"的字节数组,作为字节流读取流,通过通用的httpsutils,设置请求对象的sslsocketfactory。
代码如下:
package com.example.ajaxhttpsdemo; import android.content.Context; import android.net.http.SslCertificate; import android.net.http.SslError; import android.os.Bundle; import android.os.Handler; import android.util.Log; import android.webkit.SslErrorHandler; import android.webkit.WebSettings; import android.webkit.WebView; import android.webkit.WebViewClient; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.security.KeyManagementException; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import javax.net.ssl.KeyManager; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLException; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.X509TrustManager; public class HttpsGetSSLSocketFactory { private static SslCertificate sslCertificate; public static class SSLParams { public SSLSocketFactory sSLSocketFactory; public X509TrustManager trustManager; } public interface Callback{ void onSuccess(SSLParams sslParams); void onError(Exception e); } public static class MySSLException extends Exception{ public MySSLException(String str) { super(str); } } public static void getSslSocketFactoryOnMainThread(Context context, String httpsUrl, final Callback callback){ WebView webView = new WebView(context); webView.setWebViewClient(new WebViewClient(){ @Override public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) { sslCertificate = error.getCertificate(); handler.proceed(); if (sslCertificate!=null){ Bundle bundle = SslCertificate.saveState(sslCertificate); byte[] byteArray = bundle.getByteArray("x509-certificate");//从bundle中拿出写入的x509证书 // byte[] byteArray = "123456".getBytes();//测试错误证书 if (byteArray!=null && byteArray.length>0){ try { callback.onSuccess(getSslSocketFactory(new ByteArrayInputStream(byteArray))); } catch (Exception e) { callback.onError(e); } } }else { callback.onError(new MySSLException("没有获取到服务器证书")); } } }); webView.getSettings().setJavaScriptEnabled(true); webView.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE); webView.loadUrl(httpsUrl); } private static SSLParams getSslSocketFactory(InputStream... certificates) throws Exception { SSLParams sslParams = new SSLParams(); try { TrustManager[] trustManagers = prepareTrustManager(certificates); KeyManager[] keyManagers = prepareKeyManager(null, null); SSLContext sslContext = SSLContext.getInstance("TLS"); X509TrustManager trustManager; if (trustManagers != null) { trustManager = new MyTrustManager( chooseTrustManager(trustManagers)); } else { throw new Exception("证书错误"); } sslContext.init(keyManagers, new TrustManager[]{trustManager}, null); sslParams.sSLSocketFactory = sslContext.getSocketFactory(); sslParams.trustManager = trustManager; return sslParams; } catch (NoSuchAlgorithmException e) { throw new AssertionError(e); } catch (KeyManagementException e) { throw new AssertionError(e); } catch (KeyStoreException e) { throw new AssertionError(e); } catch (Exception e) { throw e; } } private static X509TrustManager chooseTrustManager( TrustManager[] trustManagers) { for (TrustManager trustManager : trustManagers) { if (trustManager instanceof X509TrustManager) { return (X509TrustManager) trustManager; } } return null; } private static TrustManager[] prepareTrustManager( InputStream... certificates) { if (certificates == null || certificates.length <= 0) return null; try { CertificateFactory certificateFactory = CertificateFactory .getInstance("X.509"); KeyStore keyStore = KeyStore.getInstance(KeyStore .getDefaultType()); keyStore.load(null); int index = 0; for (InputStream certificate : certificates) { String certificateAlias = Integer.toString(index++); keyStore.setCertificateEntry(certificateAlias, certificateFactory.generateCertificate(certificate)); try { if (certificate != null) certificate.close(); } catch (IOException e) { e.printStackTrace(); } } TrustManagerFactory trustManagerFactory; trustManagerFactory = TrustManagerFactory .getInstance(TrustManagerFactory.getDefaultAlgorithm()); trustManagerFactory.init(keyStore); return trustManagerFactory.getTrustManagers(); } catch (Exception e) { e.printStackTrace(); } return null; } private static KeyManager[] prepareKeyManager(InputStream bksFile, String password) { try { if (bksFile == null || password == null) return null; KeyStore clientKeyStore = KeyStore.getInstance("PKCS12"); clientKeyStore.load(bksFile, password.toCharArray()); KeyManagerFactory keyManagerFactory = KeyManagerFactory .getInstance(KeyManagerFactory.getDefaultAlgorithm()); keyManagerFactory.init(clientKeyStore, password.toCharArray()); return keyManagerFactory.getKeyManagers(); } catch (Exception e) { e.printStackTrace(); } return null; } private static class MyTrustManager implements X509TrustManager { private X509TrustManager defaultTrustManager; private X509TrustManager localTrustManager; public MyTrustManager(X509TrustManager localTrustManager) throws NoSuchAlgorithmException, KeyStoreException { TrustManagerFactory var4 = TrustManagerFactory .getInstance(TrustManagerFactory.getDefaultAlgorithm()); var4.init((KeyStore) null); defaultTrustManager = chooseTrustManager(var4 .getTrustManagers()); this.localTrustManager = localTrustManager; } @Override public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { } @Override public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { try { defaultTrustManager.checkServerTrusted(chain, authType); } catch (CertificateException ce) { localTrustManager.checkServerTrusted(chain, authType); } } @Override public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; } } }
留作备用,有错误再更正