网站首页 > 开源技术 正文
最近在忙App安全,而双向验证是第一步,本文主要记录我在双向验证过程中遇到的坑,以及解决。虽然网上类似的教程贼多,但大部分基于 Tomcat… 小打小闹尚可,毕竟反代还是 Nginx 好。通过本文,你将顺利获知如何实现 android 与 nginx 的双向验证。
我们以一段 Nginx 的配置开始吧:
- server {
- listen 443 ssl;
- server_name www.kpromise.top;
- ssl_certificate /etc/letsencrypt/live/kpromise.top/fullchain.pem;
- ssl_certificate_key /etc/letsencrypt/live/kpromise.top/privkey.pem;
- ... // 此处省略 N 多配置
这里,域名是本站域名,另外配置了证书和key,这是普通的 https 配置了。接下来,我们看另外一个配置:
- server {
- listen 443 ssl;
- server_name api.kpromise.top;
- ssl_certificate /root/ssl/server.crt;
- ssl_certificate_key /root/ssl/server.key;
- ssl_password_file /root/ssl/password_file;
- ssl_client_certificate /root/ssl/ca.crt;
- ssl_verify_client optional_no_ca;
- location / {
- root /root/ssl/api/;
- }
- }
这是一段中规中矩的服务端验证客户端的配置,主要变化是多了 ssl_client_certificate 这行。具体请看 http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_client_certificate
那么,这里的ca.crt 又是如何产生的呢,下面进入主题,生成证书:
首先,生成 ca.key 接下来签名证书的时候会用到:
1、openssl genrsa -des3 -out ca.key 4096
接着,生成 ca.crt 文件,crt 文件是客户端认证的证书文件,同样的,在你签名证书的时候会用到
2、openssl req -new -x509 -days 365 -key ca.key -out ca.crt
3、生成证书:
- openssl genrsa -des3 -out server.key 1024
- openssl req -new -key server.key -out server.csr
会提示你输入 Common Name 一般是你的域名哦
4、用 ca.key 和 ca.crt 签名证书:
- openssl x509 -req -days 365 -in server.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out server.crt
5、重复 3-4 两步,但是 -out 改为 client.x 来生成并签署另一份证书,比如:
- openssl genrsa -des3 -out client.key 1024
- openssl req -new -key client.key -out client.csr
- openssl x509 -req -days 365 -in client.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out client.crt
6、修改 Nginx 配置为 我刚开始提供的第二份配置那样,即:
- server {
- listen 443 ssl;
- server_name api.kpromise.top;
- ssl_certificate /root/ssl/server.crt;
- ssl_certificate_key /root/ssl/server.key;
- ssl_password_file /root/ssl/password_file;
- ssl_client_certificate /root/ssl/ca.crt;
- ssl_verify_client on;
- location / {
- root /root/ssl/api/;
- }
- }
然后 把相关文件移入配置所列的目录,并执行 nginx -s reload,此时,服务端就会校验客户端证书了。这里的 ssl_password_file 对应的是一个普通文件,里面是 创建证书时设置的密码哦。
接下来,我们看下 android 端的修改。首先查看 android 支持 的 keystore 类型: https://developer.android.com/reference/java/security/KeyStore#summary 大致如下:
AndroidCAStore 14+
AndroidKeyStore 18+
BCPKCS12 1-8
BKS 1+
BouncyCastle 1+
PKCS12 1+
PKCS12-DEF 1-8
可以看得出,AndroidCAStore、PKCS12、BouncyCastle、BKS 都是不错的选择,但是我看网上资料,基本都说 转为 android 支持的类型 BKS 这又是什么鬼?明明有 PKCS12 可以选择啊。我们接下来导出 client.p12 以及 server.p12 文件,命令如下:
- openssl pkcs12 -export -in client.crt -inkey client.key -out client.p12 -name "client"
- openssl pkcs12 -export -in server.crt -inkey server.key -out server.p12 -name "client"
然后把 client.p12 和 server.p12 都给android 客户端,android 客户端将他们放到 /src/main/assets/ 下面。最后,我们修改 okhttp 吧:
- class SSLHelper {
- private var clientPw: String? = null
- private var clientPKCSFileName: String = "client.p12"
- private var serverPKCSFileName: String = "server.p12"
- private var serverPw: String? = null
- private val protocolType = "TLS"
- private val keyStoreType: String = "PKCS12"
- private val certificateFormat: String = "X509"
- fun initClient(clientPKCSFileName: String, clientPw: String): SSLHelper {
- this.clientPKCSFileName = clientPKCSFileName
- this.clientPw = clientPw
- return this
- }
- fun initServer(serverPKCSFileName: String, serverPw: String): SSLHelper {
- this.serverPKCSFileName = serverPKCSFileName
- this.serverPw = serverPw
- return this
- }
- fun getSSLCertification(context: Context): SSLSocketFactory? {
- if (clientPw == null) throw RuntimeException("please call initClient first...")
- if (serverPw == null) throw RuntimeException("please call initServer first...")
- var sslSocketFactory: SSLSocketFactory? = null
- try {
- val clientKeyStore = KeyStore.getInstance(keyStoreType)
- val serverKeyStore = KeyStore.getInstance(keyStoreType)
- val clientPrivateKeyInputStream = context.assets.open(clientPKCSFileName)
- val serverPublicKeyInputStream = context.assets.open(serverPKCSFileName)
- clientKeyStore.load(clientPrivateKeyInputStream, clientPw?.toCharArray())
- serverKeyStore.load(serverPublicKeyInputStream, serverPw?.toCharArray())
- clientPrivateKeyInputStream.close()
- serverPublicKeyInputStream.close()
- val sslContext = SSLContext.getInstance(protocolType)
- val trustManagerFactory = TrustManagerFactory.getInstance(certificateFormat)
- val keyManagerFactory = KeyManagerFactory.getInstance(certificateFormat)
- trustManagerFactory.init(serverKeyStore)
- keyManagerFactory.init(clientKeyStore, clientPw?.toCharArray())
- sslContext.init(keyManagerFactory.keyManagers,
- trustManagerFactory.trustManagers, null)
- sslSocketFactory = sslContext.socketFactory
- } catch (e: Exception) {
- e.printStackTrace()
- }
- return sslSocketFactory
- }
- }
- fun getBuilder(showLog: Boolean): OkHttpClient.Builder {
- val builder = OkHttpClient.Builder()
- val sslSocketFactory = this.sslSocketFactory
- if (sslSocketFactory != null) {
- builder.sslSocketFactory(sslSocketFactory)
- }
- builder.hostnameVerifier { _, _ ->
- true
- }
- ... // 此处省略 N 多 代码
完美了,当然,hostnameVerifier 你也可以指定自己的域名啊。
遇到的天坑:
在生成 ca server 以及 client 时 会 要求输入 CN 即:Common Name (e.g. server FQDN or YOUR name) 这行,请注意这三处虽然都是域名,但千万别设一样,否则会出现 400 The SSL certificate error
猜你喜欢
- 2024-09-28 我放弃了okhttp、httpClient,选了这个神仙工具
- 2024-09-28 很懵圈,记录一次MinIO 使用okhttp版本的问题?
- 2024-09-28 android学习,OkHttp,拦截器(okhttp自定义拦截器放在哪一层)
- 2024-09-28 Okhttp上传图片失败,居然是服务端的锅?(一)
- 2024-09-28 工作日报 2021.10.20 OkHttp3错误异常:unexpected end of stream
- 2024-09-28 深入浅出 OkHttp 源码解析及应用实践
- 2024-09-28 用OkHttp实现WebSocket长连接(利用输入实现预期结果的相互关联或者相作用的一组活动 描述的是)
- 2024-09-28 干货-okHttp的优点-收藏了(水瓶座女的缺点和优点)
- 2024-09-28 软件更新丨OkHttp 4.0.0 RC 3 发布,从 Java 切换到 Kotlin
- 2024-09-28 开发者必备的Android开发资源之OkHttp
你 发表评论:
欢迎- 03-19基于layui+springcloud的企业级微服务框架
- 03-19开箱即用的前端开发模板,扩展Layui原生UI样式,集成第三方组件
- 03-19SpringMVC +Spring +Mybatis + Layui通用后台管理系统OneManageV2.1
- 03-19SpringBoot+LayUI后台管理系统开发脚手架
- 03-19layui下拉菜单form.render局部刷新方法亲测有效
- 03-19Layui 遇到的坑(记录贴)(layui chm)
- 03-19基于ASP.NET MVC + Layui的通用后台开发框架
- 03-19LayUi自定义模块的定义与使用(layui自定义表格)
- 最近发表
-
- 基于layui+springcloud的企业级微服务框架
- 开箱即用的前端开发模板,扩展Layui原生UI样式,集成第三方组件
- SpringMVC +Spring +Mybatis + Layui通用后台管理系统OneManageV2.1
- SpringBoot+LayUI后台管理系统开发脚手架
- layui下拉菜单form.render局部刷新方法亲测有效
- Layui 遇到的坑(记录贴)(layui chm)
- 基于ASP.NET MVC + Layui的通用后台开发框架
- LayUi自定义模块的定义与使用(layui自定义表格)
- Layui 2.9.11正式发布(layui2.6)
- Layui 2.9.13正式发布(layui2.6)
- 标签列表
-
- jdk (81)
- putty (66)
- rufus (78)
- 内网穿透 (89)
- okhttp (70)
- powertoys (74)
- windowsterminal (81)
- netcat (65)
- ghostscript (65)
- veracrypt (65)
- asp.netcore (70)
- wrk (67)
- aspose.words (80)
- itk (80)
- ajaxfileupload.js (66)
- sqlhelper (67)
- express.js (67)
- phpmailer (67)
- xjar (70)
- redisclient (78)
- wakeonlan (66)
- tinygo (85)
- startbbs (72)
- webftp (82)
- vsvim (79)
本文暂时没有评论,来添加一个吧(●'◡'●)