java关于socket的ssl双向认证。
单向认证:客户端向服务器发送消息,服务器接到消息后,用服务器端的密钥库中的私钥对数据进行加密,然后把加密后的数据和服务器端的公钥一起发送到客户端,客户端用服务器发送来的公钥对数据解密,然后在用传到客户端的服务器公钥对数据加密传给服务器端,服务器用私钥对数据进行解密,这就完成了客户端和服务器之间通信的安全问题,但是单向认证没有验证客户端的合法性。
双向认证:
(1)客户端向服务器发送消息,首先把消息用客户端证书加密然后连同时把客户端证书一起发送到服务器端
(2)服务器接到消息后用首先用客户端证书把消息解密,然后用服务器私钥把消息加密,把服务器证书和消息一起发送到客户端
(3)客户端用发来的服务器证书对消息进行解密,然后用服务器的证书对消息加密,然后在用客户端的证书对消息在进行一次加密,连同加密消息和客户端证书一起发送到服务器端,
(4)到服务器端首先用客户端传来的证书对消息进行解密,确保消息是这个客户发来的,然后用服务器端的私钥对消息在进行解密这个便得到了明文数据。
首先创建服务器端私有密钥和公共密钥
命令行
C:\Program Files\Java\jdk\bin
1, keytool -genkey -alias serverkey -keystore kserver.ks
密码: serverpass
2, keytool -export -alias serverkey -keystore kserver.ks -file server.crt
3, keytool -import -alias serverkey -file server.crt -keystore tclient.ks
密码: clientpublicpass
下面创建客户器端私有密钥和公共密钥
1, keytool -genkey -alias clientkey -keystore kclient.ks
密码: clientpass
2, keytool -export -alias clientkey -keystore kclient.ks -file client.crt
3, keytool -import -alias clientkey -file client.crt -keystore tserver.ks
密码: serverpublicpass
把服务器端产生的公共密钥放到客户端, 同样把客户端创建的公共密钥放到服务器端.
下面是服务器端代码:
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.OutputStream;
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.TrustManagerFactory;
public class SSLTestServer {
public static void main(String[] args) throws Exception {
SSLContext ctx = SSLContext.getInstance("SSL");
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
KeyStore ks = KeyStore.getInstance("JKS");
KeyStore tks = KeyStore.getInstance("JKS");
ks.load(new FileInputStream("cert/kserver.ks"), "serverpass".toCharArray());
tks.load(new FileInputStream("cert/tserver.ks"), "serverpublicpass".toCharArray());
kmf.init(ks, "serverpass".toCharArray());
tmf.init(tks);
ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
SSLServerSocket serverSocket = (SSLServerSocket) ctx.getServerSocketFactory().createServerSocket(8443);
serverSocket.setNeedClientAuth(true);
while (true) {
try {
Socket s = serverSocket.accept();
InputStream input = s.getInputStream();
OutputStream utput = s.getOutputStream();
BufferedInputStream bis = new BufferedInputStream(input);
BufferedOutputStream bos = new BufferedOutputStream(output);
byte[] buffer = new byte[20];
int length = bis.read(buffer);
System.out.println("Receive: " + new String(buffer, 0, length).toString());
bos.write("Hello".getBytes());
bos.flush();
s.close();
} catch (Exception e) {
System.out.println(e);
}
}
}
}
下面是客户端代码:
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.KeyStore;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.TrustManagerFactory;
public class SSLTestClient {
public static void main(String[] args) throws Exception {
SSLContext ctx = SSLContext.getInstance("SSL");
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
KeyStore ks = KeyStore.getInstance("JKS");
KeyStore tks = KeyStore.getInstance("JKS");
ks.load(new FileInputStream("cert/kclient.ks"), "clientpass".toCharArray());
tks.load(new FileInputStream("cert/tclient.ks"), "clientpublicpass".toCharArray());
kmf.init(ks, "clientpass".toCharArray());
tmf.init(tks);
ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
SSLSocket sslSocket = (SSLSocket) ctx.getSocketFactory().createSocket("localhost", 8443);
InputStream input = sslSocket.getInputStream();
OutputStream utput = sslSocket.getOutputStream();
BufferedInputStream bis = new BufferedInputStream(input);
BufferedOutputStream bos = new BufferedOutputStream(output);
bos.write("Hello".getBytes());
bos.flush();
byte[] buffer = new byte[20];
int length = bis.read(buffer);
System.out.println(new String(buffer, 0, length));
sslSocket.close();
}
}
常见的HTTPS传输, 不需要进行客户端认证, 也就是单向认证. 这时也就不需要创建客户端的私钥和公钥. 服务器端也只要配置一下服务器端的私钥即可,
当客户端浏览器访问时会生成一个证书文件,类似于上面创建的crt文件. 如果需要程序访问,可以通过这个crt文件生成一个keystore.
然后是用这个keystore作为trust keystore即可.