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 
Java代码   收藏代码
  1. SSLCertificateFile "/opt/apache/conf/cfca_server.crt"  
  2. #SSLCertificateFile "/opt/apache/conf/server.crt"  
  3. #SSLCertificateFile "/opt/apache/conf/server-dsa.crt"  
  4.   
  5. #   Server Private Key:  
  6. #   If the key is not combined with the certificate, use this  
  7. #   directive to point at the key file.  Keep in mind that if  
  8. #   you've both a RSA and a DSA private key you can configure  
  9. #   both in parallel (to also allow the use of DSA ciphers, etc.)  
  10. SSLCertificateKeyFile "/opt/apache/conf/cfca_server.key"  
  11. #SSLCertificateKeyFile "/opt/apache/conf/server.key"  
  12. #SSLCertificateKeyFile "/opt/apache/conf/server-dsa.key"  
  13.   
  14. #   Server Certificate Chain:  
  15. #   Point SSLCertificateChainFile at a file containing the  
  16. #   concatenation of PEM encoded CA certificates which form the  
  17. #   certificate chain for the server certificate. Alternatively  
  18. #   the referenced file can be the same as SSLCertificateFile  
  19. #   when the CA certificates are directly appended to the server  
  20. #   certificate for convinience.  
  21. #SSLCertificateChainFile "/opt/apache/conf/CFCA_RCA.cer  
  22.   
  23. #   Certificate Authority (CA):  
  24. #   Set the CA certificate verification path where to find CA  
  25. #   certificates for client authentication or alternatively one  
  26. #   huge file containing all of them (file must be PEM encoded)  
  27. #   Note: Inside SSLCACertificatePath you need hash symlinks  
  28. #         to point to the certificate files. Use the provided  
  29. #         Makefile to update the hash symlinks after changes.  
  30. SSLCACertificatePath "/opt/apache/conf"  
  31. #SSLCACertificateFile "/opt/apache/conf/ca.crt"  
  32. SSLCACertificateFile "/opt/apache/conf/cfca_root.crt"  

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

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

2.2 tomcat也支持APR connector,按照openssl的方式直接配置证书(类似apache上的配置): 
Java代码   收藏代码
  1. <Connector   
  2.            port="8443" maxThreads="200"  
  3.            scheme="https" secure="true" SSLEnabled="true"  
  4.            SSLCertificateFile="/home/cherubim/cert/cfca/cfca_server.crt"   
  5.            SSLCertificateKeyFile=/home/cherubim/cert/cfca/cfca_server.key"  
  6.            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格式 
Java代码   收藏代码
  1. openssl x509 -in cfca_root.crt -out cfca_root.der -outform DER  


1.2 用以下命令将DER格式的根证书导入keystore 
Java代码   收藏代码
  1. keytool -import -alias cfca_rca -keystore cfcakeystore_client.jks -import -trustcacerts -file CFCA_RCA.cer  
  2. 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 
Java代码   收藏代码
  1. 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文件已生成,可以用工具查看其中的内容: 
Java代码   收藏代码
  1. >keytool -list -v -keystore d:\cfcakeystore_all.jks  

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

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

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


三、客户端测试代码 

Java代码   收藏代码
  1. package test;  
  2.   
  3. /** 
  4.  * <p>Title: </p> 
  5.  * <p>Description: </p> 
  6.  * <p>Copyright: Copyright (c) 2005</p> 
  7.  * <p>Company: </p> 
  8.  * @author not attributable 
  9.  * @version 1.0 
  10.  */  
  11.   
  12. import java.net.*;  
  13. import java.io.InputStream;  
  14. import java.io.BufferedReader;  
  15. import java.io.InputStreamReader;  
  16. import java.util.Map;  
  17. import java.util.Iterator;  
  18. import java.io.*;  
  19.   
  20. import javax.net.ssl.HttpsURLConnection;  
  21. public class TestHttp {  
  22.   public TestHttp() {  
  23.   }  
  24.   public static void main(String[] args) {  
  25.     //TestHttp testHttp1 = new TestHttp();  
  26.     try{  
  27. //      System.setProperty("ssl.provider", "com.sun.net.ssl.internal.ssl.Provider ");  
  28. //      System.setProperty("ssl.pkgs", "com.sun.net.ssl.internal.www.protocol");  
  29.         System.setProperty("javax.net.ssl.keyStore","d:\\cfcakeystore_client.jks");  
  30.         System.setProperty("javax.net.ssl.keyStorePassword","xxxxx");   
  31.         System.setProperty("javax.net.ssl.trustStore","d:\\cfcakeystore_client.jks");   
  32.         System.setProperty("javax.net.ssl.trustStorePassword","xxxxx");   
  33.       URL url = new URL("https://xx.xxx.com:8443/docs");  
  34. //      URL url = new URL("http://localhost:7001/test/newpageflow1/Newpageflow1Controller.jpf");  
  35.       HttpsURLConnection  uc=(HttpsURLConnection)url.openConnection();  
  36.   
  37.       System.out.println(url.toExternalForm());  
  38.       //BufferedReader reader=new BufferedReader(new InputStreamReader(url.openStream()));  
  39.       System.out.println("length"+uc.getContentLength());  
  40.       System.out.println("/"+uc.getContentType());  
  41.       Map map=uc.getHeaderFields();  
  42.       Iterator it=map.entrySet().iterator();  
  43.       while(it.hasNext()){  
  44.         System.out.println(it.next());  
  45.       }  
  46.       System.out.println("========"+uc.getHeaderField("Content-disposition"));  
  47.       System.out.println(uc.getContent());  
  48.       File file=new File("c:/xx.dat");  
  49.       FileOutputStream fo=new FileOutputStream(file);  
  50.       InputStream input=uc.getInputStream();  
  51.       // ���������������  
  52.       BufferedInputStream bis=null;  
  53.       BufferedOutputStream bos=null;  
  54.       try {  
  55.           // ���������������  
  56.           bis=new BufferedInputStream(uc.getInputStream());  
  57.           bos=new BufferedOutputStream(fo);  
  58.           // ���� Buffer  
  59.           byte[] buff=new byte[1024];  
  60.           int bytesRead;  
  61.           int total=0;  
  62.           PrintWriter pw=new PrintWriter(System.out);  
  63.           // ��/дѭ����  
  64.           while(-1!=(bytesRead=bis.read(buff,0,buff.length))) {  
  65.               bos.write(buff,0,bytesRead);  
  66.               bos.flush();  
  67.               total+=bytesRead;  
  68.   
  69.               //pw.write(total+" bytes readed");  
  70.               pw.flush();  
  71.           }  
  72.       } catch(IOException e){  
  73.   //        logger.debug("IOException. \n"+e.getMessage());  
  74.           throw e;  
  75.       } finally{  
  76.           if(bis!=null) bis.close();  
  77.           if(bos!=null) bos.close();  
  78.       }  
  79. //  
  80. //      do{  
  81. //        tempStr=reader.readLine();  
  82. //        System.out.println(tempStr);  
  83. //      }while(tempStr!=null);  
  84.     }catch(Exception e){  
  85.       e.printStackTrace();  
  86.     }  
  87.   }  
  88.   
  89. }  

Java代码   收藏代码
  1. package test;  
  2.   
  3. import java.io.File;  
  4. import java.io.FileInputStream;  
  5. import java.io.IOException;  
  6. import java.security.KeyStore;  
  7.   
  8. import org.apache.http.HttpEntity;  
  9. import org.apache.http.HttpResponse;  
  10. import org.apache.http.client.HttpClient;  
  11. import org.apache.http.client.methods.HttpGet;  
  12. import org.apache.http.conn.scheme.Scheme;  
  13. import org.apache.http.conn.ssl.SSLSocketFactory;  
  14. import org.apache.http.impl.client.DefaultHttpClient;  
  15.   
  16. public class TestHttps {  
  17.   
  18.     /** 
  19.      * @param args 
  20.      */  
  21.     public static void main(String[] args) {  
  22.         DefaultHttpClient httpclient = new DefaultHttpClient();  
  23.         FileInputStream instream = null;  
  24.         FileInputStream instream2 = null;  
  25.         KeyStore trustStore;  
  26.         KeyStore keyStore;  
  27.         try {  
  28.             trustStore = KeyStore.getInstance(KeyStore.getDefaultType());  
  29.             keyStore = KeyStore.getInstance(KeyStore.getDefaultType());  
  30.             instream = new FileInputStream(new File("d:\\cfcakeystore_client.jks"));  
  31.             instream2 = new FileInputStream(new File("d:\\cfcakeystore_client.jks"));     
  32.             trustStore.load(instream, "xxxxx".toCharArray());  
  33.             keyStore.load(instream2, "xxxxx".toCharArray());  
  34.             SSLSocketFactory socketFactory = new SSLSocketFactory(keyStore,  
  35.                     "xxxxx", trustStore);  
  36.             Scheme sch = new Scheme("https", socketFactory, 8443);  
  37.             httpclient.getConnectionManager().getSchemeRegistry().register(sch);  
  38.   
  39.             HttpGet httpget = new HttpGet("https://xx.xxx.com:8443/docs");  
  40.   
  41.             System.out.println("executing request" + httpget.getRequestLine());  
  42.   
  43.             HttpResponse response = httpclient.execute(httpget);  
  44.             HttpEntity entity = response.getEntity();  
  45.   
  46.             System.out.println("----------------------------------------");  
  47.             System.out.println(response.getStatusLine());  
  48.             if (entity != null) {  
  49.                 System.out.println("Response content length: "  
  50.                         + entity.getContentLength());  
  51. //              entity.getContent().  
  52.                 entity.consumeContent();  
  53.             }  
  54.             httpclient.getConnectionManager().shutdown();  
  55.         } catch (Exception e) {  
  56.             e.printStackTrace();  
  57.         } finally {  
  58.             try {  
  59.                 instream.close();  
  60.                 instream2.close();  
  61.             } catch (IOException e) {  
  62.                 // TODO Auto-generated catch block  
  63.                 e.printStackTrace();  
  64.             }  
  65.         }  
  66.   
  67.     }  
  68. }  


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

可以修改windows\system32\drivers\etc\hosts文件,如: 
Java代码   收藏代码
  1. 172.17.249.48 xx.xxx.com  

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

客户端代码程序访问时,url也必须写成: 
Java代码   收藏代码
  1. 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以上 


http://cherubim-chen.iteye.com/blog/900487

好的,以下是一个简单的 Java 客户端服务器端的演示代码,用于验证客户端登陆服务器的用户名: ## 服务器端代码 ```java import java.io.*; import java.net.*; public class Server { public static void main(String[] args) throws IOException { ServerSocket serverSocket = new ServerSocket(9999); System.out.println("服务器已启动,等待客户端连接..."); while (true) { Socket socket = serverSocket.accept(); System.out.println("客户端已连接,地址:" + socket.getRemoteSocketAddress()); BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream())); PrintWriter out = new PrintWriter(socket.getOutputStream(), true); String username = in.readLine(); if (username.equals("admin")) { out.println("欢迎登陆!"); } else { out.println("用户名不正确!"); } socket.close(); } } } ``` ## 客户端代码 ```java import java.io.*; import java.net.*; public class Client { public static void main(String[] args) throws IOException { Socket socket = new Socket("localhost", 9999); BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream())); PrintWriter out = new PrintWriter(socket.getOutputStream(), true); BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); System.out.print("请输入用户名:"); String username = br.readLine(); out.println(username); String response = in.readLine(); System.out.println(response); socket.close(); } } ``` 以上代码实现了一个简单的用户名验证功能,当客户端输入用户名后,向服务器发送用户名,服务器接收后判断是否为 "admin",如果是,则返回欢迎信息,否则返回错误信息。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值