Android与Windows Socket通信,TLS双向认证

Android设备作为客户端,与PC服务端进行双向认证、基于TLS 1.2协议socket通信。

  • 证书制作
  • CA签发
  • JKS转换BKS
  • 加密套件修改
  • 双向认证demo

证书制作

由于是双向认证,需要制作客户端和服务器证书,这里用到的是JAVA自带的keytool工具(请自行安装JDK环境)。
话不多说,上证书制作脚本:
1)生成服务器端公私钥对,存入密钥库,命令keytool -genkeypair:

keytool -genkeypair -alias serverkey -keyalg ec -validity 365 -keystore serverkey.keystore.jks   -storepass 1234567 -dname "cn=AA.COM, ou=AA, o=AA Cor, c=CN, l=SZ" -deststoretype pkcs12
参数 含义
-alias 设置别名,可自定义
-keyalg 设置密钥算法,DSA、RSA、ECDSA等
-valiity 有效期
-keystore 密钥库名称
-storepass 密钥库密码
-dname 指定证书发行方信息
-deststoretype 指定keystore类型,默认pkcs12

2)同理,生成客户端公私钥对:

keytool -genkeypair -alias clientkey -keyalg ec -validity 365 -keystore clientkey.keystore.jks   -storepass 7654321 -dname "cn=BB.COM, ou=BB, o=BB Cor, c=CN, l=SZ" -deststoretype pkcs12

这样就生成了自签名的服务器证书和客户端证书。

CA签发

虽然我们将服务器与客户端密钥库分别提交给对方加入到各自受信任密钥库,在通信的时候互相校验,可以做到双向认证通过,但通常情况下不会这样做,一来直接将证书丢给对方很不安全,密钥库被盗用即有身份被冒用风险;二来客户端和服务端的密钥库一旦签发给对方,其存储的证书内容被固定住,无法扩展。

所以我们引入第三方受信CA,客户端和服务端分别只要信任该CA,则该CA签发的证书都是可信任的。

由于市面上流通的知名CA都是收费签发,这里使用自签CA来模拟这个过程。

1)首先生成CA密钥库:

keytool -genkeypair -alias rootca -keyalg ec -validity 3650 -keystore ca.keystore.jks -storepass   22222222 -dname "cn=CA.COM, ou=CA, o=CA Cor, c=CN, l=SZ" -deststoretype pkcs12

2)导出CA证书,用于后续导入到密钥库:
···
keytool -exportcert -alias rootca -file ca.cer -keystore ca.keystore.jks -storepass 22222222 -deststoretype pkcs12
···
3)生成服务器端证书请求serverkey.csr,即是待签发的证书文件:

keytool -certreq -alias serverkey -keystore serverkey.keystore.jks -storepass 1234567 -file   serverkey.csr -deststoretype pkcs12

4)使用 CA密钥库ca.keystore.jks对serverkey.csr进行签发:

keytool -gencert -alias rootca -infile serverkey.csr -outfile serverkey.cer -validity 365   -keystore ca.keystore.jks -storepass 22222222 -deststoretype pkcs12

使用keytool -printcert命令可以看到签发者变成了CA(CA.COM):

5)导入CA证书到服务器密钥库:

keytool -import -trustcacerts -alias rootca -file ca.cer -keystore serverkey.keystore.jks   -storepass 1234567 -deststoretype pkcs12

导入时会提示是否信任,输入y,表示信任

6)导入签发证书到服务器密钥库:

keytool -import -trustcacerts -alias serverkey -file serverkey.cer -keystore   serverkey.keystore.jks -storepass 1234567 -deststoretype pkcs12

使用keytool -list -v命令查看密钥库可以看到有两个证书,一个是签发后的密钥,一个是CA,其中密钥签发者变成了CA(CA.COM):

7)同理生成客户端证书请求并使用CA签发:

keytool -certreq -alias clientkey -keystore clientkey.keystore.jks -storepass 7654321 -file   clientkey.csr -deststoretype pkcs12
keytool -gencert -alias rootca -infile clientkey.csr -outfile clientkey.cer -validity 365   -keystore ca.keystore.jks -storepass 22222222 -deststoretype pkcs12
keytool -import -trustcacerts -alias rootca -file ca.cer -keystore clientkey.keystore.jks   -storepass 7654321 -deststoretype pkcs12
keytool -import -trustcacerts -alias clientkey -file clientkey.cer -keystore   clientkey.keystore.jks -storepass 7654321 -deststoretype pkcs12

至此,双向认证的JKS证书已经制作完成。

密钥库转换

我们这里是Android客户端,不能直接使用JKS密钥,需要用BKS格式密钥文件。

这里借助portecle https://sourceforge.net/projects/portecle/files/latest/download 工具进行转换:
下载解压缩后,进入相应目录,执行

java -jar portecle.jar

即可打开软件主界面:

转换服务器/客户端密钥库:

转换CA密钥库:
首先将CA证书导入到一个新的JKS格式CA密钥库cacer.keystore.jks:

keytool -importcert -alias rootca -file ca.cer -keystore cacer.keystore.jks   -storepass 33333333 

然后类似服务器BKS密钥库转换方式进行转换得到ca.keystore.bks【注意!!此时CA密钥库密码变为新密码 33333333

这样我们拿到了三个BKS密钥库:

密钥库 用途
ca.keystore.bks 服务器和客户端各持一份,用于验签对方证书
serverkey.keystore.bks 服务器持有,双向认证时发给客户端
clientkey.keystore.bks 客户端持有,双向认证时发给服务器

加密套件修改

加密套件(Ciphe Suites)是指在ssl/tls通信中,服务器和客户端所使用的加密算法的组合。在ssl握手初期,客户端将自身支持的加密套件列表发送给服务器;在握手阶段,服务器根据自己的配置从中尽可能的选出一个套件,作为之后所要使用的加密方式。这些算法包括:认证算法、密钥交换算法、对称算法和摘要算法等。

随着技术的发展,某些加密套件由于算法安全级别不够,不需要出现在双向认证的候选加密套件列表中,只需在openssl源码SSL_CIPHER kCiphers[]列表进行移除即可,比如下面去掉Cipher 04、Cipher 05、Cipher 0A:

/* kCiphers is an array of all supported ciphers, sorted by id. */static const SSL_CIPHER kCiphers[] = {    /* The RSA ciphers */    /* Cipher 02 */    {     SSL3_TXT_RSA_NULL_SHA,     SSL3_CK_RSA_NULL_SHA,     SSL_kRSA,     SSL_aRSA,     SSL_eNULL,     SSL_SHA1,     SSL_HANDSHAKE_MAC_DEFAULT,    },/*    /* Cipher 04 *    {     SSL3_TXT_RSA_RC4_128_MD5,     SSL3_CK_RSA_RC4_128_MD5,     SSL_kRSA,     SSL_aRSA,     SSL_RC4,     SSL_MD5,     SSL_HANDSHAKE_MAC_DEFAULT,    },    /* Cipher 05 *    {     SSL3_TXT_RSA_RC4_128_SHA,     SSL3_CK_RSA_RC4_128_SHA,     SSL_kRSA,     SSL_aRSA,     SSL_RC4,     SSL_SHA1,     SSL_HANDSHAKE_MAC_DEFAULT,    },    /* Cipher 0A     {     SSL3_TXT_RSA_DES_192_CBC3_SHA,     SSL3_CK_RSA_DES_192_CBC3_SHA,     SSL_kRSA,     SSL_aRSA,     SSL_3DES,     SSL_SHA1,     SSL_HANDSHAKE_MAC_DEFAULT,    },*/   ...};

Android应用层,若是使用SSLSocket,则只需使用setEnabledCipherSuites方法设置想要的加密套件:

        List allowedCiphers = Arrays.asList(                                                  // TLS 1.2                                                  "TLS_RSA_WITH_AES_256_GCM_SHA384",                                                  "TLS_RSA_WITH_AES_128_GCM_SHA256",                                                  "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",                                                  "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",                                                  "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",                                                  "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",                                                  "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",                                                  // maximum interoperability                                                  //"SSL_RSA_WITH_3DES_EDE_CBC_SHA", // FAQ 69                                                  "TLS_RSA_WITH_AES_128_CBC_SHA",                                                  // additionally                                                  "TLS_RSA_WITH_AES_256_CBC_SHA",                                                  //"TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA",                                                  "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",                                                  //"TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",                                                  "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA");        String[] enabledCiphers=allowedCiphers.toArray(new String[allowedCiphers.size()]);        Client_sslSocket = (SSLSocket) sslContext.getSocketFactory().createSocket(SERVER_IP,SERVER_PORT);        String[] supportSuite = Client_sslSocket.getSupportedCipherSuites();        Client_sslSocket.setEnabledCipherSuites(enabledCiphers);

完整demo代码见 https://download.csdn.net/download/peterhu_112/10606499

【参考】 https://docs.oracle.com/javase/8/docs/technotes/tools/windows/keytool.html

更多相关文章

  1. 为你的APK进行数字签名
  2. Android程序开发通过HttpURLConnection上传文件到服务器
  3. Android数字证书具体应用机制
  4. Android(安卓)Service解析
  5. Android(安卓)TCP socket通信
  6. 沃邮箱Android客户端产品体验报告
  7. android网络通信之-Http(Android操作HTTP实现与服务器通信)
  8. Android(安卓)IPC机制(三):浅谈Binder的使用
  9. Android(安卓)百度地图 SDK v3.0.0 (一)

随机推荐

  1. 阿里规范不建议多表join,可这SQL要怎么写
  2. 为Android系统编译tun.ko模块
  3. Linux网络管理
  4. Java异常有多慢?
  5. 原创 | CRUD更要知道的Spring事务传播机
  6. SpringBoot自适应异常处理
  7. XUECIYUYAN
  8. Linux进程管理
  9. IDEA常用设置及推荐插件
  10. 又踩到Dubbo的坑,但是这次我笑不出来