SSL双向认证原理和应用

RSA非对称加密原理 RSA算法的核心是欧拉定理,其安全性决定于下面的公式:

//其中d组成了私钥,如果d被计算出来那么算法被破解。n是一个随机的大整数 (1)ed≡1 (mod φ(n))。只有知道e和φ(n),才能算出d。 (2)φ(n)=(p-1)(q-1)。只有知道p和q,才能算出φ(n)。 (3)n=pq。只有将n因数分解,才能算出p和q。

可是,大整数的因数分解,是一件非常困难的事情。目前,除了暴力破解,还没有发现别的有效方法。
维基百科这样写道:
【SSL双向认证原理和应用】“对极大整数做因数分解的难度决定了RSA算法的可靠性。换言之,对一极大整数做因数分解愈困难,RSA算法愈可靠。
假如有人找到一种快速因数分解的算法,那么RSA的可靠性就会极度下降。但找到这样的算法的可能性是非常小的。今天只有短的RSA密钥才可能被暴力破解。到2008年为止,世界上还没有任何可靠的攻击RSA算法的方式。
只要密钥长度足够长,用RSA加密的信息实际上是不能被解破的。”
RSA算法常用于非对称加密,非对称加密流程如下:
(1)乙方生成两把密钥(公钥和私钥)。公钥是公开的,任何人都可以获得,私钥则是保密的。
(2)甲方获取乙方的公钥,然后用它对信息加密。
(3)乙方得到加密后的信息,用私钥解密。
SSL双向认证的原理 SSL双向认证原理和应用
文章图片
image.png
具体过程:
① 浏览器发送一个连接请求给安全服务器。
② 服务器将自己的证书,以及同证书相关的信息发送给客户浏览器。
③ 客户浏览器检查服务器送过来的证书是否是由自己信赖的 CA 中心所签发的。如果是,就继续执行协议;如果不是,客户浏览器就给客户一个警告消息:警告客户这个证书不是可以信赖的,询问客户是否需要继续。
④ 接着客户浏览器比较证书里的消息,例如域名和公钥,与服务器刚刚发送的相关消息是否一致,如果是一致的,客户浏览器认可这个服务器的合法身份。
⑤ 服务器要求客户发送客户自己的证书。收到后,服务器验证客户的证书,如果没有通过验证,拒绝连接;如果通过验证,服务器获得用户的公钥。
⑥ 客户浏览器告诉服务器自己所能够支持的通讯对称密码方案。
⑦ 服务器从客户发送过来的密码方案中,选择一种加密程度最高的密码方案,用客户的公钥加过密后通知浏览器。
⑧ 浏览器针对这个密码方案,选择一个通话密钥,接着用服务器的公钥加过密后发送给服务器。
⑨ 服务器接收到浏览器送过来的消息,用自己的私钥解密,获得通话密钥。
⑩ 服务器、浏览器接下来的通讯都是用对称密码方案,对称密钥是加过密的。
上面所述的是双向认证 SSL 协议的具体通讯过程,这种情况要求服务器和用户双方都有证书。
应用 那么如何去做一个SSL双向认证的通信测试呢?一般制作双向认证相关证书有两个工具opensslkeytool
二者比较:
  • openssl 适用范围广,使用的最多。
  • keytool 单独针对 java application
    需要安装 jre 之后才会有 keytool
    java只能用 Java Keystore,而它需要keytool 工具生成。
    keystore 可以把私钥和证书放一起,只用一个文件。
使用keytool 因为我使用java开发,所以就用keytool了,下面演示一个实例,注意这个实例中没有使用CA。
keytool命令说明:
  • keytool -genkey 生成密钥对
  • keytool -export 导出证书
  • keytool -import 导入证书或证书链
生成密钥证书文件
服务器端:
1,生成服务器端的密钥对 keytool -genkey -alias serverkey -keystore kserver.ks 密码: serverpass 2, 导出证书 keytool -export -alias serverkey -keystore kserver.ks -file server.crt 3, 把证书导入,生成的tclient.ks交给客户端,里面有公钥信息 keytool -import -alias serverkey -file server.crt -keystore tclient.ks 密码: serverpublicpass

同理生成客户端:
1, 生成客户端密钥对 keytool -genkey -alias clientkey -keystore kclient.ks 密码: clientpass 2, 导出证书 keytool -export -alias clientkey -keystore kclient.ks -file client.crt 3, 导入证书,生成的tserver.ks交给服务器,里面有客户端的公钥信息 keytool -import -alias clientkey -file client.crt -keystore tserver.ks 密码: clientpublicpass

关键代码示例:
//客户端: public static void main(String[] args) throws Exception { SSLContext ctx = SSLContext.getInstance("SSL"); KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509"); KeyStore ks = KeyStore.getInstance("JKS"); KeyStore tks = KeyStore.getInstance("JKS"); ks.load(new FileInputStream("cert/kclient.ks"), "clientpass".toCharArray()); tks.load(new FileInputStream("cert/tclient.ks"), "serverpublicpass".toCharArray()); kmf.init(ks, "clientpass".toCharArray()); tmf.init(tks); ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); SSLSocket sslSocket = (SSLSocket) ctx.getSocketFactory().createSocket("localhost", 8288); InputStream input = sslSocket.getInputStream(); OutputStream output = sslSocket.getOutputStream(); BufferedInputStream bis = new BufferedInputStream(input); BufferedOutputStream bos = new BufferedOutputStream(output); bos.write("Hello".getBytes()); bos.flush(); byte[] buffer = new byte[20]; int length = bis.read(buffer); System.out.println(new String(buffer, 0, length)); sslSocket.close(); }

//服务器端: public static void main(String[] args) throws Exception { SSLContext ctx = SSLContext.getInstance("SSL"); KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509"); KeyStore ks = KeyStore.getInstance("JKS"); KeyStore tks = KeyStore.getInstance("JKS"); ks.load(new FileInputStream("cert/kserver.ks"), "serverpass".toCharArray()); tks.load(new FileInputStream("cert/tserver.ks"), "clientpublicpass".toCharArray()); kmf.init(ks, "serverpass".toCharArray()); tmf.init(tks); ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); SSLServerSocket serverSocket = (SSLServerSocket) ctx.getServerSocketFactory().createServerSocket(8288); serverSocket.setNeedClientAuth(true); while (true) { try { Socket s = serverSocket.accept(); InputStream input = s.getInputStream(); OutputStream output = s.getOutputStream(); BufferedInputStream bis = new BufferedInputStream(input); BufferedOutputStream bos = new BufferedOutputStream(output); byte[] buffer = new byte[20]; int length = bis.read(buffer); System.out.println("Receive: " + new String(buffer, 0, length).toString()); bos.write("Hello".getBytes()); bos.flush(); s.close(); } catch (Exception e) { System.out.println(e); } } }

参考 用openssl 和 keytool 生成 SSL证书 RSA算法原理

    推荐阅读