为什么需要双向认证

Https保证的是信道的安全,即客户端和服务端通信报文的安全。但是无法保证中间人攻击,所以双向认证解决的问题就是防止中间人攻击。

中间人攻击(Man-in-the-MiddleAttack)简称(MITM),是一种“间接”的入侵攻击,这种攻击模式是通过各种技术手段将受入侵者控制的一台计算机虚拟放置在网络连接中的两台通信计算机之间,这台计算机就称为“中间人”。若没有开启双向认证,中间人可以拦截客户端发送的请求,然后篡改信息再发送到服务端;中间人也可以拦截服务端返回的信息,再发送到客户端。所以使用Https的单向认证或双向认证能够有效防止中间人攻击。

注:无论Ca证书还是自签证书都需要双向认证。

双向认证原理

1、服务端认证客户端原理

客户端有自己的bks证书auth_client.bks,并将导出的auth_client_pub.cer证书导入到服务端证书auth_server.keystore中,这样服务端就将客户端证书添加到信任列表中,从而能够让带有该auth_client_pub.cer证书信息的客户端访问服务。

2、客户端认证服务端原理

服务端有自己的证书(ca颁发的或者是自己创建的)auth_server.keystore,并导出auth_server_pub.cer证书,将该证书导入到客户端证书

auth_truststore.jks中,注意:这里不是导入到auth_client.jks中,而是导入生成另一个证书auth_truststore.jks,最后再将jks证书转化成bks证书。

实现过程

一、服务端证书

创建服务端证书

keytool -genkeypair -alias auth_server -keyalg RSA -validity 36500 -keypass auth_server -storepass auth_server -keystore /Users/renzhongrui/android/certs/auth_server.keystore

导出服务端证书公钥

keytool -export -alias auth_server -file /Users/renzhongrui/android/certs/auth_server_pub.cer -keystore /Users/renzhongrui/android/certs/auth_server.keystore -storepass auth_server

二、客户端证书

创建客户端证书(andoird不能用keystore格式的密钥库,所以先生成jks格式,再用Portecle工具转成bks格式)

keytool -genkeypair -alias auth_client -keyalg RSA -validity 36500 -keypass auth_client -storepass auth_client -keystore /Users/renzhongrui/android/certs/auth_client.jks

导出客户端证书公钥

keytool -export -alias auth_client -file /Users/renzhongrui/android/certs/auth_client_pub.cer -keystore /Users/renzhongrui/android/certs/auth_client.jks -storepass auth_client

三、证书交换

将客户端证书导入服务端keystore中,再将服务端证书导入客户端auth_truststore中, 一个keystore可以导入多个证书,生成证书列表。

将客户端公钥导入到服务端keystore证书中,使得服务端能够信任客户端。

keytool -import -v -alias auth_client -file /Users/renzhongrui/android/certs/auth_client_pub.cer -keystore /Users/renzhongrui/android/certs/auth_server.keystore -storepass auth_server

生成客户端信任证书库auth_truststore.jks,即将服务端公钥导入到客户端jks证书中,使得客户端能够信任服务端。

keytool -import -v -alias auth_server -file /Users/renzhongrui/android/certs/auth_server_pub.cer -keystore /Users/renzhongrui/android/certs/auth_truststore.jks -storepass auth_truststore

最后验证一下,查看证书库中的所有证书

keytool -list -keystore /Users/renzhongrui/android/certs/auth_server.keystore -storepass auth_server
keytool -list -keystore /Users/renzhongrui/android/certs/auth_truststore.jks -storepass auth_truststore

四、证书转换

下载portecle.jar,解压后运行jar包:

java -jar portecle.jar

1、点击File菜单选择Open Keystore File,选择创建好的auth_client.jksauth_truststore.jks证书,输入密码。

2、选中导入的证书,点击Tools菜单,选择Change Keystore Type,再选择BKS类型,再次输入密码,确认之后,会显示成功。

3、最后点击File菜单,选择Save Keystore File As,将证书保存的指定路径。

五、配置服务

证书准备好之后,就可以进行集成测试了,服务使用Spring Boot创建或者使用Nginx代理。

使用Spring Boot服务

1、添加配置

server:port: 443server:tomcat:uri-encoding: UTF-8# 开启https,配置跟证书对应ssl:key-store: classpath:auth_server.keystorekey-store-type: JKSkey-store-password: auth_serverkey-password: auth_serverkey-alias: auth_serverenabled: true#是否需要进行认证client-auth: needprotocol: TLS # 默认trust-store: classpath:auth_server.keystoretrust-store-password: auth_servertrust-store-type: JKS

2、添加代码,这里配置80端口重定向到443,也可以改成别的端口。

public class PackApplication implements WebMvcConfigurer {public static void main(String[] args) {SpringApplication.run(PackApplication.class, args);}@Beanpublic Connector connector(){Connector connector=new Connector("org.apache.coyote.http11.Http11NioProtocol");connector.setScheme("http");connector.setPort(80);connector.setSecure(false);connector.setRedirectPort(443);return connector;}@Beanpublic TomcatServletWebServerFactory tomcatServletWebServerFactory(Connector connector){TomcatServletWebServerFactory tomcat=new TomcatServletWebServerFactory(){@Overrideprotected void postProcessContext(Context context) {SecurityConstraint securityConstraint=new SecurityConstraint();securityConstraint.setUserConstraint("CONFIDENTIAL");SecurityCollection collection=new SecurityCollection();collection.addPattern("/*");securityConstraint.addCollection(collection);context.addConstraint(securityConstraint);}};tomcat.addAdditionalTomcatConnectors(connector);return tomcat;}}

使用Nginx服务配置

Nginx配置与Spring Boot服务配置略有不同。

server {listen       443;server_name  192.168.200.101; # 代理服务IPssl on; # 开启Httpsssl_certificate      /usr/local/nginx/conf/https/auth_server.cer; # auth_server.keystore导出的cer证书ssl_certificate_key  /usr/local/nginx/conf/https/auth_server.key; # auth_server.keystore导出的私钥ssl_client_certificate /usr/local/nginx/conf/https/auth_client.cer; # auth_client.keystore导出的cerssl_session_cache    shared:SSL:1m;ssl_session_timeout  5m;ssl_verify_client optional; # 配置校验客户端策略,设置成optional时候可以开启白名单接口ssl_protocols TLSv1.1 TLSv1.2;ssl_ciphers  HIGH:!aNULL:!MD5;ssl_prefer_server_ciphers  off;location / { # 需要双向验证https的接口if ($ssl_client_verify != SUCCESS) {return 401;}proxy_pass http://192.168.200.101:8008;proxy_connect_timeout 600;proxy_read_timeout 600;}location /aarm/downloadUpdateFile { # 获取证书版本和下载证书接口,不需要验证Httpsproxy_pass http://192.169.200.101:8008;proxy_connect_timeout 600;proxy_read_timeout 600;}}

六、配置客户端

在客户端app中使用OkHttp来进行网络访问,所以需要配置OkHttp来进行证书认证。

1、将上面创建的auth_client.bks和auth_truststore.bks证书放到assets目录下

2、初始化OkHttp

OkHttpClient okHttpClient = new OkHttpClient.Builder().connectTimeout(10, TimeUnit.SECONDS).readTimeout(10, TimeUnit.SECONDS).sslSocketFactory(Https.getSSLCertifcation(getApplicationContext()))//获取SSLSocketFactory.hostnameVerifier(new UnSafeHostnameVerifier())//添加hostName验证器.build();

重点需要看一下Https类的实现:

public class Https {private final static String CLIENT_PRI_KEY = "auth_client.bks";private final static String TRUSTSTORE_PUB_KEY = "auth_truststore.bks";private final static String CLIENT_BKS_PASSWORD = "auth_client";private final static String TRUSTSTORE_BKS_PASSWORD = "auth_truststore";private final static String KEYSTORE_TYPE = "BKS";private final static String PROTOCOL_TYPE = "TLS";private final static String CERTIFICATE_FORMAT = "X509";public static SSLSocketFactory getSSLCertifcation(Context context) {SSLSocketFactory sslSocketFactory = null;try {// 服务器端需要验证的客户端证书,其实就是客户端的keystoreKeyStore 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();//初始化SSLContextSSLContext sslContext = SSLContext.getInstance(PROTOCOL_TYPE);TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(CERTIFICATE_FORMAT);KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(CERTIFICATE_FORMAT);trustManagerFactory.init(trustStore);keyManagerFactory.init(keyStore, CLIENT_BKS_PASSWORD.toCharArray());sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null);sslSocketFactory = sslContext.getSocketFactory();} catch (Exception e) {e.printStackTrace();}return sslSocketFactory;}
}

还有一个UnSafeHostnameVerifier

private class UnSafeHostnameVerifier implements HostnameVerifier {@Overridepublic boolean verify(String hostname, SSLSession session) {return true;}
}

此时再进行网络请求,就能够访问到带有双向认证的服务端接口了。当然一般网上的博客到这就结束了,但是这样真的就完事了吗,其实真正的设计才刚开始,如果只是了解原理,读到这里就可以了,下面才是真实应用场景。

真实场景实现

原理还是那个原理,就看怎么合理的使用了。在真实开发环境中,需要解决几个问题:

  • auth_client.bks和auth_truststore.bks是需要动态下发的
  • 不是所有的接口都需要进行双向认证

动态下发auth_client.bks和auth_truststore.bks

1、auth_client.bksauth_truststore.bks的制作需要在本地工具完成,然后通过管理端上传到服务器,并且改变证书的版本号;

2、客户端需要访问证书版本,来判断是否需要更新证书,如果需要更新则下载证书。

这里会引出两个问题:

1、请求版本号的接口和下载证书的接口不能进行双向认证,否则无法下发证书。

2、不进行双向认证的接口是不安全的,所以,请求版本号的接口的返回值是需要加密的;

针对第一个问题处理方式:

服务端需要配置白名单,将请求版本号的接口和下载证书的接口过滤掉;

客户端OkHttp首次初始化不能进行双向认证,等下载完证书之后,需要再次进行OkHttp初始化;

针对第二个问题处理方式:

需要本地工具创建RSA公私钥对,用于请求版本号接口的加解密;

服务端使用私钥对报文加密,客户端保存公钥,并使用公钥对报文解密。

客户端使用公钥解密后的报文格式:

{"version":1,"authType":2,"clientBksPath":"https://localhost/downloadUpdateFile?fileName=auth_client.bks","trustBksPath":"https://localhost/downloadUpdateFile?fileName=auth_truststore.bks","authKey":"auth_client"
}
  • version: 表示每一次更换证书的版本;
  • authType:0 表示不开启认证,1 表示开启单向认证,2 表示开启双向认证
  • clientBksPath:auth_client.bks下载路径
  • trustBksPath:auth_truststore.bks下载路径
  • authKey:auth_client.bks证书密码

客户端每次启动都要获取服务端证书版本,并将证书信息存储到本地文件或者数据库中,通过对比服务端证书版本和数据库中版本来判断是否需要证书更新。

注:这样设计的好处是当证书过期时,能够动态下发证书,但会引出一个问题,客户端要安全的存储公钥信息,一般做法是将公钥存储到so文件里,再配合应用加固手段进行保护,不过这个就不是通信安全的问题了,而是apk安全的问题。

其他证书操作

1、查看keystore证书公钥

keytool -list -rfc --keystore release.keystore | openssl x509 -inform pem -pubkey

2、查看keystore证书私钥

先转成pfx格式

keytool -v -importkeystore -srckeystore release.keystore -srcstoretype jks -srcstorepass 123456 -destkeystore keystore/release.pfx -deststoretype pkcs12 -deststorepass 123456 -destkeypass 123456

再查看证书私钥

openssl pkcs12 -in release.pfx -nocerts -nodes

Android应用实现Https双向认证相关推荐

  1. RN https 双向认证

    一  概述 Rreact Native  https双向认证  Android端修改,有两种方案: 一是修改 facebook桥接Android的网络请求 二是自己新建桥接 android和rn,修改 ...

  2. android webview单向认证,android 让webview支持自签名证书https 双向认证(SSL)

    最近完成一个项目,安全级别比较高.所以涉及到https双向认证,在网上找了很多资料都没有完美的解决方案.最后参考了org.sandrob.sslexample的实现方式,结合实际情况才完成该技术难题, ...

  3. (转载)Android 让WebView完美支持https双向认证(SSL)

    (转载)https://blog.csdn.net/kpioneer123/article/details/51491739 这是@happyzhang0502   关于webview https的建 ...

  4. Https双向认证Android客户端配置

    Https双向认证啊  做了两遍,第一遍懵懂状态处于 好不容易做好了,换服务器,一下子懵了,使出浑身解数又找了一遍,这下终于好了  快哭啦,必须滴要记录一下,以免以后遇到继续懵,这里用retrofit ...

  5. HTTPS双向认证配置

    最近看了下HTTPS相关的,概念性的东西各位就去查查资料吧.主要找到两篇比较靠谱的文章,收藏下. xiooa面复制自https://my.oschina.net/jjface/blog/339144 ...

  6. Apache httpd设置HTTPS双向认证

    一.环境 httpd: 2.4.4  openssl:1.0.1  os:ubuntu 12.04 LTS 二.场景 我准备在httpd上配置一个HTTPS双向认证,既向客户端表明自己的身份,也只允许 ...

  7. TurboMail邮件系统支持HTTPS双向认证

    2019独角兽企业重金招聘Python工程师标准>>> HTTP单向认证已经被普遍应用,而对企业邮箱安全保密要求更加严格的企事业单位,例如国家保密局等单位,为了达到闭环的安全加密要求 ...

  8. httpd设置HTTPS双向认证

    去年用tomcat.jboss配置过HTTPS双向认证,那时候主要用的是JDK自带的keytool工具.这次是用httpd + openssl,区别比较大 在网上搜索了很多文章,发现全面介绍的不多,或 ...

  9. 用pfx证书java双向认证_把CA证书生成的crt的证书和pem的私钥转换成java能够使用的keystore和pcks12的证书,实现https双向认证...

    最近在做一个https双向认证的工作,领导先让我实现,我之前写了一篇文章,把tomcat的生成证书和配置的实现写了出来. 现在领导给了我服务器的CA证书的客户端证书和私钥,服务端信任证书,分别是crt ...

最新文章

  1. python中字符串前面加一个u或者r的区别
  2. “网络爸爸”的密码破解
  3. openWRT自学---针对backfire版本的主要目录和文件的作用的分析整理
  4. js 和 query 获取页面和滚动条的高度
  5. 定制Ocelot来满足需求
  6. Maven配置文件POM属性最全详解
  7. 购物商城微信小程序演示
  8. python平方和计算技巧,python平方和计算技巧 Python算法练习题:四平方和
  9. html页面导出word文档
  10. EBS INV:单位
  11. GBase8d产品admin目录下的文件及文件夹说明
  12. 苹果本的vscode菜单栏不见了,怎么回事
  13. 如何直观地理解矩阵的秩?
  14. 多巴胺所表达的prediction error信号
  15. Android星座运势案例
  16. 数据库SQLserver(课本)
  17. 日本下一个超大规模数据中心破土动工,位于京阪奈科学城
  18. java方法注释都英文_JDK源码中的英文注释翻译(Class)
  19. 王者荣耀测试自己本命英雄软件,王者荣耀中谁是你的本命英雄测试地址 趣推测试王者荣耀中谁是你的本命英雄...
  20. 历届试题 核桃的数量(3个数的最小公倍数),翻硬币(贪心),买不到的数目(在范围内暴力,找范围,最小公倍数是上界,最小的数是下界),兰顿蚂蚁(dfs,模拟)

热门文章

  1. 电子商务mysql设计_设计电子商务数据库 – MySQL
  2. 自制 Windows 小工具 ———— 智能截屏工具
  3. KSZ9897 switch以及官方驱动KSZ9897
  4. Matplotlib 绘制股票走势图
  5. Eigen 求解两个向量的夹角
  6. 办理恢复专利权请求书花钱吗
  7. numerov算法matlab,Numerov算法求解一维薛定谔方程研究.doc
  8. 华为E1750上网卡往嵌入式板子上移植详解
  9. 2018年的小目标和17年的总结
  10. 关于淘宝双十二彩票营销一分钟扫描二十万次的思考