Java Secure(SSL/TLS) Socket实现
来源:互联网 发布:陈奕迅 苦瓜 知乎 编辑:程序博客网 时间:2024/04/29 04:38
通信端无需向对方证明自己的身份,则称该端处于“客户模式”,否则称其处于“服务器模式”,无论是客户端还是服务器端,都可处于“客户模式”或者“服务器模式”,但是对于通信双方来说,只能有一方处于“服务模式”,而另一方则必须处于“客户模式”
一,证书部分
首先使用java自带的keytool工具生成所需的证书:
1,创建服务端keystore
keytool -genkey -keystore c:\sean_app\server.jks -storepass 1234sp -keyalg RSA -keypass 1234kp
2,创建客户端keystore
keytool -genkey -keystore c:\sean_app\client.jks -storepass 1234sp -keyalg RSA -keypass 1234kp
3,导出服务端证书
keytool -export -keystore c:\sean_app\server.jks -storepass 1234sp -file c:\sean_app\server.cer
4,导出客户端证书
keytool -export -keystore c:\sean_app\client.jks -storepass 1234sp -file c:\sean_app\client.cer
5,将服务端证书导入到客户端trustkeystroe
keytool -import -keystore c:\sean_app\clientTrust.jks -storepass 1234sp -file c:\sean_app\server.cer
6,将客户端证书导入到服务端trustkeystroe
keytool -import -keystore c:\sean_app\serverTrust.jks -storepass 1234sp -file c:\sean_app\client.cer
二,代码部分
使用JDK1.5.0_22和JDK1.6.0_45测试单向认证和双向认证均通过,唯一需要引入的包为log4j-1.2.14.jar,用来记录日志
代码结构如下:
客户端认证:
package com.test.client.auth;import java.io.FileInputStream;import java.security.KeyStore;import java.util.Properties;import javax.net.ssl.KeyManager;import javax.net.ssl.KeyManagerFactory;import javax.net.ssl.SSLContext;import javax.net.ssl.TrustManager;import javax.net.ssl.TrustManagerFactory;import com.test.server.config.Configuration;public class Auth {private static SSLContext sslContext;public static SSLContext getSSLContext() throws Exception{Properties p = Configuration.getConfig();String protocol = p.getProperty("protocol");String clientTrustCerFile = p.getProperty("clientTrustCer");String clientTrustCerPwd = p.getProperty("clientTrustCerPwd");//Trust Key StoreKeyStore keyStore = KeyStore.getInstance("JKS");keyStore.load(new FileInputStream(clientTrustCerFile), clientTrustCerPwd.toCharArray()); TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("SunX509"); trustManagerFactory.init(keyStore); TrustManager[] tms = trustManagerFactory.getTrustManagers();KeyManager[] kms = null;if(Configuration.getConfig().getProperty("authority").equals("2")){String clientCerFile = p.getProperty("clientCer");String clientCerPwd = p.getProperty("clientCerPwd");String clientKeyPwd = p.getProperty("clientKeyPwd");//Key StorekeyStore = KeyStore.getInstance("JKS"); keyStore.load(new FileInputStream(clientCerFile), clientCerPwd.toCharArray()); KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509"); keyManagerFactory.init(keyStore, clientKeyPwd.toCharArray()); kms = keyManagerFactory.getKeyManagers();}sslContext = SSLContext.getInstance(protocol);sslContext.init(kms, tms, null); return sslContext;}}
客户端主程序:
package com.test.client;import java.io.DataInputStream;import java.io.DataOutputStream;import java.io.IOException;import java.net.InetSocketAddress;import java.net.SocketAddress;import java.util.Properties;import javax.net.ssl.SSLContext;import javax.net.ssl.SSLSocket;import javax.net.ssl.SSLSocketFactory;import org.apache.log4j.Logger;import com.test.client.auth.Auth;import com.test.server.config.Configuration;import com.test.tools.MyHandshakeCompletedListener;import com.test.tools.SocketIO;public class Client {static Logger logger = Logger.getLogger(Client.class);private SSLContext sslContext;private int port = 10000;private String host = "127.0.0.1";private SSLSocket socket;private Properties p;public Client(){try {p = Configuration.getConfig();sslContext = Auth.getSSLContext();SSLSocketFactory factory = (SSLSocketFactory) sslContext.getSocketFactory(); socket = (SSLSocket)factory.createSocket(); String[] pwdsuits = socket.getSupportedCipherSuites();//socket可以使用所有支持的加密套件socket.setEnabledCipherSuites(pwdsuits);//默认就是truesocket.setUseClientMode(true);SocketAddress address = new InetSocketAddress(host, port);socket.connect(address, 0);MyHandshakeCompletedListener listener = new MyHandshakeCompletedListener();socket.addHandshakeCompletedListener(listener);} catch (Exception e) {e.printStackTrace();logger.error("socket establish failed!");}}public void request(){try{String encoding = p.getProperty("socketStreamEncoding");DataOutputStream output = SocketIO.getDataOutput(socket);String user = "name";byte[] bytes = user.getBytes(encoding);int length = bytes.length;int pwd = 123;output.write(length);output.write(bytes);output.write(pwd);DataInputStream input = SocketIO.getDataInput(socket);length = input.readShort();bytes = new byte[length];input.read(bytes);logger.info("request result:"+new String(bytes,encoding));}catch(Exception e){e.printStackTrace();logger.error("request error");}finally{try {socket.close();} catch (IOException e) {e.printStackTrace();}}}public static void main(String[] args){Client client = new Client();client.request();}}
服务端认证:
package com.test.server.auth;import java.io.FileInputStream;import java.security.KeyStore;import java.util.Properties;import javax.net.ssl.KeyManager;import javax.net.ssl.KeyManagerFactory;import javax.net.ssl.SSLContext;import javax.net.ssl.TrustManager;import javax.net.ssl.TrustManagerFactory;import com.test.server.config.Configuration;public class Auth {private static SSLContext sslContext;public static SSLContext getSSLContext() throws Exception{Properties p = Configuration.getConfig();String protocol = p.getProperty("protocol");String serverCer = p.getProperty("serverCer");String serverCerPwd = p.getProperty("serverCerPwd");String serverKeyPwd = p.getProperty("serverKeyPwd");//Key StroeKeyStore keyStore = KeyStore.getInstance("JKS"); keyStore.load(new FileInputStream(serverCer), serverCerPwd.toCharArray()); KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509"); keyManagerFactory.init(keyStore, serverKeyPwd.toCharArray()); KeyManager[] kms = keyManagerFactory.getKeyManagers();TrustManager[] tms = null;if(Configuration.getConfig().getProperty("authority").equals("2")){String serverTrustCer = p.getProperty("serverTrustCer");String serverTrustCerPwd = p.getProperty("serverTrustCerPwd");//Trust Key StorekeyStore = KeyStore.getInstance("JKS"); keyStore.load(new FileInputStream(serverTrustCer), serverTrustCerPwd.toCharArray()); TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("SunX509"); trustManagerFactory.init(keyStore); tms = trustManagerFactory.getTrustManagers();}sslContext = SSLContext.getInstance(protocol);sslContext.init(kms, tms, null); return sslContext;}}
服务端主程序:
package com.test.server;import java.io.IOException;import java.net.InetSocketAddress;import java.util.Properties;import java.util.concurrent.Executor;import java.util.concurrent.Executors;import javax.net.ssl.SSLContext;import javax.net.ssl.SSLServerSocket;import javax.net.ssl.SSLServerSocketFactory;import javax.net.ssl.SSLSocket;import org.apache.log4j.Logger;import com.test.server.auth.Auth;import com.test.server.business.Job;import com.test.server.config.Configuration;public class Server {static Logger logger = Logger.getLogger(Server.class);private SSLContext sslContext;private SSLServerSocketFactory sslServerSocketFactory;private SSLServerSocket sslServerSocket;private final Executor executor;public Server() throws Exception{Properties p = Configuration.getConfig();Integer serverListenPort = Integer.valueOf(p.getProperty("serverListenPort"));Integer serverThreadPoolSize = Integer.valueOf(p.getProperty("serverThreadPoolSize"));Integer serverRequestQueueSize = Integer.valueOf(p.getProperty("serverRequestQueueSize"));Integer authority = Integer.valueOf(p.getProperty("authority"));executor = Executors.newFixedThreadPool(serverThreadPoolSize);sslContext = Auth.getSSLContext();sslServerSocketFactory = sslContext.getServerSocketFactory(); //只是创建一个TCP连接,SSL handshake还没开始//客户端或服务端第一次试图获取socket输入流或输出流时,//SSL handshake才会开始sslServerSocket = (SSLServerSocket) sslServerSocketFactory.createServerSocket(); String[] pwdsuits = sslServerSocket.getSupportedCipherSuites();sslServerSocket.setEnabledCipherSuites(pwdsuits);//默认是client mode,必须在握手开始之前调用sslServerSocket.setUseClientMode(false);if(authority.intValue() == 2){//只有设置为server mode,该配置才生效//如果客户端不提供其证书,通信将会结束sslServerSocket.setNeedClientAuth(true);}else{//只有设置为server mode,该配置才生效//即使客户端不提供其证书,通信也将继续sslServerSocket.setWantClientAuth(true);}sslServerSocket.setReuseAddress(true);sslServerSocket.setReceiveBufferSize(128*1024);sslServerSocket.setPerformancePreferences(3, 2, 1);sslServerSocket.bind(new InetSocketAddress(serverListenPort),serverRequestQueueSize);logger.info("Server start up!");logger.info("server port is:"+serverListenPort);}private void service(){while(true){SSLSocket socket = null;try{logger.debug("Wait for client request!");socket = (SSLSocket)sslServerSocket.accept();logger.debug("Get client request!");Runnable job = new Job(socket);executor.execute(job);}catch(Exception e){logger.error("server run exception");try {socket.close();} catch (IOException e1) {e1.printStackTrace();}}}}public static void main(String[] args) {Server server;try{ server = new Server(); server.service();}catch(Exception e){e.printStackTrace();logger.error("server socket establish error!");}}}
服务端业务执行线程:
package com.test.server.business;import java.io.DataInputStream;import java.io.DataOutputStream;import java.io.IOException;import java.net.Socket;import java.util.Properties;import org.apache.log4j.Logger;import com.test.server.config.Configuration;import com.test.tools.SocketIO;public class Job implements Runnable {static Logger logger = Logger.getLogger(Job.class);private Socket socket;public Job(Socket socket){this.socket = socket;}public void run() {Properties p = Configuration.getConfig();String encoding = p.getProperty("socketStreamEncoding");DataInputStream input = null;DataOutputStream output = null;try{input = SocketIO.getDataInput(socket);int length = input.read();byte[] bytes = new byte[length];input.read(bytes);String user = new String(bytes,encoding).trim();int pwd = input.read();String result = null;if(null != user && !user.equals("") && user.equals("name") && pwd == 123){result = "login success";}else{result = "login failed";}logger.info("request user:"+user);logger.info("request pwd:"+pwd);output = SocketIO.getDataOutput(socket);bytes = result.getBytes(encoding);length = (short)bytes.length;output.writeShort(length);output.write(bytes);logger.info("response info:"+result);}catch(Exception e){e.printStackTrace();logger.error("business thread run exception");}finally{try {socket.close();} catch (IOException e) {e.printStackTrace();logger.error("server socket close error");}}}}
配置文件类:
package com.test.server.config;import java.io.File;import java.io.FileInputStream;import java.io.InputStream;import java.util.Properties;import org.apache.log4j.Logger;public class Configuration {private static Properties config;static Logger logger = Logger.getLogger(Configuration.class);public static Properties getConfig(){try{if(null == config){File configFile = new File("./conf/conf.properties");if(configFile.exists() && configFile.isFile()&& configFile.canRead()){InputStream input = new FileInputStream(configFile);config = new Properties();config.load(input);}}}catch(Exception e){//default setconfig = new Properties();config.setProperty("protocol", "TLSV1");config.setProperty("serverCer", "./certificate/server.jks");config.setProperty("serverCerPwd", "1234sp");config.setProperty("serverKeyPwd", "1234kp");config.setProperty("serverTrustCer", "./certificate/serverTrust.jks");config.setProperty("serverTrustCerPwd", "1234sp");config.setProperty("clientCer", "./certificate/client.jks");config.setProperty("clientCerPwd", "1234sp");config.setProperty("clientKeyPwd", "1234kp");config.setProperty("clientTrustCer", "./certificate/clientTrust.jks");config.setProperty("clientTrustCerPwd", "1234sp");config.setProperty("serverListenPort", "10000");config.setProperty("serverThreadPoolSize", "5");config.setProperty("serverRequestQueueSize", "10");config.setProperty("socketStreamEncoding", "UTF-8");}return config;}}
接口工具类:
package com.test.tools;import java.io.DataInputStream;import java.io.DataOutputStream;import java.io.IOException;import java.net.Socket;public class SocketIO{public static DataInputStream getDataInput(Socket socket) throws IOException{DataInputStream input = new DataInputStream(socket.getInputStream());return input;}public static DataOutputStream getDataOutput(Socket socket) throws IOException{DataOutputStream out = new DataOutputStream(socket.getOutputStream());return out;}}
握手工具类:
package com.test.tools;import javax.net.ssl.HandshakeCompletedEvent;import javax.net.ssl.HandshakeCompletedListener;public class MyHandshakeCompletedListener implements HandshakeCompletedListener {public void handshakeCompleted(HandshakeCompletedEvent arg0) {System.out.println("Handshake finished successfully");}}
配置文件:
#1:单向认证,只有服务器端需证明其身份 #2:双向认证,服务器端和客户端都需证明其身份authority=2#通信协议protocol=TLSV1#服务端证书信息serverCer=./certificate/server.jks#keystore的storepassserverCerPwd=1234sp#keystore的keypassserverKeyPwd=1234kp#服务端证书信息serverTrustCer=./certificate/serverTrust.jksserverTrustCerPwd=1234sp#客户端证书信息clientCer=./certificate/client.jksclientCerPwd=1234spclientKeyPwd=1234kpclientTrustCer=./certificate/clientTrust.jksclientTrustCerPwd=1234sp#服务器监听端口,注意root权限serverListenPort=10000#服务器线程池线程数(2*核数+1)serverThreadPoolSize=5#服务器Socket请求队列长度 serverRequestQueueSize=10#字节流编码 socketStreamEncoding=utf-8
日志文件:
log4j.rootLogger=debug,logOutput,fileLogOutputlog console out put log4j.appender.logOutput=org.apache.log4j.ConsoleAppenderlog4j.appender.logOutput.layout=org.apache.log4j.PatternLayoutlog4j.appender.logOutput.layout.ConversionPattern=%p%d{[yy-MM-dd HH:mm:ss]}[%c] -> %m%n#log file out putlog4j.appender.fileLogOutput=org.apache.log4j.RollingFileAppenderlog4j.appender.fileLogOutput.File=./log/server.loglog4j.appender.fileLogOutput.MaxFileSize=1000KBlog4j.appender.fileLogOutput.MaxBackupIndex=3log4j.appender.fileLogOutput.layout=org.apache.log4j.PatternLayoutlog4j.appender.fileLogOutput.layout.ConversionPattern=%p%d{[yy-MM-dd HH:mm:ss]}[%c] -> %m%n
运行后的结果:
客户端:
Handshake finished successfullyINFO[15-02-03 09:53:49][com.test.client.Client] -> request result:login success
服务端:
INFO[15-02-03 09:53:45][com.test.server.Server] -> Server start up!INFO[15-02-03 09:53:45][com.test.server.Server] -> server port is:10000DEBUG[15-02-03 09:53:45][com.test.server.Server] -> Wait for client request!DEBUG[15-02-03 09:53:48][com.test.server.Server] -> Get client request!DEBUG[15-02-03 09:53:48][com.test.server.Server] -> Wait for client request!INFO[15-02-03 09:53:49][com.test.server.business.Job] -> request user:nameINFO[15-02-03 09:53:49][com.test.server.business.Job] -> request pwd:123INFO[15-02-03 09:53:49][com.test.server.business.Job] -> response info:login success
PS:
1,不能随意的使用close()方法关闭socket输入输出流,使用close()方法关闭socket输入输出流会导致socket本身被关闭
2,字符串必须按照指定的编码转换为字节数组,字节数组也必须通过相同的编码转换为字符串,否则将会出现乱码
String[] pwdsuits = socket.getSupportedCipherSuites();socket.setEnabledCipherSuites(pwdsuits);
加密套件(ciphersuits)包括一组加密参数,这些参数指定了加密算法、密钥长度等加密等信息。
[SSL_RSA_WITH_RC4_128_MD5, SSL_RSA_WITH_RC4_128_SHA, TLS_RSA_WITH_AES_128_CBC_SHA, ...
- Java Secure(SSL/TLS) Socket实现
- TLS/SSL Socket 实现
- SSL (Secure Socket Layer)
- SSL (Secure Socket Layer)
- SSL (Secure Socket Layer)
- SSL (Secure Socket Layer)
- SSL (Secure Socket Layer)
- 基于Java Socket实现的SMTP邮件客户端 - 全面支持SSL, TLS
- SSL/TLS Client Certs to Secure MQTT
- SSL/TLS的Java实现--JSSE
- SSL/TLS的Java实现--JSSE
- SSL/TLS的Java实现--JSSE
- sth. about SSL (secure socket layer), JSSE (java secure socket extension)
- 基于 MINA 的 TLS/SSL NIO Socket 实现(二)
- SSL(Secure socket layer)安全技术
- SSL(Secure Socket Layer) 介绍
- SSL(Secure Socket Layer)
- SSL(Secure Socket Layer,SSL)工作原理
- 初涉XML读写,配合<记事五行农历XML通用版>的体会
- VSTO 取得Excel有内容的最后一行
- Pro Android学习笔记(二五):用户界面和控制(13):LinearLayout和TableLayout
- javaWEB session
- 总结2012EMC技术支持面试题
- Java Secure(SSL/TLS) Socket实现
- MFC ODBC数据库操作编程(二)
- JavaScript 的历史
- 为什么在SharePoint中使用FormDigest控件
- windows XP 安装pip
- 创建 PHP 函数
- JNI_Java Native Interface
- C++中虚函数工作原理和(虚)继承类的内存占用大小计算
- 收集了一些XSS攻击平台