SSL有两种应用,一种是网页访问服务器,一种是socket之间
一、socket之间互相访问:
KeyStore:其中保存服务端的私钥,用来加密服务端发来的数据
TrustKeyStore:其中保存客户端的授权证书,用来解密客户端发来的数据
Client:
KeyStore:其中保存客户端的私钥,用来加密客户端发来的数据
TrustKeyStore:其中保存服务端的授权证书,用来解密服务端发来的数据
实现技术:
JSSE(Java Security Socket Extension)
是Sun为了解决在Internet上的实现安全信息传输的解决方案。它实现了SSL和TSL(传输层安全)协议。在JSSE中包含了数据加密,服务器验证,消息完整性和客户端验证等技术。通过使用JSSE,可以在Client和Server之间通过TCP/IP协议安全地传输数据。
为了实现消息认证。
Server需要:
1)KeyStore: 其中保存服务端的私钥
2)Trust KeyStore:其中保存客户端的授权证书
Client需要:
1)KeyStore:其中保存客户端的私钥
2)Trust KeyStore:其中保存服务端的授权证书
使用Java自带的keytool命令,去生成这样信息文件:
1)生成服务端私钥,并且导入到服务端KeyStore文件中
2)根据私钥,导出服务端证书
3)将服务端证书,导入到客户端的Trust KeyStore中
采用同样的方法,生成客户端的私钥,客户端的证书,并且导入到服务端的Trust KeyStore中
1)keytool -genkey -alias clientkey -keystore kclient.keystore
2)keytool -export -alias clientkey -keystore kclient.keystore -file client.crt
3)keytool -import -alias clientkey -file client.crt -keystore tserver.keystore
Server:
- package ssl;
- 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;
- /**
- *
- * @author Leo
- */
- public class Server implements Runnable{
- private static final int DEFAULT_PORT = 7777;
- private static final String SERVER_KEY_STORE_PASSWORD = "123456";
- private static final String SERVER_TRUST_KEY_STORE_PASSWORD = "123456";
- private SSLServerSocket serverSocket;
- /**
- * 启动程序
- *
- * @param args
- */
- public static void main(String[] args) {
- Server server = new Server();
- server.init();
- Thread thread = new Thread(server);
- thread.start();
- }
- public synchronized void start() {
- if (serverSocket == null) {
- System.out.println("ERROR");
- return;
- }
- while (true) {
- try {
- Socket s = serverSocket.accept();
- InputStream input = s.getInputStream();
- OutputStream output = s.getOutputStream();
- BufferedInputStream bis = new BufferedInputStream(input);
- BufferedOutputStream bos = new BufferedOutputStream(output);
- byte[] buffer = new byte[20];
- bis.read(buffer);
- System.out.println("------receive:--------"+new String(buffer).toString());
- bos.write("yes".getBytes());
- bos.flush();
- s.close();
- } catch (Exception e) {
- System.out.println(e);
- }
- }
- }
- public void init() {
- try {
- 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("src/ssl/kserver.keystore"), SERVER_KEY_STORE_PASSWORD.toCharArray());
- tks.load(new FileInputStream("src/ssl/tserver.keystore"), SERVER_TRUST_KEY_STORE_PASSWORD.toCharArray());
- kmf.init(ks, SERVER_KEY_STORE_PASSWORD.toCharArray());
- tmf.init(tks);
- ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
- serverSocket = (SSLServerSocket) ctx.getServerSocketFactory().createServerSocket(DEFAULT_PORT);
- serverSocket.setNeedClientAuth(true);
- } catch (Exception e) {
- System.out.println(e);
- }
- }
- public void run() {
- // TODO Auto-generated method stub
- start();
- }
- }
Client:
- package ssl;
- import java.io.BufferedInputStream;
- import java.io.BufferedOutputStream;
- import java.io.FileInputStream;
- import java.io.IOException;
- 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;
- /**
- * SSL Client
- *
- * @author Leo
- */
- public class Client {
- private static final String DEFAULT_HOST = "127.0.0.1";
- private static final int DEFAULT_PORT = 7777;
- private static final String CLIENT_KEY_STORE_PASSWORD = "123456";
- private static final String CLIENT_TRUST_KEY_STORE_PASSWORD = "123456";
- private SSLSocket sslSocket;
- /**
- * 启动客户端程序
- *
- * @param args
- */
- public static void main(String[] args) {
- Client client = new Client();
- client.init();
- client.process();
- }
- public void process() {
- if (sslSocket == null) {
- System.out.println("ERROR");
- return;
- }
- try {
- InputStream input = sslSocket.getInputStream();
- OutputStream output = sslSocket.getOutputStream();
- BufferedInputStream bis = new BufferedInputStream(input);
- BufferedOutputStream bos = new BufferedOutputStream(output);
- bos.write("1234567890".getBytes());
- bos.flush();
- byte[] buffer = new byte[20];
- bis.read(buffer);
- System.out.println(new String(buffer));
- sslSocket.close();
- } catch (IOException e) {
- System.out.println(e);
- }
- }
- public void init() {
- try {
- 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("src/ssl/kclient.keystore"), CLIENT_KEY_STORE_PASSWORD.toCharArray());
- tks.load(new FileInputStream("src/ssl/tclient.keystore"), CLIENT_TRUST_KEY_STORE_PASSWORD.toCharArray());
- kmf.init(ks, CLIENT_KEY_STORE_PASSWORD.toCharArray());
- tmf.init(tks);
- ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
- sslSocket = (SSLSocket) ctx.getSocketFactory().createSocket(DEFAULT_HOST, DEFAULT_PORT);
- } catch (Exception e) {
- System.out.println(e);
- }
- }
- }
启动Server
启动Client,发送信息。
Server接收如下:正确解密
返回Client信息,如下:
如此,就完成了服务端和客户端之间的基于身份认证的交互。
client采用kclient.keystore中的clientkey私钥进行数据加密,发送给server。
server采用tserver.keystore中的client.crt证书(包含了clientkey的公钥)对数据解密,如果解密成功,证明消息来自client,进行逻辑处理。
server采用kserver.keystore中的serverkey私钥进行数据加密,发送给client。
client采用tclient.keystore中的server.crt证书(包含了serverkey的公钥)对数据解密,如果解密成功,证明消息来自server,进行逻辑处理。
如果过程中,解密失败,那么证明消息来源错误。不进行逻辑处理。这样就完成了双向的身份认证。
创建证书
证书是单点登录认证系统中很重要的一把钥匙,客户端于服务器的交互安全靠的就是证书;本教程由于是演示所以就自己用JDK自带的keytool工具生成证书;如果以后真正在产品环境中使用肯定要去证书提供商去购买,证书认证一般都是由VeriSign认证,中文官方网站:http://www.verisign.com/cn/
用JDK自带的keytool工具生成证书:
keytool -genkey -alias wsria -keyalg RSA -keystore d:/keys/wsriakey
无图不给力,有图有真相:
具体的输入项图片中都有说明,有一点我要解释一下;在输入完密码后提示输入域名是我输入的是sso.wsria.com,其实这个域名是不存在的,但是我为了演示所以虚拟了这个域名,技巧在于修改
C:\Windows\System32\drivers\etc\hosts
添加内容如下:
127.0.0.1 sso.wsria.com
这样在访问sso.wsria.com的时候其实是访问的127.0.0.1也就是本机
严重提醒:提示输入域名的时候不能输入IP地址
三、导出证书
D:\keys>keytool -export -file d:/keys/wsria.crt -alias wsria -keystore d:/keys/wsriakey
特别提示:如果提示:
keytool error: java.io.IOException: Keystore was tampered with, or password was incorrect
那么请输入密码:changeit
来点颜色:
至此导出证书完成,可以分发给应用的JDK使用了,接下来讲解客户端的JVM怎么导入证书。
四、为客户端的JVM导入证书
keytool -import -keystore D:\tools\jdk\1.6\jdk1.6.0_20\jre\lib\security\cacerts -file D:/keys/wsria.crt -alias wsria
来点颜色瞧瞧:
特别说明
D:\tools\jdk\1.6\jdk1.6.0_20\jre\lib\security -- 是jre的目录;密码还是刚刚输入的密码。至此证书的创建、导出、导入到客户端JVM都已完成,下面开始使用证书到Web服务器中,本教程使用tomcat。
五、应用证书到Web服务器-Tomcat
说是应用起始做的事情就是启用Web服务器(Tomcat)的SSL,也就是HTTPS加密协议,为什么加密我就不用啰嗦了吧…… 准备好一个干净的tomcat,本教程使用的apache-tomcat-6.0.29 打开tomcat目录的conf/server.xml文件,开启83和87行的注释代码,并设置keystoreFile、keystorePass修改结果如下:
- <</span>Connector
port="8443" protocol="HTTP/1.1" SSLEnabled="true" -
maxThreads="150" scheme="https" secure="true" -
clientAuth="false" sslProtocol="TLS" -
keystoreFile="D:/keys/wsriakey" -
keystorePass="wsria.com" - />
- keystoreFile:在第一步创建的key存放位置
- keystorePass:创建证书时的密码
好了,到此Tomcat的SSL启用完成,现在你可以启动tomcat试一下了,例如本教程输入地址:https://sso.wsria.com:8443/
打开的是:
好的,那么我们点击“继续浏览此网站(不推荐)。 ”,现在进入Tomcat目录了吧,如果是那么你又向成功迈进了一步。
----------------------------------------------------------------------
错误总结:
tomcat配置8443后 启动报错
你需要protocol改成
protocol="org.apache.coyote.http11.Http11Protocol"
摘抄文章:
http://avery-leo.iteye.com/blog/276096/
http://blog.sina.com.cn/s/blog_8020e4110101azsx.html