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

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<String> 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

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值