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。

可是,大整数的因数分解,是一件非常困难的事情。目前,除了暴力破解,还没有发现别的有效方法。
维基百科这样写道:

“对极大整数做因数分解的难度决定了RSA算法的可靠性。换言之,对一极大整数做因数分解愈困难,RSA算法愈可靠。
假如有人找到一种快速因数分解的算法,那么RSA的可靠性就会极度下降。但找到这样的算法的可能性是非常小的。今天只有短的RSA密钥才可能被暴力破解。到2008年为止,世界上还没有任何可靠的攻击RSA算法的方式。
只要密钥长度足够长,用RSA加密的信息实际上是不能被解破的。”

RSA算法常用于非对称加密,非对称加密流程如下:
(1)乙方生成两把密钥(公钥和私钥)。公钥是公开的,任何人都可以获得,私钥则是保密的。
(2)甲方获取乙方的公钥,然后用它对信息加密。
(3)乙方得到加密后的信息,用私钥解密。

SSL双向认证的原理

SSL双向认证原理和应用_第1张图片
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算法原理

你可能感兴趣的:(SSL双向认证原理和应用)