允许 Java 使用不受信任的证书进行 SSL/HTTPS 连接
Posted
技术标签:
【中文标题】允许 Java 使用不受信任的证书进行 SSL/HTTPS 连接【英文标题】:Allowing Java to use an untrusted certificate for SSL/HTTPS connection 【发布时间】:2010-11-15 02:55:53 【问题描述】:我一直在开发一个从动态 Web 应用程序中提取信息的程序,该程序运行良好,直到我将我的 tomcat 服务器设置为使用自签名(因此,不受信任)证书使用 SSL。错误的堆栈跟踪是:
javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
Error: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Alerts.java:150)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1584)
at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Handshaker.java:174)
at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Handshaker.java:168)
at com.sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:848)
at com.sun.net.ssl.internal.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:106)
at com.sun.net.ssl.internal.ssl.Handshaker.processLoop(Handshaker.java:495)
at com.sun.net.ssl.internal.ssl.Handshaker.process_record(Handshaker.java:433)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:877)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1089)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1116)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1100)
at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:402)
at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:170)
at sun.net.www.protocol.http.HttpURLConnection.getOutputStream(HttpURLConnection.java:857)
at sun.net.www.protocol.https.HttpsURLConnectionImpl.getOutputStream(HttpsURLConnectionImpl.java:230)
at com.certicom.gls.glscs.nongui.URLReader$PostURL.setupURL(URLReader.java:34)
at com.certicom.gls.glscs.nongui.URLReader.getLogin(URLReader.java:227)
at com.certicom.gls.glscs.nongui.URLReader.testLogin(URLReader.java:436)
at com.certicom.gls.glscs.nongui.Controller.loginMenu(Controller.java:384)
at com.certicom.gls.glscs.nongui.Controller.menu(Controller.java:324)
at com.certicom.gls.glscs.nongui.Controller.<init>(Controller.java:49)
at com.certicom.gls.glscs.nongui.Controller.main(Controller.java:61)
在网络浏览器中,当用户使用不受信任的证书访问 HTTPS 站点时,系统会提示用户一个警告,如果他想继续,则要求他例外;我想为我的命令行应用程序实现类似的功能......我承认我对套接字编程和一般网络是新手;任何解决这个问题的建议都会很棒!
【问题讨论】:
您可以使用this implemenation... 它有两个部分 1. 在您调用Https
之前调用一个带有静态方法的类文件
【参考方案1】:
Here 是一些相关代码:
// Create a trust manager that does not validate certificate chains
TrustManager[] trustAllCerts = new TrustManager[]
new X509TrustManager()
public java.security.cert.X509Certificate[] getAcceptedIssuers()
return null;
public void checkClientTrusted(
java.security.cert.X509Certificate[] certs, String authType)
public void checkServerTrusted(
java.security.cert.X509Certificate[] certs, String authType)
;
// Install the all-trusting trust manager
try
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
catch (Exception e)
// Now you can access an https URL without having the certificate in the truststore
try
URL url = new URL("https://hostname/index.html");
catch (MalformedURLException e)
这将完全禁用 SSL 检查——只是不要从此类代码中学习异常处理!
要做你想做的事,你必须在你的 TrustManager 中实现一个提示用户的检查。
【讨论】:
这是这样做的方法;但是,您需要非常小心,不要让此代码投入生产 - 如果您在生产应用程序中使用不受信任的证书,这是一件坏事。如果您的应用中有像 spring 这样的依赖注入框架,请考虑隔离此代码并为生产环境准备一个安全版本。new java.security.SecureRandom()
参数也可以替换为null
。
从 Java 7 开始,X509ExtendedTrustManager
rather 可以规避算法约束等更多问题。当然,我必须重申@TimHowland 的评论..
不知何故,当我使用 java 1.8_77 在 linux 服务器上运行它时,它没有进入方法 checkServerTrusted
并且握手问题仍然存在。知道为什么吗?【参考方案2】:
遵循here 的代码是一个有用的解决方案。没有密钥库等。只需在初始化服务和端口(在 SOAP 中)之前调用方法 SSLUtilities.trustAllHttpsCertificates()。
import java.security.GeneralSecurityException;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
/**
* This class provide various static methods that relax X509 certificate and
* hostname verification while using the SSL over the HTTP protocol.
*
* @author Jiramot.info
*/
public final class SSLUtilities
/**
* Hostname verifier for the Sun's deprecated API.
*
* @deprecated see @link #_hostnameVerifier.
*/
private static com.sun.net.ssl.HostnameVerifier __hostnameVerifier;
/**
* Thrust managers for the Sun's deprecated API.
*
* @deprecated see @link #_trustManagers.
*/
private static com.sun.net.ssl.TrustManager[] __trustManagers;
/**
* Hostname verifier.
*/
private static HostnameVerifier _hostnameVerifier;
/**
* Thrust managers.
*/
private static TrustManager[] _trustManagers;
/**
* Set the default Hostname Verifier to an instance of a fake class that
* trust all hostnames. This method uses the old deprecated API from the
* com.sun.ssl package.
*
* @deprecated see @link #_trustAllHostnames().
*/
private static void __trustAllHostnames()
// Create a trust manager that does not validate certificate chains
if (__hostnameVerifier == null)
__hostnameVerifier = new SSLUtilities._FakeHostnameVerifier();
// if
// Install the all-trusting host name verifier
com.sun.net.ssl.HttpsURLConnection
.setDefaultHostnameVerifier(__hostnameVerifier);
// __trustAllHttpsCertificates
/**
* Set the default X509 Trust Manager to an instance of a fake class that
* trust all certificates, even the self-signed ones. This method uses the
* old deprecated API from the com.sun.ssl package.
*
* @deprecated see @link #_trustAllHttpsCertificates().
*/
private static void __trustAllHttpsCertificates()
com.sun.net.ssl.SSLContext context;
// Create a trust manager that does not validate certificate chains
if (__trustManagers == null)
__trustManagers = new com.sun.net.ssl.TrustManager[]new SSLUtilities._FakeX509TrustManager();
// if
// Install the all-trusting trust manager
try
context = com.sun.net.ssl.SSLContext.getInstance("SSL");
context.init(null, __trustManagers, new SecureRandom());
catch (GeneralSecurityException gse)
throw new IllegalStateException(gse.getMessage());
// catch
com.sun.net.ssl.HttpsURLConnection.setDefaultSSLSocketFactory(context
.getSocketFactory());
// __trustAllHttpsCertificates
/**
* Return true if the protocol handler property java. protocol.handler.pkgs
* is set to the Sun's com.sun.net.ssl. internal.www.protocol deprecated
* one, false otherwise.
*
* @return true if the protocol handler property is set to the Sun's
* deprecated one, false otherwise.
*/
private static boolean isDeprecatedSSLProtocol()
return ("com.sun.net.ssl.internal.www.protocol".equals(System
.getProperty("java.protocol.handler.pkgs")));
// isDeprecatedSSLProtocol
/**
* Set the default Hostname Verifier to an instance of a fake class that
* trust all hostnames.
*/
private static void _trustAllHostnames()
// Create a trust manager that does not validate certificate chains
if (_hostnameVerifier == null)
_hostnameVerifier = new SSLUtilities.FakeHostnameVerifier();
// if
// Install the all-trusting host name verifier:
HttpsURLConnection.setDefaultHostnameVerifier(_hostnameVerifier);
// _trustAllHttpsCertificates
/**
* Set the default X509 Trust Manager to an instance of a fake class that
* trust all certificates, even the self-signed ones.
*/
private static void _trustAllHttpsCertificates()
SSLContext context;
// Create a trust manager that does not validate certificate chains
if (_trustManagers == null)
_trustManagers = new TrustManager[]new SSLUtilities.FakeX509TrustManager();
// if
// Install the all-trusting trust manager:
try
context = SSLContext.getInstance("SSL");
context.init(null, _trustManagers, new SecureRandom());
catch (GeneralSecurityException gse)
throw new IllegalStateException(gse.getMessage());
// catch
HttpsURLConnection.setDefaultSSLSocketFactory(context
.getSocketFactory());
// _trustAllHttpsCertificates
/**
* Set the default Hostname Verifier to an instance of a fake class that
* trust all hostnames.
*/
public static void trustAllHostnames()
// Is the deprecated protocol setted?
if (isDeprecatedSSLProtocol())
__trustAllHostnames();
else
_trustAllHostnames();
// else
// trustAllHostnames
/**
* Set the default X509 Trust Manager to an instance of a fake class that
* trust all certificates, even the self-signed ones.
*/
public static void trustAllHttpsCertificates()
// Is the deprecated protocol setted?
if (isDeprecatedSSLProtocol())
__trustAllHttpsCertificates();
else
_trustAllHttpsCertificates();
// else
// trustAllHttpsCertificates
/**
* This class implements a fake hostname verificator, trusting any host
* name. This class uses the old deprecated API from the com.sun. ssl
* package.
*
* @author Jiramot.info
*
* @deprecated see @link SSLUtilities.FakeHostnameVerifier.
*/
public static class _FakeHostnameVerifier implements
com.sun.net.ssl.HostnameVerifier
/**
* Always return true, indicating that the host name is an acceptable
* match with the server's authentication scheme.
*
* @param hostname the host name.
* @param session the SSL session used on the connection to host.
* @return the true boolean value indicating the host name is trusted.
*/
public boolean verify(String hostname, String session)
return (true);
// verify
// _FakeHostnameVerifier
/**
* This class allow any X509 certificates to be used to authenticate the
* remote side of a secure socket, including self-signed certificates. This
* class uses the old deprecated API from the com.sun.ssl package.
*
* @author Jiramot.info
*
* @deprecated see @link SSLUtilities.FakeX509TrustManager.
*/
public static class _FakeX509TrustManager implements
com.sun.net.ssl.X509TrustManager
/**
* Empty array of certificate authority certificates.
*/
private static final X509Certificate[] _AcceptedIssuers = new X509Certificate[];
/**
* Always return true, trusting for client SSL chain peer certificate
* chain.
*
* @param chain the peer certificate chain.
* @return the true boolean value indicating the chain is trusted.
*/
public boolean isClientTrusted(X509Certificate[] chain)
return (true);
// checkClientTrusted
/**
* Always return true, trusting for server SSL chain peer certificate
* chain.
*
* @param chain the peer certificate chain.
* @return the true boolean value indicating the chain is trusted.
*/
public boolean isServerTrusted(X509Certificate[] chain)
return (true);
// checkServerTrusted
/**
* Return an empty array of certificate authority certificates which are
* trusted for authenticating peers.
*
* @return a empty array of issuer certificates.
*/
public X509Certificate[] getAcceptedIssuers()
return (_AcceptedIssuers);
// getAcceptedIssuers
// _FakeX509TrustManager
/**
* This class implements a fake hostname verificator, trusting any host
* name.
*
* @author Jiramot.info
*/
public static class FakeHostnameVerifier implements HostnameVerifier
/**
* Always return true, indicating that the host name is an acceptable
* match with the server's authentication scheme.
*
* @param hostname the host name.
* @param session the SSL session used on the connection to host.
* @return the true boolean value indicating the host name is trusted.
*/
public boolean verify(String hostname, javax.net.ssl.SSLSession session)
return (true);
// verify
// FakeHostnameVerifier
/**
* This class allow any X509 certificates to be used to authenticate the
* remote side of a secure socket, including self-signed certificates.
*
* @author Jiramot.info
*/
public static class FakeX509TrustManager implements X509TrustManager
/**
* Empty array of certificate authority certificates.
*/
private static final X509Certificate[] _AcceptedIssuers = new X509Certificate[];
/**
* Always trust for client SSL chain peer certificate chain with any
* authType authentication types.
*
* @param chain the peer certificate chain.
* @param authType the authentication type based on the client
* certificate.
*/
public void checkClientTrusted(X509Certificate[] chain, String authType)
// checkClientTrusted
/**
* Always trust for server SSL chain peer certificate chain with any
* authType exchange algorithm types.
*
* @param chain the peer certificate chain.
* @param authType the key exchange algorithm used.
*/
public void checkServerTrusted(X509Certificate[] chain, String authType)
// checkServerTrusted
/**
* Return an empty array of certificate authority certificates which are
* trusted for authenticating peers.
*
* @return a empty array of issuer certificates.
*/
public X509Certificate[] getAcceptedIssuers()
return (_AcceptedIssuers);
// getAcceptedIssuers
// FakeX509TrustManager
// SSLUtilities
【讨论】:
这比@Yishai 解决方案更安全、更安全还是同样安全?【参考方案3】:另一种选择是获取该特定服务器的“.pem”(公钥)文件,并将其本地安装到您的 JRE 的“cacerts”文件的中心(使用 keytool 帮助应用程序),然后它将能够从该服务器下载而无需投诉,不会损害您正在运行的 JVM 的整个 SSL 结构并允许从其他未知证书服务器下载...
【讨论】:
以上是关于允许 Java 使用不受信任的证书进行 SSL/HTTPS 连接的主要内容,如果未能解决你的问题,请参考以下文章