Java中的https证书问题

越来越多的网站由http迁移至https,Apple甚至要求必须使用https,现在iOS中的app一律都得使用https。

https与http之最大不同便在于安全性,多了一个证书。我们使用浏览器访问https站点的时候,会首先下载该站点的证书并安装,然后验证该证书的权威性。如果证书合法,有些浏览器会在网址栏处出现一个钩状或者绿色的标记;如果证书错误,但不正确,则无法访问;如果证书正确,但不是权威机构颁发的,例如网站自己签发的证书,则浏览器会需要用户确认是否继续访问。

Java中访问https站点和浏览器又有不同,其中一个差别便是对于权威机构的认定,一些浏览器能够正常访问的https站点,在Java中却访问不了,出现javax.net.ssl.SSLHandshakeException,其实就是因为Java认为证书不够权威。

如何解决这一问题呢?两种方式:

1 不进行证书验证,即所有https站点的证书都认为合法,可以访问所有https站点,缺点就是牺牲了https的安全性。

2 修改证书验证,即读取特定站点的证书,加入证书信任列表,这样就可以对指定的https进行访问了。

 

代码如下;

 

package com.dancen.util.https;import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.util.Collection;import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;public class MyHttpsUtil
{public static void main(String[] args){try{HttpsURLConnection connection = MyHttpsUtil.getMyHttpsURLConnection("https://src.test.com/home/config.json");System.out.println(connection.getContentType());InputStream is = connection.getInputStream();InputStreamReader isr = new InputStreamReader(is);BufferedReader br = new BufferedReader(isr);String data;while(null != (data = br.readLine())){System.out.println(data);}br.close();isr.close();is.close();}catch(Exception e){e.printStackTrace();}}/*** 获取不安全的HttpsConnection* 关闭证书验证,可以和任何https站点建立连接* @param url* @return* @throws IOException*/public static HttpsURLConnection getMyHttpsURLConnection(String url) throws IOException{HttpsURLConnection rs = null;if(null != url && !url.isEmpty()){try{rs = (HttpsURLConnection)new URL(url.trim()).openConnection();TrustManager[] trustManagers = {new MyX509TrustManager()};SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(null, trustManagers, new SecureRandom());rs.setSSLSocketFactory(sslContext.getSocketFactory());rs.setHostnameVerifier(new MyHostnameVerifier());}catch(Exception e){throw new IOException(e);}}return rs;}/*** 获取自定义的HttpsConnection* 读取证书后,和特定的https站点建立连接* @param url* @param cerFilePath* @return* @throws IOException*/public static HttpsURLConnection getHttpsURLConnection(String url, String cerFilePath) throws IOException{HttpsURLConnection rs = null;if(null != url && !url.isEmpty()){try{if(null == cerFilePath || cerFilePath.isEmpty()){rs = getDefaultHttpsURLConnection(url);}else{rs = getHttpsURLConnection(url, new FileInputStream(cerFilePath.trim()));}}catch(Exception e){throw new IOException(e);}}return rs;}/*** 获取自定义的HttpsConnection* 读取证书流后,可以和特定的https站点建立连接* @param url* @param cerInputStream* @return* @throws IOException*/public static HttpsURLConnection getHttpsURLConnection(String url, InputStream cerInputStream) throws IOException{HttpsURLConnection rs = null;if(null != url && !url.isEmpty()){try{rs = (HttpsURLConnection)new URL(url.trim()).openConnection();if(null != cerInputStream){CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");Collection<? extends Certificate> certificates = certificateFactory.generateCertificates(cerInputStream);KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());keyStore.load(null, null);int index = 0;if(null != certificates){for(Certificate certificate : certificates){String certificateAlias = Integer.toString(index++);keyStore.setCertificateEntry(certificateAlias, certificate);}}KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());keyManagerFactory.init(keyStore, null);TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());trustManagerFactory.init(keyStore);SSLContext sslContext = SSLContext.getInstance("TLS");sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), new SecureRandom());rs.setSSLSocketFactory(sslContext.getSocketFactory());}}catch(Exception e){throw new IOException(e);}}return rs;}/*** 获取默认的HttpsURLConnection* 只能与系统信任的https站点建立连接* @param url* @return* @throws IOException*/public static HttpsURLConnection getDefaultHttpsURLConnection(String url) throws IOException{HttpsURLConnection rs = null;if(null != url && !url.isEmpty()){rs = (HttpsURLConnection)new URL(url.trim()).openConnection();}return rs;}
}

 

 

 

 

 

 

package com.dancen.util.https;import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;import javax.net.ssl.X509TrustManager;public class MyX509TrustManager implements X509TrustManager
{@Overridepublic void checkClientTrusted(X509Certificate ax509certificate[], String s) throws CertificateException{//TODO nothing}@Overridepublic void checkServerTrusted(X509Certificate ax509certificate[], String s) throws CertificateException{//TODO nothing}@Overridepublic X509Certificate[] getAcceptedIssuers(){return new X509Certificate[]{};}
}

 

package com.dancen.util.https;import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSession;public class MyHostnameVerifier implements HostnameVerifier
{public MyHostnameVerifier(){}@Overridepublic boolean verify(String hostname, SSLSession session){return true;}
}


附录:https相关知识

 

地址:http://kingj.iteye.com/blog/2103662

 

 

一:什么是https协议

 

     在说HTTPS之前先说说什么是HTTP,HTTP就是我们平时浏览网页时候使用的一种协议。HTTP协议传输的数据都是未加密的,也就是明文的,因此使 用HTTP协议传输隐私信息非常不安全。为了保证这些隐私数据能加密传输,于是网景公司设计了SSL(Secure Sockets Layer)协议用于对HTTP协议传输的数据进行加密,从而就诞生了HTTPS。SSL目前的版本是3.0,被IETF(Internet Engineering Task Force)定义在RFC 6101中,之后IETF对SSL 3.0进行了升级,于是出现了TLS(Transport Layer Security) 1.0,定义在RFC 2246。实际上我们现在的HTTPS都是用的TLS协议,但是由于SSL出现的时间比较早,并且依旧被现在浏览器所支持,因此SSL依然是HTTPS的 代名词,但无论是TLS还是SSL都是上个世纪的事情,SSL最后一个版本是3.0,今后TLS将会继承SSL优良血统继续为我们进行加密服务。目前 TLS的版本是1.2,定义在RFC 5246中,暂时还没有被广泛的使用。对历史感兴趣的朋友可以参考http://en.wikipedia.org/wiki/Transport_Layer_Security,这里有对TLS/SSL详尽的叙述。

 

二:https的工作原理是什么

     HTTPS在传输数据之前需要客户端(浏览器)与服务端(网站)之间进行一次握手,在握 手过程中将确立双方加密传输数据的密码信息,通常情况下会配合数字证书实现。

TLS/SSL协议不仅仅是一套加密传输的协议,更是一件经过艺术家精心设计的艺术品,TLS/SSL中使用 非对称加密对称加密以及HASH算法

这里我们先看看这上面提到的几种技术(如果你对这些技术已经非常了解,那么请跳过该段落,直接到段落三)

   三 https握手的过程详细描述

1.浏览器将自己支持的一套加密规则发送给网站,如RSA加密算法,DES对称加密算法,SHA1摘要算法
2.网站从中选出一组加密算法与HASH算法,并将自己的身份信息以证书的形式发回给浏览器。证书里面包含了网站地址,加密公钥,以及证书的颁发机构等信息(证书中的私钥只能用于服务器端进行解密,在握手的整个过程中,都用到了证书中的公钥和浏览器发送给服务器的随机密码以及对称加密算法


3.获得网站证书之后浏览器要做以下工作:
    a) 验证证书的合法性(颁发证书的机构是否合法,证书中包含的网站地址是否与正在访问的地址一致等),如果证书受信任,则浏览器栏里面会显示一个小锁头,否则会给出证书不受信的提示。
    b) 如果证书受信任,或者是用户接受了不受信的证书,浏览器会生成一串随机数的密码,并用证书中提供的公钥加密。
    c) 使用约定好的HASH算法计算握手消息(如SHA1),并使用生成的随机数对消息进行加密,最后将之前生成的被公钥加密的随机数密码,HASH摘要值一起发送给服务器


4.网站接收浏览器发来的数据之后要做以下的操作:
    a) 使用自己的私钥将信息解密并取出浏览器发送给服务器的随机密码,使用密码解密浏览器发来的握手消息,并验证HASH是否与浏览器发来的一致。
    b) 使用随机密码加密一段握手消息,发送给浏览器。
    5.浏览器解密并计算握手消息的HASH,如果与服务端发来的HASH一致,此时握手过程结束,之后所有的通信数据将由之前浏览器生成的随机密码并利用对称加密算法进行加密。

 

从上面的4个大的步骤可以看到,握手的整个过程使用到了数字证书、对称加密、HASH摘要算法。

 

本文链接:https://my.lmcjl.com/post/9123.html

展开阅读全文

4 评论

留下您的评论.