Android平台实现双向证书(https)验证简单说明

前言

  • Android端实现双向认证的难点主要在于Android接受的证书格式是BKS,而一般提供的证书不包含此格式,需要手动转换
  • 转换之后如果请求不了,表现为握手失败(Handshake failed),则一般是证书转换错误
  • 网上有一大堆转换方法,有的虽能正常生成BKS证书,但还是导致握手失败,推测应该还是转换方式有误
  • 在这里提供一种自己亲自试验过能正常转换的方法供大家参考

证书转换

使用转换工具

  • 在这里提供一个比较好用的转换工具Portecle
  • 下载完后解压双击start.bat即可使用,至于详细使用请自行百度
  • 使用此工具可以很方便地将jks转为bks

通过openssl和keytool转换

  • openssl可以到官网下载,openssl暂时没有windows版,大伙可以在此处下载Win32OpenSSLwindows版本
  • 下载完记得配置环境变量,同时也记得配置jdk的环境变量,因为后面需要用到keytool命令
  • 现在假设你拿到了客户端证书client.crt和client.key(或者client.pem和client.key)
  • 通过以下命令生成pfx文件,期间会让输入密码,输入你想要的密码即可,假如这里输入的是123456

openssl pkcs12 -export -out client.pfx -inkey client.key -in client.pem
或者
openssl pkcs12 -export -out client.pfx -inkey client.key -in client.crt

  • 执行成功则可以看到client.pfx文件,接着再执行如下命令

keytool -importkeystore -srckeystore client.pfx -destkeystore client.jks -srcstoretype PKCS12 -deststoretype JKS

  • 执行成功即可看到生成了client.jks
  • 拿到生成的jks就可以通过Portecle工具转换成bks了

代码实现

SSLHelper.java

import android.content.Context;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.UnrecoverableKeyException;import java.security.cert.CertificateException;import javax.net.ssl.KeyManagerFactory;import javax.net.ssl.SSLContext;import javax.net.ssl.SSLSocketFactory;import javax.net.ssl.TrustManagerFactory;public class SSLHelper {    private static final String TAG = "SSLHelper";    private final static String CLIENT_PRI_KEY = "client.bks";    private final static String TRUSTSTORE_PUB_KEY = "ca.bks";    private final static String CLIENT_BKS_PASSWORD = "123456";    private final static String TRUSTSTORE_BKS_PASSWORD = "123456";    private final static String KEYSTORE_TYPE = "BKS";    private final static String PROTOCOL_TYPE = "TLS";    private final static String CERTIFICATE_STANDARD = "X509";    public static SSLSocketFactory getSSLCertifcation(Context context) {        SSLSocketFactory sslSocketFactory = null;        try {            // 服务器端需要验证的客户端证书,其实就是客户端的keystore            KeyStore keyStore = KeyStore.getInstance(KEYSTORE_TYPE);            // 客户端信任的服务器端证书            KeyStore trustStore = KeyStore.getInstance(KEYSTORE_TYPE);            //读取证书            InputStream ksIn = context.getAssets().open(CLIENT_PRI_KEY);            InputStream tsIn = context.getAssets().open(TRUSTSTORE_PUB_KEY);            //加载证书            keyStore.load(ksIn, CLIENT_BKS_PASSWORD.toCharArray());            trustStore.load(tsIn, TRUSTSTORE_BKS_PASSWORD.toCharArray());            ksIn.close();            tsIn.close();            TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(CERTIFICATE_STANDARD);            KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(CERTIFICATE_STANDARD);            trustManagerFactory.init(trustStore);            keyManagerFactory.init(keyStore, CLIENT_BKS_PASSWORD.toCharArray());            //初始化SSLContext            SSLContext sslContext = SSLContext.getInstance(PROTOCOL_TYPE);            sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), new java.security.SecureRandom());            sslSocketFactory = sslContext.getSocketFactory();        } catch (KeyStoreException e) {            e.printStackTrace();        } catch (IOException e) {            e.printStackTrace();        } catch (CertificateException e) {            e.printStackTrace();        } catch (NoSuchAlgorithmException e) {            e.printStackTrace();        } catch (UnrecoverableKeyException e) {            e.printStackTrace();        } catch (KeyManagementException e) {            e.printStackTrace();        }        return sslSocketFactory;    }}
  • HttpsUtil.java
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.UnrecoverableKeyException;import java.security.cert.CertificateException;import java.security.cert.CertificateFactory;import java.security.cert.X509Certificate;import javax.net.ssl.HostnameVerifier;import javax.net.ssl.KeyManager;import javax.net.ssl.KeyManagerFactory;import javax.net.ssl.SSLContext;import javax.net.ssl.SSLSession;import javax.net.ssl.SSLSocketFactory;import javax.net.ssl.TrustManager;import javax.net.ssl.TrustManagerFactory;import javax.net.ssl.X509TrustManager;public class HttpsUtil {        public static class SSLParams      {          public SSLSocketFactory sSLSocketFactory;          public X509TrustManager trustManager;      }        public static SSLParams getSslSocketFactory(InputStream[] certificates, InputStream bksFile, String password)      {          SSLParams sslParams = new SSLParams();          try          {              TrustManager[] trustManagers = prepareTrustManager(certificates);              KeyManager[] keyManagers = prepareKeyManager(bksFile, password);              SSLContext sslContext = SSLContext.getInstance("TLS");              X509TrustManager trustManager = null;              if (trustManagers != null)              {                  trustManager = new MyTrustManager(chooseTrustManager(trustManagers));              } else              {                  trustManager = new UnSafeTrustManager();              }              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);          }      }    public static class  UnSafeHostnameVerifier implements HostnameVerifier    {          @Override          public boolean verify(String hostname, SSLSession session)          {              return true;          }      }        public static class UnSafeTrustManager implements X509TrustManager      {          @Override          public void checkClientTrusted(X509Certificate[] chain, String authType)                  throws CertificateException          {          }            @Override          public void checkServerTrusted(X509Certificate[] chain, String authType)                  throws CertificateException          {          }            @Override          public X509Certificate[] getAcceptedIssuers()          {            return new X509Certificate[]{};        }      }        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)                    {                  }              }              TrustManagerFactory trustManagerFactory = null;                trustManagerFactory = TrustManagerFactory.                      getInstance(TrustManagerFactory.getDefaultAlgorithm());              trustManagerFactory.init(keyStore);                TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();                return trustManagers;          } catch (NoSuchAlgorithmException e)          {              e.printStackTrace();          } catch (CertificateException e)          {              e.printStackTrace();          } catch (KeyStoreException e)          {              e.printStackTrace();          } 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("BKS");              clientKeyStore.load(bksFile, password.toCharArray());              KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());              keyManagerFactory.init(clientKeyStore, password.toCharArray());              return keyManagerFactory.getKeyManagers();            } catch (KeyStoreException e)          {              e.printStackTrace();          } catch (NoSuchAlgorithmException e)          {              e.printStackTrace();          } catch (UnrecoverableKeyException e)          {              e.printStackTrace();          } catch (CertificateException e)          {              e.printStackTrace();          } catch (IOException e)          {              e.printStackTrace();          } catch (Exception e)          {              e.printStackTrace();          }          return null;      }        private static X509TrustManager chooseTrustManager(TrustManager[] trustManagers)      {          for (TrustManager trustManager : trustManagers)          {              if (trustManager instanceof X509TrustManager)              {                  return (X509TrustManager) trustManager;              }          }          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];          }      }    }  
  • retrofit调用
//创建日志拦截器,用于日志打印        HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();        interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);        //创建okhttp        OkHttpClient httpClient = new OkHttpClient().newBuilder()                .addInterceptor(interceptor)                .sslSocketFactory(SSLHelper.getSSLCertifcation(this), new HttpsUtil.UnSafeTrustManager())                .hostnameVerifier(new HttpsUtil.UnSafeHostnameVerifier())//由于还没有域名,此处设置忽略掉域名校验                .build();         //创建retrofit               String baseUrl = "https://180.101.147.89:8743/";        Retrofit retrofit = new Retrofit.Builder()                .baseUrl(baseUrl)                .client(httpClient)                //.addConverterFactory(GsonConverterFactory.create())                .addConverterFactory(ScalarsConverterFactory.create())                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())                .build();
  • 顺便贴下依赖build.gradle
apply plugin: 'com.android.application'android {...}dependencies {    ...    //retrofit    implementation "com.squareup.retrofit2:retrofit:2.3.0"    implementation "com.squareup.retrofit2:converter-scalars:2.3.0"    implementation "com.squareup.retrofit2:converter-gson:2.3.0"    implementation "com.squareup.okhttp3:logging-interceptor:3.8.1"    }

参考

  • http://blog.csdn.net/zhangyong125/article/details/50402183

更多相关文章

  1. 基于Android移动终端的搜索客户端应用【团队项目】
  2. 微信Android客户端架构演进及其对开发流程的影响
  3. Android(安卓)Socket 发送与接收数据问题: 发送后的数据接收到总
  4. Android:学习AIDL,这一篇文章就够了(下)
  5. Android客户端实现图片轮播控件
  6. Android(安卓)签名
  7. Android与PHP交互,Android传递JSON数据,PHP接受并保存数据
  8. Android(安卓)面试必备 - http 与 https 协议
  9. Android中进程间通信(IPC)方式总结

随机推荐

  1. android studio 无法在线更新解决
  2. ViewPager实现Android向导页
  3. Android之SQLite开发(2)—SQLiteOpenHelper
  4. android屏幕的适配
  5. android的tabHost的使用
  6. Android : gen already exists but is no
  7. Android Volley完全解析(三),定制自己的Re
  8. android开发之Android ActionBar完全解析
  9. android 键对应的号码
  10. 深入Activity