您的位置:首页 > 理论基础 > 计算机网络

java 通过SSL/TLS加密https建立连接

2017-04-12 19:22 531 查看
这个项目是基于与Ruby客户端进行通信,经过查看源代码,发现对方是经过TLS加密通信。第一次调试,尝试用serversocket与对方进行沟通,可以收到数据,但是无法获取http的包头数据,因此,将先对socket通信进行处理。
一、根据原有软件进行沟通,可以找到原来生成的rcs-db.key、rcs-db.crt文件,可知rcs-db.crt为证书,rcs-db.key为秘钥。因为并没有任何思路来处理这两个文件,我就先打开看一看。发现秘钥的内容如下:

-----BEGIN RSA PRIVATE KEY-----
MIICWwIBAAKBgQDBajNHgexQbClBGOxJgTkevfZU4R/OmI2BZUgt3DD4RNO4cXtG
...........................................................................
-----END RSA PRIVATE KEY-----


可知使用的是RSA公钥加密算法。并且根据源码可知,是用OpenSSL加密的。开始我就根据http://blog.csdn.net/chaijunkun/article/details/7275632/这篇文章所讲将公钥和私钥都解了出来,可是并不知道怎么去使用。

然后查了一下,要通过一下步骤可以将key和certificate应用在Java里面。

安装好openssl。

首先将.key、crt文件转换成.jks文件及一对公私钥存储在新建的PKCS12 keystore中。

# Create PKCS12 keystore from private key and public certificate.
openssl pkcs12 -export -name myservercert -in selfsigned.crt -inkey server.key -out keystore.p12

# Convert PKCS12 keystore into a JKS keystore
keytool -importkeystore -destkeystore mykeystore.jks -srckeystore keystore.p12 -srcstoretype pkcs12 -alias myservercert

然后可以用这个命令来查看keystore中的jks。注意这里我们将jks的别名起为myservercert。crt,p12记得加上地址。

keytool -list -v -keystore mykeystore.jks

但是要保证是自签证的证书,否则需要按此步骤顺着证书链找到可信赖的证书。
相关概念:Keytool 是一个有效的安全钥匙和证书的管理工具。Java
中的 keytool.exe (位于 JDK\Bin 目录下)可以用来创建数字证书,所有的数字证书是以一条一条(采用别名区别)的形式存入证书库的中,证书库中的一条证书包含该条证书的私钥,公钥和对应的数字证书的信息。证书库中的一条证书可以导出数字证书文件,数字证书文件只包括主体信息和对应的公钥。 Keytool
把钥匙和证书储存到一个keystore.默任的实现keystore的是一个文件.它用一个密码保护钥匙。

因为是需要双向认证的,因此我们需要生成truststore。JSSE使用Truststore和Keystore文件来提供客户端和服务器之间的安全数据传输。keytool是一个工具可以用来创建包含公钥和密钥的的keystore文件,并且利用keystore文件来创建只包含公钥的truststore文件。我们通过下面的5步简单的创建truststore和keystore文件: 

    生成一个含有一个私钥的keystore文件 

    验证新生成的keystor而文件 

    导出凭证文件 

    把认凭证件导入到truststore文件 

    验证新创建的truststore文件 

导出凭证文件 在这一步,你可以导出自我签署凭证或是Verisign或其他的认证机构的商业凭证的。这里只说导出自我签署的凭证: 通过执行下面的命令把自我签署的凭证保存到 “selfsignedcert.cer”文件 :

keytool -export -alias certificatekey -keystore keystore.jks -rfc -file selfsignedcert.cer
把认凭证件导入到truststore文件 执行下面的命令:

keytool -import -alias myservercert -file selfsignedcert.cer -keystore truststore.jks
最后验证新创建的truststore文件 执行下面的命令 :
keytool -list -v -keystore truststore.jks

二、至此,我们已经将Truststore和Keystore全部倒入keytool中了。因为我需要解析包头。全部的代码如下:
package com.cms.listener;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLDecoder;
import java.security.KeyStore;
import java.util.ArrayList;
import java.util.StringTokenizer;

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;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

/**
* Application Lifecycle Listener implementation class WorkerListener
*
*/
public class WorkerListener implements ServletContextListener {

//调用线程池
ApplicationContext ctx =  new ClassPathXmlApplicationContext("config/spring/applicationContext.xml");
ThreadPoolTaskExecutor taskExecutor = (ThreadPoolTaskExecutor)ctx.getBean("taskExecutor");
//定义端口号
public static final int SERVER_PORT = 442;

/**
* @see ServletContextListener#contextDestroyed(ServletContextEvent)
*/
public void contextDestroyed(ServletContextEvent arg0)  {
// TODO Auto-generated method stub
}

/**
* @see ServletContextListener#contextInitialized(ServletContextEvent)
*/
public void contextInitialized(ServletContextEvent arg0)  {
// TODO Auto-generated method stub

try {
String serverKeyStoreFile = "c:\\Windows\\System32\\mykeystore.jks";
String serverKeyStorePwd = "changeit";
String catServerKeyPwd = "changeit";
String serverTrustKeyStoreFile = "c:\\RCS\\DB\\config\\certs\\truststore.jks";
String serverTrustKeyStorePwd = "changeit";

KeyStore serverKeyStore = KeyStore.getInstance("JKS");
serverKeyStore.load(new FileInputStream(serverKeyStoreFile), serverKeyStorePwd.toCharArray());

KeyStore serverTrustKeyStore = KeyStore.getInstance("JKS");
serverTrustKeyStore.load(new FileInputStream(serverTrustKeyStoreFile), serverTrustKeyStorePwd.toCharArray());

KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(serverKeyStore, catServerKeyPwd.toCharArray());

TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(serverTrustKeyStore);

SSLContext sslContext = SSLContext.getInstance("TLSv1");
//第二个参数TrustManager[] 是认证管理器,在需要双向认证时使用
sslContext.init(kmf.getKeyManagers(), t
4000
mf.getTrustManagers(), null);

SSLServerSocketFactory sslServerSocketFactory = sslContext.getServerSocketFactory();
SSLServerSocket sslServerSocket = (SSLServerSocket) sslServerSocketFactory.createServerSocket(SERVER_PORT);
//sslServerSocket.setNeedClientAuth(true);

while (true) {
SSLSocket s = (SSLSocket)sslServerSocket.accept();
Accepter accepter = new Accepter(s);
accepter.service();

}
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
}

class Accepter {
private SSLSocket socket;

public Accepter(SSLSocket socket) {
this.socket = socket;
System.out.println("连接到服务器的用户:" + socket);
}

public void service() {
taskExecutor.execute(new Runnable() {
public void run() {
//这里编写处理业务代码
synchronized (this) {
try {
// 第一阶段: 打开输入流
InputStream is = socket.getInputStream();

System.out.println("客户端发送的请求信息: >>>>>>>>>>>>>>>>>>>>>>>>>");
// 读取第一行, 请求地址
String line = readLine(is, 0);
//打印请求行
System.out.print(line);
// < Method > < URL > < HTTP Version > <\r\n>  取的是URL部分
String line2 = line;
String httpversion = line2.substring(line2.length()-10).trim();
httpversion = URLDecoder.decode(httpversion, "UTF-8");//反编码 URL 地址

String resource = line.substring(line.indexOf('/'), line.lastIndexOf('/') - 5);
//获得请求的资源的地址
resource = URLDecoder.decode(resource, "UTF-8");//反编码 URL 地址
String method = new StringTokenizer(line).nextElement().toString();// 获取请求方法, GET 或者 POST
int contentLength = 0;//如果为POST方法,则会有消息体长度

// 读取所有浏览器发送过来的请求参数头部信息
do {
line = readLine(is, 0);
//如果有Content-Length消息头时取出
if (line.startsWith("Content-Length")) {
contentLength = Integer.parseInt(line.split(":")[1].trim());
}
//打印请求部信息
System.out.print(line);
//如果遇到了一个单独的回车换行,则表示请求头结束
} while (!line.equals("\r\n"));
//如果是POST请求,则有请求体
if ("POST".equalsIgnoreCase(method)) {
//注,这里只是简单的处理表单提交的参数,而对于上传文件这里是不能这样处理的,
//因为上传的文件时消息体不只是一行,会有多行消息体
System.out.print(readLine(is, contentLength));
System.out.println();
}
System.out.println("客户端发送的请求信息结束 <<<<<<<<<<<<<<<<<<<<<<<<<<");
System.out.println("用户请求的资源是(uri):" + resource);
System.out.println("请求的类型是: " + method);
System.out.println("请求的http版本是: " + httpversion);
System.out.println("连接到服务器的用户:" + socket.getRemoteSocketAddress());

} catch (Exception e) {
// replace with other code
e.printStackTrace();
}
}
}
});
}
}
/*   这里我们自己模拟读取一行,因为如果使用API中的BufferedReader时,它是读取到一个回车换行后
才返回,否则如果没有读取,则一直阻塞,这就导致如果为POST请求时,表单中的元素会以消息体传送,
这时,消息体最末按标准是没有回车换行的,如果此时还使用BufferedReader来读时,则POST提交
时会阻塞。如果是POST提交时我们按照消息体的长度Content-Length来截取消息体,这样就不会阻塞 */

private String readLine(InputStream is, int contentLe) throws IOException {
ArrayList lineByteList = new ArrayList();
byte readByte;
int total = 0;
if (contentLe != 0) {
do {
readByte = (byte) is.read();
lineByteList.add(Byte.valueOf(readByte));
total++;
} while (total < contentLe);//消息体读还未读完
} else {
do {
readByte = (byte) is.read();
lineByteList.add(Byte.valueOf(readByte));
} while (readByte != 10);
}

byte[] tmpByteArr = new byte[lineByteList.size()];
for (int i = 0; i < lineByteList.size(); i++) {
tmpByteArr[i] = ((Byte) lineByteList.get(i)).byteValue();
}
lineByteList.clear();

String tmpStr = new String(tmpByteArr, "UTF-8");
/*       http请求的header中有一个Referer属性,这个属性的意思就是如果当前请求是从别的页面链接过
来的,那个属性就是那个页面的url,如果请求的url是直接从浏览器地址栏输入的就没有这个值。得
到这个值可以实现很多有用的功能,例如防盗链,记录访问来源以及记住刚才访问的链接等。另外,浏
览器发送这个Referer链接时好像固定用UTF-8编码的,所以在GBK下出现乱码,我们在这里纠正一下 */

if (tmpStr.startsWith("Referer")) {//如果有Referer头时,使用UTF-8编码
tmpStr = new String(tmpByteArr, "UTF-8");
}
return tmpStr;
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐