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

来源:互联网 发布:python3防止sql注入 编辑:程序博客网 时间:2024/06/04 18:35

这个项目是基于与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 keystorekeytool -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(), tmf.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;     }  }

0 0