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);
这样大体就是一个使用心得