为了尽量避免安全问题,公司的不少系统服务都逐步https化,虽然开始过程会遇到各类问题,但趋势不改。最完美的https应用是能实现双向认证,客户端用私钥签名用服务端公钥加密,服务端用私钥签名客户端都公钥加密,但现实不少状况不可能让每一个客户端都申请一个证书,所以只实现https的单项认证,即只要服务端又证书,客户端只验证https端证书可靠就可进行https通讯。在某些状况下为了避免花钱买第三方信任机构颁发都证书,客户端在一些状况下也不作服务器端都认证,两边只实现htts的加密通讯。最近就遇到一个问题,https调用证书验证失败,最终考虑仍是忽略调服务证书的验证。html
java程序在访问https资源时,出现报错sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target这本质上,是java在访问https资源时的证书信任问题。如何解决这个问题呢?为什么有这个问题?解决这个问题前,要了解1)https通讯过程客户端在使用HTTPS方式与Web服务器通讯时有如下几个步骤,如图所示。(1)客户使用https的URL访问Web服务器,要求与Web服务器创建SSL链接。(2)Web服务器收到客户端请求后,会将网站的证书信息(证书中包含公钥)传送一份给客户端。(3)客户端的浏览器与Web服务器开始协商SSL链接的安全等级,也就是信息加密的等级。(4)客户端的浏览器根据双方赞成的安全等级,创建会话密钥,而后利用网站的公钥将会话密钥加密,并传送给网站。(5)Web服务器利用本身的私钥解密出会话密钥。(6)Web服务器利用会话密钥加密与客户端之间的通讯。
2)java程序的证书信任规则如上文所述,客户端会从服务端拿到证书信息。调用端(客户端)会有一个证书信任列表,拿到证书信息后,会判断该证书是否可信任。若是是用浏览器访问https资源,发现证书不可信任,通常会弹框告诉用户,对方的证书不可信任,是否继续之类。Java虚拟机并不直接使用操做系统的keyring,而是有本身的security manager。与操做系统相似,jdk的security manager默认有一堆的根证书信任。若是你的https站点证书是花钱申请的,被这些根证书所信任,那使用java来访问此https站点会很是方便。所以,若是用java访问https资源,发现证书不可信任,则会报文章开头说到的错误。解决问题的方法1)将证书导入到jdk的信任证书中(理论上应该可行,未验证)2)在客户端(调用端)添加逻辑,忽略证书信任问题第一种方法,须要在每台运行该java程序的机器上,都作导入操做,不方便部署,所以,采用第二种方法。下面贴下该方法对应的代码。验证可行的代码1)先实现验证方法
- HostnameVerifier hv = new HostnameVerifier() {
- public boolean verify(String urlHostName, SSLSession session) {
- System.out.println("Warning: URL Host: " + urlHostName + " vs. "
- + session.getPeerHost());
- return true;
- }
- };
- private static void trustAllHttpsCertificates() throws Exception {
- javax.net.ssl.TrustManager[] trustAllCerts = new javax.net.ssl.TrustManager[1];
- javax.net.ssl.TrustManager tm = new miTM();
- trustAllCerts[0] = tm;
- javax.net.ssl.SSLContext sc = javax.net.ssl.SSLContext
- .getInstance("SSL");
- sc.init(null, trustAllCerts, null);
- javax.net.ssl.HttpsURLConnection.setDefaultSSLSocketFactory(sc
- .getSocketFactory());
- }
- static class miTM implements javax.net.ssl.TrustManager,
- javax.net.ssl.X509TrustManager {
- public java.security.cert.X509Certificate[] getAcceptedIssuers() {
- return null;
- }
- public boolean isServerTrusted(
- java.security.cert.X509Certificate[] certs) {
- return true;
- }
- public boolean isClientTrusted(
- java.security.cert.X509Certificate[] certs) {
- return true;
- }
- public void checkServerTrusted(
- java.security.cert.X509Certificate[] certs, String authType)
- throws java.security.cert.CertificateException {
- return;
- }
- public void checkClientTrusted(
- java.security.cert.X509Certificate[] certs, String authType)
- throws java.security.cert.CertificateException {
- return;
- }
- }
2)在访问https资源前,调用
- trustAllHttpsCertificates();
- HttpsURLConnection.setDefaultHostnameVerifier(hv);
http://blog.csdn.net/lizeyang/article/details/18983843java
解决https证书验证不经过的问题
一、报错信息api
java.security.cert.CertificateException: No name matching api.weibo.com found; nested exception is javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: No name matching api.weibo.com found缘由:在调用api.weibo.com的时候,咱们使用的是https的方式,正常状况下应该是使用api.weibo.com的证书,但因为某些缘由,咱们只能使用本身的证书,致使在验证证书的时候,就报了这个错误。浏览器
解决的办法:忽略服务端和客户端的证书校验便可。java 提供的相关的类。tomcat
二、具体实现方式安全
经过重写TrustManager的checkClientTrusted(检查客户端证书信任)和checkServerTrusted(检查服务端证书验证)。服务器
以及HostnameVerifier的verify(校验)方法便可取消对证书的全部验证。session
import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.net.ssl.*; import java.io.IOException; import java.net.URL; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; public final class DisableSSLCertificateCheckUtil { private static final Logger LOGGER = LoggerFactory.getLogger(DisableSSLCertificateCheckUtil.class); /** * Prevent instantiation of utility class. */ private DisableSSLCertificateCheckUtil() { } /** * Disable trust checks for SSL connections. */ public static void disableChecks() { try { new URL("https://0.0.0.0/").getContent(); } catch (IOException e) { // This invocation will always fail, but it will register the // default SSL provider to the URL class. } try { SSLContext sslc; sslc = SSLContext.getInstance("TLS"); TrustManager[] trustManagerArray = {new X509TrustManager() { @Override public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { } @Override public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { } @Override public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; } }}; sslc.init(null, trustManagerArray, null); HttpsURLConnection.setDefaultSSLSocketFactory(sslc.getSocketFactory()); HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() { @Override public boolean verify(String s, SSLSession sslSession) { return true; } }); } catch (Exception e) { LOGGER.error("error msg:{}", e); throw new IllegalArgumentException("证书校验异常!"); } } }调用方式:ide
DisableSSLCertificateCheckUtil.disableChecks();影响的范围:将会影响整个tomcat里面对证书的验证。即经过tomcat里面的其余项目虽然没有执行这一段代码可是也一样会忽略证书的验证。post
影响的时间:执行这段代码以后的全部时间都生效。