SSL连接分为双向认证和单向认证。双向认证意味着服务器和客户端需要分别验证对方的身份。单向认证只需要客户端来验证服务器的身份。
SSL双向认证流程如下图所示:
从以上流程可以看出,为了完成双向认证,服务器和客户需要验证对方的证书,然后进行加密协商。这是基于JAVA实现服务器端和客户端的双向认证。
首先,准备服务器和客户端的相关证书:
1. 创建自签名的根密钥
openssl genrsa -out rootkey.pem 2048
2. 生成根证书
openssl req -x509 -new -key rootkey.pem -out root.crt -subj="/C=CN/ST=GD/L=GZ/O=RootCA/OU=RootCA/CN=RootCA"
3. 生成客户端密钥
openssl genrsa -out clientkey.pem 2048
4. 生成客户端证书请求文件,使用根证书签发
openssl req -new -key clientkey.pem -out client.csr -subj="/C=CN/ST=GD/L=GZ/O=BMW/OU=Vehicle/CN=Vehicle1"
5. 用根证签发客户端请求文件,生成客户端证书client.crt
openssl x509 -req -in client.csr -CA root.crt -CAkey rootkey.pem -CAcreateserial -days 3650 -out client.crt
6.包装客户端数据为pkcs12格式(client.pkcs12)
openssl pkcs12 -export -in client.crt -inkey clientkey.pem -out client.pkcs12
7.在服务器端生成密钥
openssl genrsa -out serverkey.pem 2048
8.生成服务器端证书的请求文件。请求签发证书
openssl req -new -key serverkey.pem -out server.csr -subj="/C=CN/ST=GD/L=GZ/O=BMW/OU=IT/CN=Broker"
9.服务器端请求文件由根证签发生成server.crt
openssl x509 -req -in server.csr -CA root.crt -CAkey rootkey.pem -CAcreateserial -days 3650 -out server.crt
10.打包服务器端资料为pkcs12格式(server.pkcs12 )
openssl pkcs12 -export -in server.crt -inkey serverkey.pem -out server.pkcs12
11.生成信任客户端keystore,将根证和需要信任的客户端证添加到此中keystore
keytool -importcert -alias ca -file root.crt -keystore clienttrust.jks
keytool -importcert -alias clientcert -file client.crt -keystore clienttrust.jks
12. 生成信任服务器端keystore,将根证和需要信任的服务端证添加到此中keystore
keytool -importcert -alias ca -file root.crt -keystore servertrust.jks
keytool -importcert -alias servercert -file server.crt -keystore servertrust.jks
服务器程序
以下是服务器程序
public class SSLServer { private SSLServerSocket sslServerSocket; public static void main( String[] args ) throws Exception { SSLServer server = new SSLserver(); server.init(); System.out.println( "Server initialted!" ); server.process(); } public void init() throws Exception { int port = 9999; String keystorePath = "/home/roy/projects/cert/server.pkcs12"; String keystorePass = "123456"; SSLContext context = SSLContext.getInstance("TLSv1.2"); ///加载服务器的证书Private key KeyStore serverKeyStore = KeyStore.getInstance("pkcs12"); FileInputStream keystoreFis = new FileInputStream(keystorePath); serverKeyStore.load(keystoreFis, keystorePass.toCharArray()); KeyManagerFactory kmf = KeyManagerFactory.getInstance("sunx509"); kmf.init(serverKeyStore, keystorePass.toCharArray()); ///加载信任的客户端证书keystore String trustClientKeystorePath = "/home/roy/projects/cert/clienttrust.jks"; KeyStore trustKeyStore = KeyStore.getInstance("jks"); FileInputStream trustKeystoreFis = new FileInputStream(trustClientKeystorePath); trustKeyStore.load(trustKeystoreFis, keystorePass.toCharArray()); TrustManagerFactory tmf = TrustManagerFactory.getInstance("sunx509"); tmf.init(trustKeyStore); context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); sslServerSocket = (SSLServerSocket)context.getServerSocketFactory().createServerSocket(port); sslServerSocket.setNeedClientAuth(true); } public void process() throws Exception { String bye = "Bye!"; byte[] buffer = new byte[50]; while(true) { Socket socket = sslServerSocket.accept(); InputStream in = socket.getInputStream(); in.read(buffer); System.out.println("Received: " new String(buffer)); OutputStream out = socket.getOutputStream(); out.write(bye.getBytes()); out.flush(); } } }
客户端程序
以下代码:
public class SSLClient { private SSLSocket sslSocket; public static void main( String[] args ) throws Exception { SSLClient client = new SSLClient(); client.init(); System.out.println( "Client initiated." ); client.process(); } public void init() throws Exception { String host = "127.0.0.1"; int port = 9999; String keystorePath = "/home/roy/projects/cert/client.pkcs12"; String keystorePass = "123456"; SSLContext context = SSLContext.getInstance("TLSv1.2);
//加载客户端的证书和private key
KeyStore clientKeyStore = KeyStore.getInstance("pkcs12");
FileInputStream keystoreFis = new FileInputStream(keystorePath);
clientKeyStore.load(keystoreFis, keystorePass.toCharArray());
KeyManagerFactory kmf = KeyManagerFactory.getInstance("sunx509");
kmf.init(clientKeyStore, keystorePass.toCharArray());
//加载信任的服务器证书的keystore
String trustServerKeystorePath = "/home/roy/projects/cert/servertrust.jks";
KeyStore trustKeyStore = KeyStore.getInstance("jks");
FileInputStream trustKeystoreFis = new FileInputStream(trustServerKeystorePath);
trustKeyStore.load(trustKeystoreFis, keystorePass.toCharArray());
TrustManagerFactory tmf = TrustManagerFactory.getInstance("sunx509");
tmf.init(trustKeyStore);
context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
sslSocket = (SSLSocket)context.getSocketFactory().createSocket(host, port);
}
public void process() throws Exception {
String hello = "Hello World";
OutputStream out = sslSocket.getOutputStream();
out.write(hello.getBytes(), 0, hello.getBytes().length);
out.flush();
Thread.sleep(1000);
InputStream in = sslSocket.getInputStream();
byte [] buffer = new byte[50];
in.read(buffer);
System.out.println(new String(buffer));
}
}
之后分别运行服务器和客户端程序,可以见到SSL的双向认证通过,建立连接并成功收发信息。
如果要实现单向认证,那么客户端的代码不需要改动,服务器端的代码改动如下:
public class SSLServer
{
private SSLServerSocket sslServerSocket;
public static void main( String[] args ) throws Exception
{
SSLServer server = new SSLserver();
server.init();
System.out.println( "Server initialted!" );
server.process();
}
public void init() throws Exception {
int port = 9999;
String keystorePath = "/home/roy/projects/cert/server.pkcs12";
String keystorePass = "123456";
SSLContext context = SSLContext.getInstance("TLSv1.2");
//加载服务器的证书和Private key
KeyStore serverKeyStore = KeyStore.getInstance("pkcs12");
FileInputStream keystoreFis = new FileInputStream(keystorePath);
serverKeyStore.load(keystoreFis, keystorePass.toCharArray());
KeyManagerFactory kmf = KeyManagerFactory.getInstance("sunx509");
kmf.init(serverKeyStore, keystorePass.toCharArray());
context.init(kmf.getKeyManagers(), null, null);
sslServerSocket = (SSLServerSocket)context.getServerSocketFactory().createServerSocket(port);
sslServerSocket.setNeedClientAuth(false);
}
public void process() throws Exception {
String bye = "Bye!";
byte[] buffer = new byte[50];
while(true) {
Socket socket = sslServerSocket.accept();
InputStream in = socket.getInputStream();
in.read(buffer);
System.out.println("Received: " + new String(buffer));
OutputStream out = socket.getOutputStream();
out.write(bye.getBytes());
out.flush();
}
}
}