`
cherubim.chen
  • 浏览: 32661 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

java client访问https server(客户端代码、服务器端配置)

阅读更多
openssl配置证书时直接配置crt、key即可
而jsse通过keystore来存储证书、证书链信息,java客户端通过jsse来使用ssl
具体openssl和keytool各种证书类型之间的转换可以参考“收藏”中一些文章

cfca颁发的pfx个人证书需要转换成keystore格式才能使用
从cfca获得如下测试证书:
个人证书:cfca_client.pfx
服务器证书:cfca_server.crt、cfca_server.key
根证书:CFCA_RCA.cer(一级根证书)、cfca_root.crt(二级根证书)

以下以上述cfca的证书为例,其实也可以自己用openssl生成自己的ca自己的服务器、客户端证书进行测试
服务器证书和个人证书都从同一个ca签发,生成keystore时只需要分别把根证书、服务器证书生成服务器端的keystore文件;根证书、客户端个人证书生成客户端的keystore文件,即可完成证书配置,而不需要将服务器端证书加入客户端的keystore文件,也不需要将客户端证书加入服务器端的keystore文件,他们之间通过相同的根证书来建立互相信任,这样客户端、服务器端的keyStore和trustStore都分别指向一个jks文件即可
但是,如果客户端、服务器端根证书不一致,就需要在客户端新建一个可信任的keystore文件信任服务器端的根证书,在服务器端另建一个可信任的keystore文件信任客户端的根证书,配置时分别陪在trustStore中,而自己的证书作为keyStrore配置

一、服务器端配置
1、如果通过apache做负载均衡,只需要配置在apache上即可
apache ssl的配置参考文章《rhel5+apache+jboss+ssl》
将上述证书文件复制到服务器目录下,修改$APACHE_HOME/conf/extra/httpd-ssl.conf
SSLCertificateFile "/opt/apache/conf/cfca_server.crt"
#SSLCertificateFile "/opt/apache/conf/server.crt"
#SSLCertificateFile "/opt/apache/conf/server-dsa.crt"

#   Server Private Key:
#   If the key is not combined with the certificate, use this
#   directive to point at the key file.  Keep in mind that if
#   you've both a RSA and a DSA private key you can configure
#   both in parallel (to also allow the use of DSA ciphers, etc.)
SSLCertificateKeyFile "/opt/apache/conf/cfca_server.key"
#SSLCertificateKeyFile "/opt/apache/conf/server.key"
#SSLCertificateKeyFile "/opt/apache/conf/server-dsa.key"

#   Server Certificate Chain:
#   Point SSLCertificateChainFile at a file containing the
#   concatenation of PEM encoded CA certificates which form the
#   certificate chain for the server certificate. Alternatively
#   the referenced file can be the same as SSLCertificateFile
#   when the CA certificates are directly appended to the server
#   certificate for convinience.
#SSLCertificateChainFile "/opt/apache/conf/CFCA_RCA.cer

#   Certificate Authority (CA):
#   Set the CA certificate verification path where to find CA
#   certificates for client authentication or alternatively one
#   huge file containing all of them (file must be PEM encoded)
#   Note: Inside SSLCACertificatePath you need hash symlinks
#         to point to the certificate files. Use the provided
#         Makefile to update the hash symlinks after changes.
SSLCACertificatePath "/opt/apache/conf"
#SSLCACertificateFile "/opt/apache/conf/ca.crt"
SSLCACertificateFile "/opt/apache/conf/cfca_root.crt"

2、如果不通过apache,有后端的应用服务器暴露ssl端口,那就需要在应用服务器上进行配置,以tomcat为例:
2.1 修改$TOMCAT_HOME/conf/server.xml,tomcat支持jsse模式即配置keystore:
    <Connector port="8443" protocol="HTTP/1.1" SSLEnabled="true"
               maxThreads="150" scheme="https" secure="true"
               clientAuth="true" sslProtocol="TLS"
               keystoreFile="/home/cherubim/cert/cfca/cfcakeystore_server.jks"  keystorePass="xxxxx"
               truststoreFile="/home/cherubim/cert/cfca/cfcakeystore_server.jks" truststorePass="xxxxx"/>

tcServerKeystore.jks文件时根据上述cfca证书生成的,具体参考后面内容

2.2 tomcat也支持APR connector,按照openssl的方式直接配置证书(类似apache上的配置):
<Connector 
           port="8443" maxThreads="200"
           scheme="https" secure="true" SSLEnabled="true"
           SSLCertificateFile="/home/cherubim/cert/cfca/cfca_server.crt" 
           SSLCertificateKeyFile=/home/cherubim/cert/cfca/cfca_server.key"
           clientAuth="optional" SSLProtocol="TLSv1"/>

tomcat如何启用APR,参考tomcat文档:http://tomcat.apache.org/tomcat-7.0-doc/ssl-howto.html

二、将pfx、crt、key等证书、密钥文件转换为keystore文件
需要用到openssl(linux下需要安装openssl包)
还要用到keytool(在$JAVA_HOME/jre/bin下)
1、客户端:将pfx证书转换为keystore
1.1 cfca的二级根证书文件时ascii格式的需要转换(可以用文本编辑器打开,是两段加密字符串):
用以下命令将ca根证书转换成DER格式
openssl x509 -in cfca_root.crt -out cfca_root.der -outform DER


1.2 用以下命令将DER格式的根证书导入keystore
keytool -import -alias cfca_rca -keystore cfcakeystore_client.jks -import -trustcacerts -file CFCA_RCA.cer
keytool -import -alias cfca_root -keystore cfcakeystore_client.jks -import -trustcacerts -file cfca_root.der


注:一级根证书、二级根证书以及后续的pfx个人证书导入顺序似乎没有要求,但最好还是按照先一级、再二级等顺序来导吧,生成keystore时会要求输入密码,这个密钥要记住,后续编码要用到

(如果不行在第一条命令中将格式改为PEM:...cfca_root.pem -outform PEM)

1.3 用以下命令将个人证书导入keystore
keytool -importkeystore -v  -srckeystore cfca_client.pfx -srcstoretype pkcs12 -srcstorepass 123456 -destkeystore cfcakeystore_client.jks -deststoretype jks 


注:pfx个人证书一般带有保护密码,如上述的123456,那么在将pfx导入到keystore中时,kestore的保护密码(第二步中第一次生成keystore时会要求输入密码)必须与这个密码一致,否则在后续客户端代码从pfx中获取个人证书时会报“can not recover key”的异常(这样就限死了keystore的密码必须与pfx的初始密码一致)
如果想自己定义keystore的密码,可以先将pfx文件导入到ie(导入时选择允许导出私钥),再从ie中导出(选择导出私钥),再选择pfx格式,其他不要选,导出时定义一个自己的密码,如“xxxxx”,那么在生成自己的keystore时就可以继续用这个密码
在ie中导出pfx时,有个选项“如果可能,则数据包包含证书路径的所有证书”,如果选了这个pfx中将包含根证书信息,但如果直接将这个pfx导成keystore文件,keystore文件中格式似乎不对,根证书在这个jks文件中不是一个独立的entry,用程序去访问也会报错!最好的方式还是按照上述步骤一步步生成keystore文件

keystore文件已生成,可以用工具查看其中的内容:
>keytool -list -v -keystore d:\cfcakeystore_all.jks

可以看到3个entry,两个“trustedCertEntry”和一个“PrivateKeyEntry”

1.4 将上述jks文件作为客户端的keystore和trustedstore试试

2、服务器端:如果要配置tomcat使用jsse的keystore格式,需要将服务器证书、key文件合并生成pfx文件,再按上述步骤生成keystore文件
openssl pkcs12 -export -in cfca_server.crt -inkey cfca_server.key -out cfca_server.p12 -name cfca_server 
openssl x509 -in cfca_root.crt -out cfca_root.der -outform DER
keytool -import -alias cfca_rca -keystore cfcakeystore_server.jks -import -trustcacerts -file CFCA_RCA.cer
keytool -import -alias cfca_root -keystore cfcakeystore_server.jks -import -trustcacerts -file cfca_root.der
keytool -importkeystore -v  -srckeystore cfca_server.p12 -srcstoretype pkcs12 -srcstorepass xxxxx -destkeystore cfcakeystore_server.jks -deststoretype jks


三、客户端测试代码

package test;

/**
 * <p>Title: </p>
 * <p>Description: </p>
 * <p>Copyright: Copyright (c) 2005</p>
 * <p>Company: </p>
 * @author not attributable
 * @version 1.0
 */

import java.net.*;
import java.io.InputStream;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Map;
import java.util.Iterator;
import java.io.*;

import javax.net.ssl.HttpsURLConnection;
public class TestHttp {
  public TestHttp() {
  }
  public static void main(String[] args) {
    //TestHttp testHttp1 = new TestHttp();
    try{
//    	System.setProperty("ssl.provider", "com.sun.net.ssl.internal.ssl.Provider ");
//    	System.setProperty("ssl.pkgs", "com.sun.net.ssl.internal.www.protocol");
    	System.setProperty("javax.net.ssl.keyStore","d:\\cfcakeystore_client.jks");
    	System.setProperty("javax.net.ssl.keyStorePassword","xxxxx"); 
    	System.setProperty("javax.net.ssl.trustStore","d:\\cfcakeystore_client.jks"); 
    	System.setProperty("javax.net.ssl.trustStorePassword","xxxxx"); 
      URL url = new URL("https://xx.xxx.com:8443/docs");
//      URL url = new URL("http://localhost:7001/test/newpageflow1/Newpageflow1Controller.jpf");
      HttpsURLConnection  uc=(HttpsURLConnection)url.openConnection();

      System.out.println(url.toExternalForm());
      //BufferedReader reader=new BufferedReader(new InputStreamReader(url.openStream()));
      System.out.println("length"+uc.getContentLength());
      System.out.println("/////////////"+uc.getContentType());
      Map map=uc.getHeaderFields();
      Iterator it=map.entrySet().iterator();
      while(it.hasNext()){
        System.out.println(it.next());
      }
      System.out.println("========"+uc.getHeaderField("Content-disposition"));
      System.out.println(uc.getContent());
      File file=new File("c:/xx.dat");
      FileOutputStream fo=new FileOutputStream(file);
      InputStream input=uc.getInputStream();
      // ���������������
      BufferedInputStream bis=null;
      BufferedOutputStream bos=null;
      try {
          // ���������������
          bis=new BufferedInputStream(uc.getInputStream());
          bos=new BufferedOutputStream(fo);
          // ���� Buffer
          byte[] buff=new byte[1024];
          int bytesRead;
          int total=0;
          PrintWriter pw=new PrintWriter(System.out);
          // ��/дѭ����
          while(-1!=(bytesRead=bis.read(buff,0,buff.length))) {
              bos.write(buff,0,bytesRead);
              bos.flush();
              total+=bytesRead;

              //pw.write(total+" bytes readed");
              pw.flush();
          }
      } catch(IOException e){
  //        logger.debug("IOException. \n"+e.getMessage());
          throw e;
      } finally{
          if(bis!=null) bis.close();
          if(bos!=null) bos.close();
      }
//
//      do{
//        tempStr=reader.readLine();
//        System.out.println(tempStr);
//      }while(tempStr!=null);
    }catch(Exception e){
      e.printStackTrace();
    }
  }

}


package test;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.KeyStore;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.client.DefaultHttpClient;

public class TestHttps {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		DefaultHttpClient httpclient = new DefaultHttpClient();
		FileInputStream instream = null;
		FileInputStream instream2 = null;
		KeyStore trustStore;
		KeyStore keyStore;
		try {
			trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
			keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
			instream = new FileInputStream(new File("d:\\cfcakeystore_client.jks"));
			instream2 = new FileInputStream(new File("d:\\cfcakeystore_client.jks"));	
			trustStore.load(instream, "xxxxx".toCharArray());
			keyStore.load(instream2, "xxxxx".toCharArray());
			SSLSocketFactory socketFactory = new SSLSocketFactory(keyStore,
					"xxxxx", trustStore);
			Scheme sch = new Scheme("https", socketFactory, 8443);
			httpclient.getConnectionManager().getSchemeRegistry().register(sch);

			HttpGet httpget = new HttpGet("https://xx.xxx.com:8443/docs");

			System.out.println("executing request" + httpget.getRequestLine());

			HttpResponse response = httpclient.execute(httpget);
			HttpEntity entity = response.getEntity();

			System.out.println("----------------------------------------");
			System.out.println(response.getStatusLine());
			if (entity != null) {
				System.out.println("Response content length: "
						+ entity.getContentLength());
//				entity.getContent().
				entity.consumeContent();
			}
			httpclient.getConnectionManager().shutdown();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				instream.close();
				instream2.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

	}
}



四、客户端代码troubleshooting
1、程序中如果keystore密码输错,将会出现异常
2、如果生成keystore时,keystore的保护密码和被导入的pfx的保护密码不一致将出现异常
3、如果服务器端的证书的cn与服务器的dns或者ip不一致,程序将报错:
java.security.cert.CertificateException: No subject alternative names present

可以修改windows\system32\drivers\etc\hosts文件,如:
172.17.249.48 xx.xxx.com

xx.xxx.com与服务器证书的cn一致

客户端代码程序访问时,url也必须写成:
URL url = new URL("https://xx.xxx.com:8443/docs");

而不能用ip地址替代xx.xxx.com,但是在第二段代码中ip和域名都可以,为什么会这样懒得深究了!
4、如果服务器的证书过期,可以修改客户端机器的时间

老革命碰到新问题,在另一台机器上测试出现以下异常(用第一段代码,使用jdk自带的HttpsURLConnection访问,用apache的httpclient没试过):
javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Alerts.java:174)
at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Alerts.java:136)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.recvAlert(SSLSocketImpl.java:1657)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:932)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readDataRecord(SSLSocketImpl.java:746)
at com.sun.net.ssl.internal.ssl.AppInputStream.read(AppInputStream.java:75)
at java.io.BufferedInputStream.fill(BufferedInputStream.java:218)
at java.io.BufferedInputStream.read1(BufferedInputStream.java:258)
at java.io.BufferedInputStream.read(BufferedInputStream.java:317)
at sun.net.www.http.HttpClient.parseHTTPHeader(HttpClient.java:687)
at sun.net.www.http.HttpClient.parseHTTP(HttpClient.java:632)
at sun.net.www.http.HttpClient.parseHTTP(HttpClient.java:652)
at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1000)
at java.net.HttpURLConnection.getResponseCode(HttpURLConnection.java:373)
at sun.net.www.protocol.https.HttpsURLConnectionImpl.getResponseCode(HttpsURLConnectionImpl.java:318)
at com.stf.action.TestCert.main(TestCert.java:34)


在网上找了一下,原来是TLS的Renegotiation 的问题:
参考:http://www.oracle.com/technetwork/java/javase/documentation/tlsreadme2-176330.html
http://httpd.apache.org/docs/trunk/mod/mod_ssl.html#sslinsecurerenegotiation

有两种解决方案(都测试通过)
1、修改apache的配置加上:SSLInsecureRenegotiation on
但这样似乎不安全:were vulnerable to a Man-in-the-Middle attack (CVE-2009-3555) during a renegotiation

2、将java端的jre升级,参考oralce的文档jdk1.6的话要升级到update22以上

分享到:
评论
2 楼 linyfei 2012-05-07  
您好,我想问下,上述文中提到:
有两种解决方案(都测试通过)
1、修改apache的配置加上:SSLInsecureRenegotiation on
但这样似乎不安全:were vulnerable to a Man-in-the-Middle attack (CVE-2009-3555) during a renegotiation

2、将java端的jre升级,参考oralce的文档jdk1.6的话要升级到update22以上

关于第一种,我想问下,修改apache的配置指的是修改apache的哪个配置文件?具体位置可以帮忙指明一下吗?谢谢!


1 楼 hwj_wj123 2012-03-15  
你好,我看了您的这篇“java client访问https server(客户端代码、服务器端配置) ”,我现在也在实现java client访问https的功能,一直提示no_certification,困惑了好几天,还是不能解决,能指点一下吗。
我的处理步骤:
1.从网页上保存出证书,cer文件。
2.将导出的证书导入到jre/lib/security下的cacerts
3.
===========================简化后的相关代码==================
System.setProperty("javax.net.ssl.keyStore","d:\\...cacerts");  
System.setProperty("javax.net.ssl.keyStorePassword","xxxxx");  

HttpURLConnection connection = null;
InputStream in = null;
URL url = new URL(srcUrl);
connection = (HttpURLConnection) url.openConnection();
in = connection.getInputStream();
到这里就会出错,提示没有证书。这种方式可以实现getinputstream吗?

相关推荐

    Java 实现的FTP服务器与客户端

    用Java自己的写FTP服务器与客户端。 Control和Data双线程,数据传输时开20口,21口进行侦听与Control连接 支持USER, PASS, RETR, STOR, ABOR, QUIT 5个口令。用户验证没加入,需要的自行加入。

    SocketIO包括客户端和服务器端代码

    Usage example: 1. Build or install Netty-socketio lib to your maven repository. `mvn clean install` ... `mvn exec:java` 4. Run client in browser, by opening* file /client/index.html

    java服务器 java机器人 unity3D客户端.rar

    Netty Client/Server 实现网络层的客户端与服务器通信 Spring Shell 用于开发命令窗口,模拟客户端操作 Arthas 实现不重启服务器热更新代码 Logback 日志框架 MongoDB 数据库 lombok 用于自动生成类的Getter和Setter...

    ssl证书(nginx+tomcat+java代码适用)

    ssl自制全套证书(包含服务器端、客户端、ca端的证书,格式有.crt,.key,.truststore,.keystore,.p12,.cer,.pem等类型),当时要配置...client代表客户端的证书,server代表服务器端的证书,ca代表三方公正机构。

    java聊天室小程序+源代码

    今天把以前忘记的又看了一遍,这是一个java开发的聊天小程序,分为客户端和服务器端,我都是在本机上搞的,如果有局域网就把里面的client的IP改成运行server.jar的机子的IP,然后再打个jar包就可以了。运行时,必须...

    JAVA上百实例源码以及开源项目源代码

     Tcp服务端与客户端的JAVA实例源代码,一个简单的Java TCP服务器端程序,别外还有一个客户端的程序,两者互相配合可以开发出超多的网络程序,这是最基础的部分。 递归遍历矩阵 1个目标文件,简单! 多人聊天室 3...

    java源码包---java 源码 大量 实例

     Tcp服务端与客户端的JAVA实例源代码,一个简单的Java TCP服务器端程序,别外还有一个客户端的程序,两者互相配合可以开发出超多的网络程序,这是最基础的部分。 递归遍历矩阵 1个目标文件,简单! 多人聊天室 3...

    java网络白板

    自己写的一个网络白板程序,有客户端和服务器端,服务器实现多线程,并且可以实时显示连接数及连接的ip,客户端实现绘图功能,并把图像发到服务器端,服务器转发给其他客户端。代码是用java写的,采用的基本Server/...

    基于stm32、树莓派,后端使用Java的SpringBoot架构,以微信小程序作为用户控制端的智能家居控制系统,含完整源代码

    socketBOX:(运行在智能音箱上的socket程序) |-- client.h(客户端程序头文件) |-- client.c(客户端程序) |-- server.c (服务器程序) |-- openPiTCP.sh (连接客户端脚本) |-- server (socket服务器可执行文件) |--...

    client/server

    java客户端与服务器端源代码,通过http协议来交互

    java源码包3

     Tcp服务端与客户端的JAVA实例源代码,一个简单的Java TCP服务器端程序,别外还有一个客户端的程序,两者互相配合可以开发出超多的网络程序,这是最基础的部分。 递归遍历矩阵 1个目标文件,简单! 多人聊天室 ...

    Java开发技术大全(500个源代码).

    代码范例列表 第1章 示例描述:本章演示如何开始使用JDK进行程序的开发。 HelloWorldApp.java 第一个用Java开发的应用程序。 firstApplet.java 第一个用Java开发的Applet小程序。 firstApplet.htm 用来装载...

    一个我自己学习Erlang的聊天室服务器及客户端代码

    里面包含Server端Erlang代码和Client端JAVA代码。编写过程在我博客里。

    CS结构的TCP服务器-Java代码.rar

    CS结构的TCP服务器-Java代码,TCP服务器端代码如下:  public class SocketServerDemo{  int port=2345; //端口号  ServerSocket serverSocket; //服务器套接字  public SocketServerDemo(){  try{   ...

    JAVA上百实例源码以及开源项目

     Tcp服务端与客户端的JAVA实例源代码,一个简单的Java TCP服务器端程序,别外还有一个客户端的程序,两者互相配合可以开发出超多的网络程序,这是最基础的部分。 递归遍历矩阵 1个目标文件,简单! 多人聊天室 3...

    疯狂Java讲义(第三版)-项目源代码

    疯狂Java讲义第三版,随书光盘项目源码,总共... fivechess-server: 第15章 五子棋游戏大厅服务器端模块 fivechess-client: 第15章 五子棋游戏大厅客户端模块 fivechess-commons: 第15章 五子棋游戏大厅公用模块

    Java-Gui-Server-and-Client:通过套接字进行通信的 Java 代码示例

    服务器端 司机 从这里开始。 包含图形用户界面 中继服务器包含后端代码 客户端中继 司机 从这里开始。 包含图形用户界面 中继客户端包含后端代码 I am new to programming, and know that this program does not ...

    java源码包2

     Tcp服务端与客户端的JAVA实例源代码,一个简单的Java TCP服务器端程序,别外还有一个客户端的程序,两者互相配合可以开发出超多的网络程序,这是最基础的部分。 递归遍历矩阵 1个目标文件,简单! 多人聊天室 ...

Global site tag (gtag.js) - Google Analytics