项目地址:https://github.com/gongxianshengjiadexiaohuihui/PKI/tree/master/ssl
上一篇 我们讲了如何用java自带的keytool工具生成数字证书,我们需要准备两个证书,一个是server的一个是client的
keytool -import -file client_pub_cer.cer -keystore server.keystore -storepass 123456
把 client的证书导入到server的证书仓库中,并且信任
同样把server的证书导入到client的证书仓库中,并且信任
然后就开始代码编写
服务端代码
package com.ggp.server;
import javax.net.ServerSocketFactory;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLServerSocket;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.security.KeyStore;
/**
* @ClassName SSLServer
* @Description TODO
* @Author Mr.G
* @Date 2018/11/29 16:44
* @Version 1.0
*/
public class SSLServer extends Thread {
private Socket socket;
private ServerSocket serverSocket;
public SSLServer(ServerSocket serverSocket)throws Exception{
this.serverSocket = serverSocket;
}
@Override
public void run() {
while (this.isAlive()) {
try {
socket = serverSocket.accept();
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintWriter writer = new PrintWriter(socket.getOutputStream());
String data = reader.readLine();
writer.println(data);
writer.close();
socket.close();
} catch (IOException e) {
}
}
}
public static void main(String[] args) throws Exception{
/**
* KeyStore的位置路径
*/
String serverKeyStore = "C:\\Users\\14747\\server.keystore";
/**
* KeyStore的密码
*/
String serverKeyStorePassword = "123456";
/**
* 指定trustStore
*/
System.setProperty("javax.net.ssl.trustStore",serverKeyStore);
/**
* 定义并初始化一个keyStore
*/
KeyStore server_KeyStore = KeyStore.getInstance("JKS");
server_KeyStore.load(new FileInputStream(serverKeyStore),null);
/**
* 选用一个安全套接字协议
*/
SSLContext context = SSLContext.getInstance("TLSv1");
/**
* 初始化一个keyManager工厂
*/
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(server_KeyStore,serverKeyStorePassword.toCharArray());
/**
* 初始化SSLcontext
*/
context.init(keyManagerFactory.getKeyManagers(),null,null);
/**
* 得到ServerSocket工厂
*/
ServerSocketFactory factory = context.getServerSocketFactory();
ServerSocket serverSocket = factory.createServerSocket(28888);
/**
* 决定服务端是否验证客户端的身份 即选择单向认证还是双向认证
*/
((SSLServerSocket)serverSocket).setNeedClientAuth(true);
new SSLServer(serverSocket).start();
}
}
客户端代码
package com.ggp.client;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.security.KeyStore;
/**
* @ClassName SSLClient
* @Description TODO
* @Author Mr.G
* @Date 2018/11/30 9:17
* @Version 1.0
*/
public class SSLClient {
public static void main(String[] args)throws Exception{
String clientKeyStore = "D:\\client.keystore";
String clientKeyStorePassword = "123456";
System.setProperty("javax.net.ssl.trustStore",clientKeyStore);
System.setProperty("javax.net.debug","ssl,handshake");
KeyStore client_keyStore = KeyStore.getInstance("JKS");
client_keyStore.load(new FileInputStream(clientKeyStore),null);
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(client_keyStore,clientKeyStorePassword.toCharArray());
SSLContext context = SSLContext.getInstance("TLSv1");
context.init(keyManagerFactory.getKeyManagers(),null,null);
SSLSocketFactory factory = context.getSocketFactory();
Socket socket =factory.createSocket("localhost",28888);
PrintWriter writer = new PrintWriter(socket.getOutputStream());
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
writer.println("hello");
writer.flush();
System.out.println(reader.readLine());
socket.close();
}
}
服务端的代码,注释很详细,客户端的代码基本配置和服务端大致相同
然后我们讨论里面出现的几个名词
System.setProperty("javax.net.ssl.trustStore",serverKeyStore);
如果是要发起SSL请求,这个时候通常是需要指定trustStore的
一种方式是通过启动参数指定
java -Djavax.net.ssl.trustStore=yourTruststore -Djavax.net.ssl.trustStorePassword=yourpassword yourApp
还有一种方式,就是通过程序中指定Properties参数进行加载,不过一定要在请求发出前进行加载。就是我们所用的方式
SSLContext context = SSLContext.getInstance("TLSv1");
首先给出一张图
(1)SSLContext: 此类的实例表示安全套接字协议的实现, 它是SSLSocketFactory、SSLServerSocketFactory和SSLEngine的工厂。
(2)SSLSocket: 扩展自Socket
(3)SSLServerSocket: 扩展自ServerSocket
(4)SSLSocketFactory: 抽象类,扩展自SocketFactory, SSLSocket的工厂
(5)SSLServerSocketFactory: 抽象类,扩展自ServerSocketFactory, SSLServerSocket的工厂
(6)KeyStore: 表示密钥和证书的存储设施
(7)KeyManager: 接口,JSSE密钥管理器
(8)TrustManager: 接口,信任管理器
(9)X590TrustedManager: TrustManager的子接口,管理X509证书,验证远程安全套接字
几种SSLContext Algorithms
SL - Supports some version of SSL; may support other versions
SSLv2 - Supports SSL version 2 or later; may support other versions
SSLv3 - Supports SSL version 3; may support other versions
TLS - Supports some version of TLS; may support other versions
TLSv1 - Supports RFC 2246: TLS version 1.0 ; may support other versions
TLSv1.1 - Supports RFC 4346: TLS version 1.1 ; may support other versions
TLSv1.2 - Supports RFC 5246: TLS version 1.2 ; may support other versions