一.概要
为了保护用户的信息安全、保护自己的商业利益,减少攻击面,我们需要保障通信信道的安全,采用开发方便的HTTPS是比较好的方式,比用私有协议要好,省时省力。但是如果HTTPS使用不当,就很难起到应有的保护效果。例如有人为了省事选择信任所有证书的方式绕过证书的认证,这样https的作用就完全没意义了。下面我就用Android比较流行的网络框架okhttp来做使用https,包括单向认证和双向认证二.OpenSSL生成证书
网上很多如何生成自签名证书的资料,这里就不详细说明,大概步骤如下: 1.生成私钥server.key 2.生成server.crt CA根证书(公钥) 3.用根证书给客户端签发证书server.cer三.单向认证
把服务器颁发的证书server.cer放到Android的目录assets /** * 添加证书 * */ public static SSLSocketFactory getSocketFactory() { try { CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); keyStore.load(null); InputStream is = mContext.getAssets().open("server.cer"); keyStore.setCertificateEntry("0", certificateFactory.generateCertificate(is)); if (is!=null){ is.close(); } SSLContext sslContext = SSLContext.getInstance("TLS"); TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); trustManagerFactory.init(keyStore); sslContext.init(null, trustManagerFactory.getTrustManagers(), new SecureRandom()); return sslContext.getSocketFactory(); } catch (Exception e) { e.printStackTrace(); } return null; } 然后 mOkHttpClient.setSslSocketFactory(getSocketFactory()); 就这样就实现了配置okhttp的https请求 另外如果后台服务器用是nginx搭建的可以先配置 ssl on; ssl_certificate ...server.crt; ssl_certificate_key ...server.key; 然后Android客户端直接使用server.crt来代替server.cer,利用nginx来认证https请求,省去了客户端到多台服务器的认证。四.双向认证
首先对于双向证书验证,也就是说,客户端也会有个“.key文件”,服务器那边会同时有个“.cer文件”与之对应。 我们已经生成了server.key和server.cer文件。 接下来按照生成证书的方式,再生成一对这样的文件,我们命名为:client.key,client.cer. 然后配置服务器 <Connector 其他属性与前面一致 clientAuth="true" truststoreFile="client.cer" /> 配置Android端: 还记得我们单向认证时 sslContext.init(null, trustManagerFactory.getTrustManagers(), new SecureRandom()); sslContext.init的第一个参数我们传入的是null,第一个参数的类型实际上是KeyManager[] km,主要就用于管理我们客户端的key。 另外我们生成的客户端client.key是jks格式的文件,java平台才能识别,Android直接用会报错:Java.io.IOException: Wrong version of key store。所有Android端要转成bks格式,可以用转换工具Portecle,网上很多资料可以参考,这里就不详说了。 然后就是我们Android端的代码实现: /** * 添加证书 * */ public static SSLSocketFactory getSocketFactory() { try { CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); keyStore.load(null); InputStream is = mContext.getAssets().open("server.cer"); keyStore.setCertificateEntry("0", certificateFactory.generateCertificate(is)); if (is!=null){ is.close(); } SSLContext sslContext = SSLContext.getInstance("TLS"); TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); trustManagerFactory.init(keyStore); //初始化keystore KeyStore clientKeyStore = KeyStore.getInstance("BKS"); clientKeyStore.load(mContext.getAssets().open("client.bks"), "123456".toCharArray()); KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); keyManagerFactory.init(clientKeyStore, "123456".toCharArray()); sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), new SecureRandom()); return sslContext.getSocketFactory(); } catch (Exception e) { e.printStackTrace(); } return null; } "123456"为证书密码总结
由于项目需要用到https,而之前也没有接触过,花了几天搜罗了很多资料,最后找到了实践可行的的方法,现在分享出来,希望对大家有帮助,如果有理解错误的地方,请指出,谢谢!