客户端代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
import java.io.*; import java.net.InetSocketAddress; import java.net.Socket; import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLSocketFactory; public class SslClient { private SSLSocket s; private DataOutputStream out; private DataInputStream in; public SslClient() throws IOException { } public void sendMessage(Socket s, String msg) { try { out = new DataOutputStream(s.getOutputStream()); String jsonString = msg; out.write(jsonString.getBytes()); } catch (IOException e) { } } public String receiveMessage(Socket s) { try { in = new DataInputStream(s.getInputStream()); BufferedReader socketIn = new BufferedReader(new InputStreamReader(in));// 接受到 String res = socketIn.readLine(); if (res == null || res.equals("null")) { res = ""; } return res; } catch (Exception e) { return ""; } } public String talk(String ip, int port, String trustStore, String trustStorePassword, int socketOutTime, String msg) throws Exception { try { int connectTimeout = socketOutTime * 1000;//设置超时时间 System.setProperty("javax.net.ssl.trustStore", trustStore);// 设置可信任的密钥仓库 System.setProperty("javax.net.ssl.trustStorePassword", trustStorePassword); // 设置可信任的密钥仓库的密码 SSLSocketFactory sslsf = (SSLSocketFactory) SSLSocketFactory.getDefault();// 利用工厂来创建SSLSocket安全套接字 s = (SSLSocket) sslsf.createSocket(); s.connect(new InetSocketAddress(ip, port), connectTimeout); s.startHandshake(); System.out.println("向服务器发送:" + msg); sendMessage(s, msg); // 发字符串 String result = receiveMessage(s); System.out.println("服务器返回:" + result); out.close(); in.close(); if (s != null) { s.close(); } return result; } catch (Exception e) { e.printStackTrace(); out.close(); in.close(); if (s != null) { s.close(); } return ""; } } public static void main(String[] args) throws Exception { SslClient c = new SslClient(); String responseMsg = c.talk("xxx.xxx.xxx.xxx", 8089, "D:\\xxxx\\shfqtruststore.jks", "123456", 5, "test\n"); System.out.println(responseMsg); } } |
服务端代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
//服务端代码如下 参考 https://blog.csdn.net/spdxspdx/article/details/84230574 import java.io.BufferedReader; import java.io.DataOutputStream; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.security.KeyManagementException; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.UnrecoverableKeyException; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLServerSocket; import javax.net.ssl.SSLServerSocketFactory; import javax.net.ssl.SSLSocket; public class SSLServer { public static void startSSLServer() throws IOException, java.security.cert.CertificateException { int port = 8089;// 监听端口 String keyFile = "D:\\xxxx\\shfqkeystore.jks";// 密钥库文件 String keyFilePass = "123456";// 密钥库的密码 String keyPass = "123456";// 密钥别名的密码 SSLServerSocket sslsocket = null;// 安全连接套接字 KeyStore ks;// 密钥库 KeyManagerFactory kmf;// 密钥管理工厂 SSLContext sslc = null;// 安全连接方式 // 初始化安全连接的密钥 try { ks = KeyStore.getInstance("JKS"); ks.load(new FileInputStream(keyFile), keyFilePass.toCharArray()); kmf = KeyManagerFactory.getInstance("SunX509"); kmf.init(ks, keyPass.toCharArray()); sslc = SSLContext.getInstance("SSLv3"); sslc.init(kmf.getKeyManagers(), null, null); } catch (KeyManagementException ex) { ex.printStackTrace(); } catch (UnrecoverableKeyException ex) { ex.printStackTrace(); } catch (KeyStoreException ex) { ex.printStackTrace(); } catch (NoSuchAlgorithmException ex) { ex.printStackTrace(); } // 用安全连接的工厂来创建安全连接套接字 SSLServerSocketFactory sslssf = sslc.getServerSocketFactory(); sslsocket = (SSLServerSocket) sslssf.createServerSocket(port);// 创建并进入监听 System.out.println("Listening..."); while (true) { SSLSocket ssocket = (SSLSocket) sslsocket.accept();// 接受客户端的连接 System.out.println("Server Connection ....."); // 以下代码同socket通讯实例中的代码 BufferedReader socketIn = new BufferedReader(new InputStreamReader(ssocket.getInputStream())); DataOutputStream socketOut = new DataOutputStream(ssocket.getOutputStream()); String s = null; System.out.println("Please wait client 's message.."); s = socketIn.readLine(); System.out.println("Client Message: " + s); s = "接收成功"; System.out.print("Server Message: " + s); socketOut.write(s.getBytes()); ssocket.close(); } } public static void main(String[] args) { try { startSSLServer(); } catch (Exception e) { e.printStackTrace(); } } } |
1 SSL单向认证概念
当客户端(服务请求方)向服务端(服务提供方)发起请求时,服务器端需要向客户端提供认证。服务端需要生成一个keystore和一个服务器密钥对儿(公钥和私钥),客户端需要生成一个truststore,然后导入服务端的公钥证书。
2 keystore以及服务器密钥对儿的生成
1 |
keytool -genkeypair -alias certificatekey -keyalg RSA -validity 365 -keystore shfqkeystore.jks |
这条命令会在生成 keystore 后接着生成一个密钥对儿。RSA是非对称密钥算法,也可以改为 keytool支持的其他密钥算法,365 代表的是证书的有效期,可以自己指定,shfqkeystore.jks 是 keystroe 的名称,也可以自己指定。打开 cmd 命令行,输入:
1 |
keytool -genkeypair -alias certificatekey -keyalg RSA -validity 365 -keystore shfqkeystore.jks |
会提示输入 keystore 的密码,接着会提示输入名字等信息,如下图:
补充:输入<certificatekey>的主密码,是指生成服务端证书的私钥。服务端私钥如果和keystore的相同的话,直接按回车。建议直接按回车,即服务端私钥和keystore的密码相同。如果两者的密码不相同的话在服务端tomcat server.xml中配置完毕以后启动tomcat会报一个 UnrecoverableKeyException: Cannot recover key 的异常(后面会介绍服务端 tomcat server.xml 的配置的)。
keytool会把生成的keystore文件默认保存到C:\Users\lenovo路径下(用户目录下的计算机名称下)接下来生成的所有文件也都保存到此处。
3 验证新生成的keystor文件以及证书信息
可以执行下面的命令:
1 |
keytool -list -v -keystore shfqkeystore.jks |
会显示出以下信息,如图:
4 导出公钥证书
下面的命令可以导出自签公钥证书:
1 |
keytool -export -alias certificatekey -keystore shfqkeystore.jks -rfc -file shfqcert.cer |
其中shfqcert.cer是导出证书的名称,可以随便起个名字,shfqkeystore.jks是2中生成的keystore 文件。
执行上面的命令会要求输入shfqkeystore的密码,会显示以下信息,如下图。
5 Truststore的生成以及公钥证书的导入
把4生成的公钥证书shfqcert.cer导入到truststore中
1 |
Keytool -import -alias certificatekey -file shfqcert.cer -keystore shfqtruststore.jks |
shfqcert.cer是4导出的公钥证书,shfqtruststore.jks可以随便起,是生成的truststore的文件名。这条命令首先会生成一个truststore,然后导入4生成的公钥证书shfqcert.cer。
执行
1 |
keytool -import -alias certificatekey -file shfqcert.cer -keystore shfqtruststore.jks |
后,首先会提示输入 truststore 的密码,如下图:
6 验证5生成的truststore文件
1 |
keytool -list -v -keystore shfqtruststore.jks |
shfqtruststore.jks是5生成的truststore文件名。
到此为止,keystore、truststore、公钥证书都已生成完毕。