关于SSL证书双向认证的原理过程

HTTPS是工作于SSL层之上的HTTP协议,SSL(安全套接层)工作于TCP层之上,向应用层提供了两个基本安全服务:认证和保密。SSL有三个子协议:握手协议,记录协议和警报协议。其中握手协议实现服务器与客户端的认证与密钥交换,记录协议进行数据加密并保证数据的完整性,警报协议则规定了错误类型和处理机制。

SSL握手协议包含4个阶段,下面简单介绍每个阶段。

双向认证 SSL 协议的具体过程




Java ssl双向认证示例 简述ssl的双向认证过程_socket认证


① 浏览器发送一个连接请求给安全服务器。

② 服务器将自己的证书,以及同证书相关的信息发送给客户浏览器。

③ 客户浏览器检查服务器送过来的证书是否是由自己信赖的 CA 中心所签发的。如果是,就继续执行协议;如果不是,客户浏览器就给客户一个警告消息:警告客户这个证书不是可以信赖的,询问客户是否需要继续。

④ 接着客户浏览器比较证书里的消息,例如域名和公钥,与服务器刚刚发送的相关消息是否一致,如果是一致的,客户浏览器认可这个服务器的合法身份。

⑤ 服务器要求客户发送客户自己的证书。收到后,服务器验证客户的证书,如果没有通过验证,拒绝连接;如果通过验证,服务器获得用户的公钥。

⑥ 客户浏览器告诉服务器自己所能够支持的通讯对称密码方案。

⑦ 服务器从客户发送过来的密码方案中,选择一种加密程度最高的密码方案,用客户的公钥加过密后通知浏览器。

⑧ 浏览器针对这个密码方案,选择一个通话密钥,接着用服务器的公钥加过密后发送给服务器。

⑨ 服务器接收到浏览器送过来的消息,用自己的私钥解密,获得通话密钥。

⑩ 服务器、浏览器接下来的通讯都是用对称密码方案,对称密钥是加过密的。

上面所述的是双向认证 SSL 协议的具体通讯过程,这种情况要求服务器和用户双方都有证书。

SSL双向认证的例子

首先创建服务器端私有密钥和公共密钥

1, keytool -genkey -alias clientkey -keystore kserver.ks
密码: serverpass
2, keytool -export -alias serverkey -keystore kserver.ks -file server.crt
3, keytool -import -alias serverkey -file server.crt -keystore tclient.ks
密码: clientpublicpass

下面创建客户器端私有密钥和公共密钥

1, keytool -genkey -alias clientkey -keystore kclient.ks
密码: clientpass
2, keytool -export -alias clientkey -keystore kclient.ks -file client.crt
3, keytool -import -alias clientkey -file client.crt -keystore tserver.ks
密码: serverpublicpass

把服务器端产生的公共密钥放到客户端, 同样把客户端创建的公共密钥放到服务器端.

下面是服务器端代码:

import java.io.BufferedInputStream; 
  
import java.io.BufferedOutputStream;   
import java.io.FileInputStream;   
import java.io.InputStream;   
import java.io.OutputStream;   
import java.net.Socket;   
import java.security.KeyStore;   
import javax.net.ssl.KeyManagerFactory;   
import javax.net.ssl.SSLContext;   
import javax.net.ssl.SSLServerSocket;   
import javax.net.ssl.TrustManagerFactory;   
 public class SSLTestServer {   
 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”), “serverpublicpass”.toCharArray());   
 kmf.init(ks, “serverpass”.toCharArray());   
tmf.init(tks);   
 ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);   
 SSLServerSocket serverSocket = (SSLServerSocket) ctx.getServerSocketFactory().createServerSocket(8443);   
serverSocket.setNeedClientAuth(true);   
 while (true) {   
try {   
Socket s = serverSocket.accept();   
InputStream input = s.getInputStream();   
OutputStream utput = 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);   
}   
}   
}   
}


下面是客户端代码:

import java.io.BufferedInputStream;   
import java.io.BufferedOutputStream;   
import java.io.FileInputStream;   
import java.io.InputStream;   
import java.io.OutputStream;   
import java.security.KeyStore;   
import javax.net.ssl.KeyManagerFactory;   
import javax.net.ssl.SSLContext;   
import javax.net.ssl.SSLSocket;   
import javax.net.ssl.TrustManagerFactory;   
 public class SSLTestClient {   
 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”), “clientpublicpass”.toCharArray());   
 kmf.init(ks, “clientpass”.toCharArray());   
tmf.init(tks);   
 ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);   
SSLSocket sslSocket = (SSLSocket) ctx.getSocketFactory().createSocket(“localhost”, 8443);   
InputStream input = sslSocket.getInputStream();   
OutputStream utput = 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();   
}   
 }

常见的HTTPS传输, 不需要进行客户端认证, 也就是单向认证. 这时也就不需要创建客户端的私钥和公钥. 服务器端也只要配置一下服务器端的私钥即可, 当客户端浏览器访问时会生成一个证书文件,类似于上面创建的crt文件. 如果需要程序访问,可以通过这个crt文件生成一个keystore. 然后是用这个keystore作为trust keystore即可.


Java ssl双向认证示例 简述ssl的双向认证过程_java_02

全球可信CA机构