在网络上信息由源主机到目标主机要经过很多路由和计算机,通常这些机器不会监听路过的信息。但在使用网络银行进行网上消费时,不加以保护的账号密码很有可能被黑客截获并利用给消费者造成不可估量的损失。
Java安全套接字扩展(JSSE,Java Secure Socket Extension)为基于SSL和TLS协议的Java网络应用程序提供了Java API及参考实现。JSSE支持数据加密、服务器端身份验证、数据完整性。使用JSSE,能保证采用各种应用层协议(HTTP、Telnet、FTP等)的客户程序与服务器程序安全地交换数据
要实现用JSSE交换数据,需要用到证书,获取证书有两种方式,一是从权威机构购买证书,二是创建自我签名的证书。我们用JDK现有的工具keytool创建一个自我签名的证书。
服务端证书
客户端证书
jks文件是一个密钥容器,证书都会存储在里面,如果要导出刚刚生成的证书,用下面的命令
keytool -export -alias keyAlias
-storepass changeit
-file server.cer
-keystore keystore.jks
JSSE中负责安全通信的核心类是SSLServerSocket类和SSLSocket类,它们分别是ServerSocket与Socket类的子类。SSLSocket对象由SSLSocketFactory创建,不过SSLServerSocket的accept()方法也会创建SSLSocket。SSLServerSocketFactory、SSLSocketFactory对象都由SSLContext对象创建。
下面是一个简单的例子,客户端和服务端采用刚才生成的两个证书进行通信,在控制台随便输入信息,服务端都会响应,输入"88"结束本次会话。
客户端:
package com.bill99.seashell.domain;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.security.KeyStore;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManagerFactory;
public class MySSLClient {
private static final String SSL_TYPE = "SSL";
private static final String X509 = "SunX509";
private static final String KS_TYPE = "JKS";
private SSLSocket sslcntSocket;
public MySSLClient(String targetHost,int port) throws Exception {
SSLContext sslContext = createSSLContext(); //创建SSL上下文
SSLSocketFactory sslcntFactory =(SSLSocketFactory) sslContext.getSocketFactory();
sslcntSocket = (SSLSocket) sslcntFactory.createSocket(targetHost, port);
String[] supported = sslcntSocket.getSupportedCipherSuites();
sslcntSocket.setEnabledCipherSuites(supported); //设置加密套件
}
private SSLContext createSSLContext() throws Exception{
KeyManagerFactory kmf = KeyManagerFactory.getInstance(X509);
TrustManagerFactory tmf = TrustManagerFactory.getInstance(X509);
//-----------------------------------------------------------
String clientKeyStoreFile = "c:\\merchant.jks"; //客户端用于证实自己身份的证书
String cntPassphrase = "baitour"; //证书密码
char[] cntPassword = cntPassphrase.toCharArray();
KeyStore clientKeyStore = KeyStore.getInstance(KS_TYPE);
clientKeyStore.load(new FileInputStream(clientKeyStoreFile),cntPassword);
//-----------------------------------------------------------
String serverKeyStoreFile = "c:\\paygateway.jks"; //服务端证书
String svrPassphrase = "99bill"; //证书密码
char[] svrPassword = svrPassphrase.toCharArray();
KeyStore serverKeyStore = KeyStore.getInstance(KS_TYPE);
serverKeyStore.load(new FileInputStream(serverKeyStoreFile), svrPassword);
kmf.init(clientKeyStore, cntPassword);
tmf.init(serverKeyStore); //添加信任的证书
SSLContext sslContext = SSLContext.getInstance(SSL_TYPE);
sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
return sslContext;
}
/**
* 发送消息至服务器
* @param sayMsg 发送给服务器信息
* @return String 服务器响应的信息
* @throws IOException 发送过程Socket出错时抛出此异常
*/
public String sayToSvr(String sayMsg) throws IOException{
BufferedReader ioReader = new BufferedReader(new InputStreamReader(
sslcntSocket.getInputStream()));
PrintWriter ioWriter = new PrintWriter(sslcntSocket.getOutputStream());
ioWriter.println(sayMsg);
ioWriter.flush();
return ioReader.readLine();
}
public static void main(String[] args) throws Exception {
MySSLClient mysslCnt = new MySSLClient("127.0.0.1",7612);
BufferedReader ioReader = new BufferedReader(new InputStreamReader(System.in));
String sayMsg = "";
String svrRespMsg= "";
while( (sayMsg = ioReader.readLine())!= null ) {
svrRespMsg = mysslCnt.sayToSvr(sayMsg);
if(svrRespMsg != null && !svrRespMsg.trim().equals("")) {
System.err.println("服务器响应:"+svrRespMsg);
}
if(sayMsg.trim().equals("88")) {
break ;
}
}
}
}
服务端
package com.bill99.seashell.domain;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
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 javax.net.ssl.SSLSocket;
import javax.net.ssl.TrustManagerFactory;
public class MySSLServer {
private static final String SSL_TYPE = "SSL";
private static final String KS_TYPE = "JKS";
private static final String X509 = "SunX509";
private final static int PORT = 7612; //监听端口
private final static Object lock = new Object();//对象锁
private static MySSLServer mysslServer;
private SSLServerSocket svrSocket;
/**
* 通过单态模式获得MySSLServer对象
*/
public static MySSLServer getInstance() throws Exception {
synchronized (lock) {
if (mysslServer == null) {
mysslServer = new MySSLServer();
}
return mysslServer;
}
}
private MySSLServer() throws Exception{
//输出跟踪日志
System.setProperty("javax.net.debug","all");
//创建SSL上下文
SSLContext sslContext = createSSLContext();
SSLServerSocketFactory serverFactory = sslContext.getServerSocketFactory();
svrSocket =(SSLServerSocket) serverFactory.createServerSocket(PORT);
svrSocket.setNeedClientAuth(true); //需要验证客户的身份
System.err.println("【SSL服务器启动,监听端口:"+PORT+ "】");
System.err.println(svrSocket.getNeedClientAuth() ? "【需要验证对方身份】" : "【不需要验证对方的身份】");
//设置支持加密的套件
String[] supported = svrSocket.getEnabledCipherSuites();
svrSocket.setEnabledCipherSuites(supported);
}
/**
* 创建上下文
* @return SSLContext
* @throws Exception 在创建SSLContext发生错误时抛出此异常
*/
private SSLContext createSSLContext() throws Exception{
//证书管理器
KeyManagerFactory kmf = KeyManagerFactory.getInstance(X509);
//信任管理器
TrustManagerFactory tmf = TrustManagerFactory.getInstance(X509);
//服务器证书加载到证书管理器
//----------------------------------------------------------
String serverKeyStoreFile = "c:\\paygateway.jks"; //服务器用于证实自己身份的证书
String svrPassphrase = "99bill"; //服务器证书密码
char[] svrPassword = svrPassphrase.toCharArray();
KeyStore serverKeyStore = KeyStore.getInstance(KS_TYPE);
serverKeyStore.load(new FileInputStream(serverKeyStoreFile), svrPassword);
kmf.init(serverKeyStore, svrPassword);
//客户机证书加载到证书管理器
//-----------------------------------------------------------
String clientKeyStoreFile = "c:\\merchant.jks"; //客户端用于证实自己身份的证书
String cntPassphrase = "baitour"; //证书密码
char[] cntPassword = cntPassphrase.toCharArray();
KeyStore clientKeyStore = KeyStore.getInstance(KS_TYPE);
clientKeyStore.load(new FileInputStream(clientKeyStoreFile),cntPassword);
tmf.init(clientKeyStore); //添加信任的证书
SSLContext sslContext = SSLContext.getInstance(SSL_TYPE);
sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
return sslContext;
}
/**
* 开始服务
*/
public void startService() {
SSLSocket cntSocket = null;
BufferedReader ioReader = null;
PrintWriter ioWriter = null;
String tmpMsg = null;
while( true ) {
try {
cntSocket =(SSLSocket) svrSocket.accept();
System.err.println("[有客户机连接,IP:"+cntSocket.getInetAddress()+"]");
ioReader = new BufferedReader(new InputStreamReader(cntSocket.getInputStream()));
ioWriter = new PrintWriter(cntSocket.getOutputStream());
while ( (tmpMsg = ioReader.readLine()) != null) {
System.err.println("[客户机说:"+tmpMsg+"]");
if("88".equals(tmpMsg)) {
break;
}
tmpMsg = " **** Welcome to our website **** ";
ioWriter.println(tmpMsg);
ioWriter.flush();
System.err.println("[服务器说:"+tmpMsg+"]");
}
} catch(IOException e) {
e.printStackTrace();
} finally {
try {
if(cntSocket != null) cntSocket.close();
} catch(Exception ex) {ex.printStackTrace();}
}
}//end while
}//end startService method
public static void main(String[] args) throws Exception {
MySSLServer mysslSvr = MySSLServer.getInstance();
mysslSvr.startService();
}
}