package com.cetcs.logreport.utils; import android.content.Context; import org.apache.http.conn.ssl.SSLSocketFactory; import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; import java.net.Socket; import java.net.UnknownHostException; import java.security.InvalidKeyException; import java.security.KeyStore; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.SignatureException; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; /** * * 解析服務器證書的類,對服務器端的證書serv.crt進行解析,其中server.crt文件是使用 * openssl生成的自簽名的證書 * */ public class SSLVerifyLogServerCrtSocketFactory extends SSLSocketFactory { private static final String TAG = "SSLTrustAllSocketFactory"; private SSLContext mCtx; private Context context; public SSLVerifyLogServerCrtSocketFactory(String crtName, KeyStore truststore, Context context) throws Throwable { super(truststore); this.context = context; try { InputStream certInputStream = new BufferedInputStream(context.getAssets().open(crtName)); CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); final X509Certificate serverCertificate = (X509Certificate) certificateFactory.generateCertificate(certInputStream); mCtx = SSLContext.getInstance("TLS"); mCtx.init(null, new TrustManager[]{new X509TrustManager() { @Override public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { } @Override public void checkServerTrusted(X509Certificate[] x509Certificates, String authType) throws CertificateException { if(x509Certificates == null){ throw new IllegalArgumentException("checkServerTrusted x509Certificates is null "); } if(x509Certificates.length < 0){ throw new IllegalArgumentException("checkServerTrusted x509Certificates is null "); } for(X509Certificate cert :x509Certificates){ cert.checkValidity(); try { cert.verify(serverCertificate.getPublicKey()); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (InvalidKeyException e) { e.printStackTrace(); } catch (NoSuchProviderException e) { e.printStackTrace(); } catch (SignatureException e) { e.printStackTrace(); } } } @Override public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; } }}, null); } catch (Exception ex) { } } @Override public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException, UnknownHostException { return mCtx.getSocketFactory().createSocket(socket, host, port, autoClose); } @Override public Socket createSocket() throws IOException { return mCtx.getSocketFactory().createSocket(); } //第一個參數是服務器證書的名字例如:server.crt,第二個參數是應用的上下文 public static SSLSocketFactory getSocketFactory( String crtName,Context context) { try { if(crtName == null || "".equalsIgnoreCase(crtName)){ throw new IllegalArgumentException(" getSocketFactory crtName is null"); } if(context == null){ throw new IllegalArgumentException(" getSocketFactory context is null"); } InputStream certInputStream = new BufferedInputStream(context.getAssets().open(crtName)); CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); X509Certificate serverCertificate = (X509Certificate) certificateFactory.generateCertificate(certInputStream); //生成一個保護服務器證書的keystore String keyStoreType = KeyStore.getDefaultType(); KeyStore keyStore = KeyStore.getInstance(keyStoreType); keyStore.load(null, null); String alias = serverCertificate.getSubjectX500Principal().getName(); keyStore.setCertificateEntry(alias, serverCertificate); //生成SSLSocketFactory SSLSocketFactory factory = new SSLVerifyLogServerCrtSocketFactory(crtName,keyStore,context); return factory; } catch (Throwable e) { e.printStackTrace(); } return null; } }
需求使用:實現客戶端對服務器的校驗,需要認證服務器證書的合法性,當https在握手的協議中返回給客戶端的證書應該和保存在客戶端本地的證書解析出來的域名應該一樣,說明服務器返回的證書給保證在本地的證書是一樣的,說明服務器是合法的
try { String crtName = "server.crt"; SSLSocketFactory sf = SSLVerifyLogServerCrtSocketFactory.getSocketFactory(crtName, mContext); //對主機的有效域名進行嚴格校驗 sf.setHostnameVerifier(SSLSocketFactory.STRICT_HOSTNAME_VERIFIER);return new DefaultHttpClient(ccm, params);
其中server.crt就是保存在手機app 例如assert目錄下的證書,是app本地保證的證書,這個證書必須和配置到后台例如tomacat服務器中的證書是一模一樣,這里為了客戶端驗證服務器證書的合法性,在手機app客戶端保存了一個證書
mContext是activity或者context對應的手機的上下文,如果這里客戶端和服務器在建立https的過程中,如果服務器返回給客戶端的證書的域名和app本地保存的證書解析出來的域名是一樣的,說明服務器是合法的。
如果客戶端在和服務器建立https協議的時候,不對服務器的合法性做校驗,信任所有的服務器
package com.cetcs.logreport.utils; /** * Created by wei.yuan on 2016/8/2. * * 該類主要是用於對服務器證書的單項驗證 */ import org.apache.http.conn.ssl.SSLSocketFactory; import java.io.IOException; import java.lang.reflect.Field; import java.net.InetAddress; 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; public class SSLSocketFactoryEx extends SSLSocketFactory { SSLContext sslContext = SSLContext.getInstance("TLS"); public SSLSocketFactoryEx(KeyStore truststore) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException { super(truststore); // set up a TrustManager that trusts everything TrustManager tm = new X509TrustManager() { public X509Certificate[] getAcceptedIssuers() { //return new X509Certificate[]{}; return null; } @Override public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException { // TODO Auto-generated method stub } @Override public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { // TODO Auto-generated method stub } }; sslContext.init(null, new TrustManager[] { tm }, new java.security.SecureRandom()); } @Override public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException, UnknownHostException { injectHostname(socket, host); return sslContext.getSocketFactory().createSocket(socket, host, port, autoClose); } @Override public Socket createSocket() throws IOException { return sslContext.getSocketFactory().createSocket(); } private void injectHostname(Socket socket, String host) { try { Field field = InetAddress.class.getDeclaredField("hostName"); field.setAccessible(true); field.set(socket.getInetAddress(), host); } catch (Exception ignored) { } } }
使用技巧:
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType()); trustStore.load(null, null); SSLSocketFactory sf = new SSLSocketFactoryEx(trustStore); sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
這樣大體就是一個使用心得