1.Keytool工具生成SSL证书

    keytool即JDK中自带的证书生成工具,常见的还有openssl工具。

     1.生成一个自签名的CA证书,为了给Client和Server的证书签名。

      命令:keytool -genkeypair (-keyalg RSA) -alias TEST_ROOT -keystore
test_root.jks

     
解释:生成一对密钥,存储在test_root.jks中,条目别名为TEST_ROOT。输入该命令后会提示输入个人信息。当命令完成后,会在test_root.jks中生成一个自签名的证书。当然,私钥也保存在该文件中。这里密钥库的密码和密钥密码都设置成123456

如果不加keyalg 的,默认是DSA算法生成

    

2.为server生成一对密钥(也就是一个自签名的证书)

 命令:keytool -genkeypair -alias TEST_SERVER -keystore test_server.jks

解释:生成一对密钥,存储在test_server.jks中,条目别名TEST_SERVER。



3.生成一个签名请求文件(即请求CA给server自签名的证书签名)

命令:keytool -certreq -file test_server.csr -alias TEST_SERVER -keystore
test_server.jks

解释:为存储在test_server.jks中的别名TEST_SERVER生成一个证书请求文件test_server.csr。



4.ca为server的证书签名

命令:keytool -gencert -infile test_server.csr -outfile test_server.cer -alias
TEST_ROOT -keystore TEST_ROOT.jks


解释:利用TEST_ROOT.jks中条目别名为TEST_ROOT的私钥为test_server.csr证书请求对应的证书签名,并将签名后的证书保存在test_server.cer中。



5.导出ca证书

命令:keytool -exportcert -alias TEST_ROOT -file test_root.cer -keystore
test_root.jks

解释:将test_root.jks中条目别名为TEST_ROOT的证书导出到test_root.cer中。



6.将ca证书导入到test_server.jks中。

命令:keytool -importcert -alias TEST_ROOT -file test_root.cer -keystore
TEST_SERVER.jks

解释:将test_root.cer证书文件导入TEST_SERVER.jks中,条目别名为TEST_ROOT。
这里需要注意,在TEST_SERVER.jks中之前应不存在条目别名为TEST_ROOT,这样才会以信任证书的方式导入。



7.更新server证书

命令:keytool -importcert -alias TEST_SERVER -file test_server.cer -keystore
TEST_SERVER.jks

解释:将test_server.cer导入到TEST_SERVER.jks中。因为在生成server密钥对的时候,
在test_server.jks中已经存在自签的证书,条目别名为TEST_SERVER。而该命令保存的别名还是TEST_SERVER。意思上就是更新证书。将之前自签名的证书替换为ca签名过的证书。



8.查看test_server.jks中server的证书和ca证书。

命令:keytool -list -v -keystore test_server.jks





从条目test_server中可以看出,该证书被ca签名后,ca的证书也保存在该条目中,形成证书链。

对于Client的证书生成参考2-7步。在该过程中生成的文件如下:



2.在Java中实现SSL通信

  Java中主要通过JSSE(Java Secure Socket Extension
)来完成安全套接字的编写。其中主要涉及了一些类,例如Keystore、KeyManagerFactory、TrustManagerFactory、SSLContext等。它们的关系如下图(图的来源于
文章结尾的链接中)

 

编程的步骤如下:

1.利用keystore类完成客户端和服务器的证书库和信任库的加载

2.利用加载的证书库和信任库,完成KeyManagerFactory和TrustManagerFactory的初始化。


3.生成SSLContext对象,并用keyManagerFactory和TrustManagerFactory生成的keyManager和TrustManager完成初始化。

4.利用SSLContext对象,生成对应的SSLSocket和SSLServerSocket。

需要的文件


1.test_root.jks(存储了ca证书,因为client和server的证书都是被ca签名的,所以ca证书被信任,则client和server的证书都会被信任)。

2.test_client.jks(存储了client的证书)。

3.test_server.jks(存储了server的证书)。

客户端代码
public class Client { public static void main(String[] args) throws Exception
{ String clientKeyStoreFile = "d:\\keystore\\test_client.jks"; String
clientKeyStorePwd = "123456"; String clientKeyPwd = "123456"; String
clientTrustKeyStoreFile = "d:\\keystore\\test_root.jks"; String
clientTrustKeyStorePwd = "123456"; //生成client的keystore对象 KeyStore
clientKeyStore = KeyStore.getInstance("JKS"); clientKeyStore.load(new
FileInputStream(clientKeyStoreFile), clientKeyStorePwd.toCharArray());
//生成信任证书的keystore对象 KeyStore clientTrustKeyStore = KeyStore.getInstance("JKS");
clientTrustKeyStore.load(new FileInputStream(clientTrustKeyStoreFile),
clientTrustKeyStorePwd.toCharArray()); KeyManagerFactory kmf =
KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(clientKeyStore, clientKeyPwd.toCharArray()); TrustManagerFactory tmf =
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(clientTrustKeyStore); SSLContext sslContext =
SSLContext.getInstance("TLSv1"); sslContext.init(kmf.getKeyManagers(),
tmf.getTrustManagers(), null); SSLSocketFactory socketFactory =
sslContext.getSocketFactory(); Socket socket =
socketFactory.createSocket("localhost", Server.SERVER_PORT); PrintWriter out =
new PrintWriter(socket.getOutputStream(), true); BufferedReader in = new
BufferedReader(new InputStreamReader(socket.getInputStream())); send("hello",
out); send("exit", out); receive(in); socket.close(); } public static void
send(String s, PrintWriter out) throws IOException {
System.out.println("Sending: " + s); out.println(s); } public static void
receive(BufferedReader in) throws IOException { String s; while ((s =
in.readLine()) != null) { System.out.println("Reveived: " + s); } } }
服务器代码
public class Server implements Runnable,HandshakeCompletedListener {
    public static final int SERVER_PORT = 11123; private final Socket s;
private String peerCerName; public Server(Socket s) { this.s = s; } public
static void main(String[] args) throws Exception { String serverKeyStoreFile =
"d:\\keystore\\test_server.jks"; String serverKeyStorePwd = "123456"; String
ServerKeyPwd = "123456"; String serverTrustKeyStoreFile =
"d:\\keystore\\test_root.jks"; String serverTrustKeyStorePwd = "123456"; /* *
加载server.keystore * */ KeyStore serverKeyStore = KeyStore.getInstance("JKS");
serverKeyStore.load(new FileInputStream(serverKeyStoreFile),
serverKeyStorePwd.toCharArray()); /* * 加载servertrust.keystore * */ KeyStore
serverTrustKeyStore = KeyStore.getInstance("JKS"); serverTrustKeyStore.load(new
FileInputStream(serverTrustKeyStoreFile),
serverTrustKeyStorePwd.toCharArray()); KeyManagerFactory kmf =
KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(serverKeyStore, ServerKeyPwd.toCharArray()); TrustManagerFactory tmf =
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(serverTrustKeyStore); SSLContext sslContext =
SSLContext.getInstance("TLSv1"); sslContext.init(kmf.getKeyManagers(),
tmf.getTrustManagers(), null); SSLServerSocketFactory sslServerSocketFactory =
sslContext.getServerSocketFactory(); SSLServerSocket sslServerSocket =
(SSLServerSocket) sslServerSocketFactory.createServerSocket(SERVER_PORT);
//设置双向验证 sslServerSocket.setNeedClientAuth(true); while (true) { SSLSocket s =
(SSLSocket)sslServerSocket.accept(); Server cs = new Server(s);
s.addHandshakeCompletedListener(cs); new Thread(cs).start(); } } @Override
public void run() { try { BufferedReader reader = new BufferedReader(new
InputStreamReader(s.getInputStream())); PrintWriter writer = new
PrintWriter(s.getOutputStream(), true); writer.println("Welcome~, enter exit to
leave."); String message; while ((message = reader.readLine()) != null &&
!message.trim().equalsIgnoreCase("exit")) { writer.println("Echo: " + message);
} writer.println("Bye~, " + peerCerName); } catch (Exception e) {
e.printStackTrace(); } finally { try { s.close(); } catch (IOException e) {
e.printStackTrace(); } } } @Override public void
handshakeCompleted(HandshakeCompletedEvent event) { try { X509Certificate cert
= (X509Certificate) event.getPeerCertificates()[0]; peerCerName =
cert.getSubjectX500Principal().getName(); } catch (SSLPeerUnverifiedException
ex) { ex.printStackTrace(); } } }
运行结果:



说明SSL握手成功建立。

参考的文章:https://www.ibm.com/developerworks/cn/java/j-lo-socketkeytool/
<https://www.ibm.com/developerworks/cn/java/j-lo-socketkeytool/>