系统环境:
java version "1.8.0_66" x64
一:使用keytool生成服务端证书
生成密钥对
keytool -genkeypair -alias myserver -keystore myserver.jks
输入密码123456(注意,两次输入的密码一样)
从密钥对中导出证书
keytool -exportcert -alias myserver -keystore myserver.jks -file trust.cer
把导出的证书导入到受信任的证书库中
keytool -importcert -file trust.cer -keystore trust.jks
输入密码123456(注意,两次输入的密码一样)
这样,就得到了2个jks证书文件
二:服务端代码
package com.lala.ssl;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
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.SSLServerSocketFactory;
import org.apache.commons.io.IOUtils;
public class SSLServer
{
public static final int port = 4488;
static SSLServerSocket createServerSocket(int port)throws Exception
{
KeyStore ks = KeyStore.getInstance("jks");
InputStream input = new FileInputStream("d:/tmp/myserver.jks");
ks.load(input, "123456".toCharArray());
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(ks, "123456".toCharArray());
SSLContext context = SSLContext.getInstance("TLSv1.2");
/**
* KeyManager[] 第一个参数是授权的密钥管理器,用来授权验证。TrustManager[]第二个是被授权的证书管理器,
* 用来验证服务器端的证书。第三个参数是一个随机数值,可以填写null。如果只是服务器传输数据给客户端来验证,就传入第一个参数就可以,
* 客户端构建环境就传入第二个参数。双向认证的话,就同时使用两个管理器。
*/
context.init(kmf.getKeyManagers(), null, null);
input.close();
SSLServerSocketFactory ssf = context.getServerSocketFactory();
return (SSLServerSocket)ssf.createServerSocket(port);
}
public static void main(String[] args)throws Exception
{
final ServerSocket ss = createServerSocket(port);
System.out.println("ssl server startup at port " + port);
while(true)
{
final Socket s = ss.accept();
new Thread(()-> {
OutputStream output = null;
try
{
output = s.getOutputStream();
output.write("hello word".getBytes());
output.flush();
}catch(Exception e)
{
e.printStackTrace();
}
finally
{
IOUtils.closeQuietly(output);
IOUtils.closeQuietly(s);
}
}).start();
}
}
}
三:客户端代码
package com.lala.ssl;
import java.io.FileInputStream;
import java.io.InputStream;
import java.net.Socket;
import java.security.KeyStore;
import javax.net.SocketFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.TrustManagerFactory;
import org.apache.commons.io.IOUtils;
public class SSLClient
{
static SSLSocket createSocket(String host, int port)throws Exception
{
KeyStore ks = KeyStore.getInstance("jks");
InputStream input = new FileInputStream("d:/tmp/trust.jks");
ks.load(input, "123456".toCharArray());
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
tmf.init(ks);
SSLContext context = SSLContext.getInstance("TLSv1.2");
/**
* KeyManager[] 第一个参数是授权的密钥管理器,用来授权验证。TrustManager[]第二个是被授权的证书管理器,
* 用来验证服务器端的证书。第三个参数是一个随机数值,可以填写null。如果只是服务器传输数据给客户端来验证,就传入第一个参数就可以,
* 客户端构建环境就传入第二个参数。双向认证的话,就同时使用两个管理器。
*/
context.init(null, tmf.getTrustManagers(), null);
input.close();
SocketFactory sf = context.getSocketFactory();
return (SSLSocket)sf.createSocket(host, port);
}
public static void main(String[] args) throws Exception
{
Socket s = createSocket("127.0.0.1", SSLServer.port);
InputStream input = s.getInputStream();
String response = IOUtils.toString(input);
System.out.println(response);
IOUtils.closeQuietly(input);
IOUtils.closeQuietly(s);
}
}
这里,服务端演示的是,服务端给连接上的客户端发送hello word消息,客户端收到之后,输出。
先启动服务端,然后启动客户端,即可看到输出