使用 Java URL 类,我可以连接到外部HTTPS
服务器(例如我们的生产站点),但是使用本地 URL 我得到以下异常。
"SunCertPathBuilderException: unable to find valid certification path to requested target".
如何获得有效的认证路径?
编辑:我没有使用此 URL 直接创建连接,而是将 URL 传递给itext
PDFReader
,然后出现连接问题。
使用 Java URL 类,我可以连接到外部HTTPS
服务器(例如我们的生产站点),但是使用本地 URL 我得到以下异常。
"SunCertPathBuilderException: unable to find valid certification path to requested target".
如何获得有效的认证路径?
编辑:我没有使用此 URL 直接创建连接,而是将 URL 传递给itext
PDFReader
,然后出现连接问题。
这是我的解决方案,它结合了该线程中的一些想法,并与来自网络的代码一起使用。我所做的只是调用这个函数,它为 HttpsURLConnection 设置了默认的信任管理器和主机名验证器。这对某些人来说可能是不可取的,因为它会影响所有 HttpsURLConnections,但我只是在编写一个简单的代理,所以它对我有用。
private void setTrustAllCerts() throws Exception
{
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());
HttpsURLConnection.setDefaultHostnameVerifier(
new HostnameVerifier() {
public boolean verify(String urlHostName, SSLSession session) {
return true;
}
});
}
catch ( Exception e ) {
//We can not recover from this exception.
e.printStackTrace();
}
}
您可能需要设置一个HostnameVerifier
. 在连接之前,您需要将其添加到连接对象中
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
conn.setHostnameVerifier(new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
// check hostname/session
return true;
}
});
conn.connect();
// read/write...
如果您需要,肯定有一些实现。您可能还想查看HttpClient项目。
要查看的另一件事是TrustManager
您正在使用。该错误消息表明服务器提供的证书未由受信任的根签名。由于您无法直接控制所创建的 SSL 套接字,因此我认为最好的办法是使用已使用服务器证书链的根 CA 设置的证书来初始化您自己SSLContext
的套接字。然后将此上下文设置为默认值。TrustManager
这是假设您使用的是 Java 6。Java 5 中的 API 受到更多限制。您可以获得 default SSLSocketFactory
,但没有标准的设置方法。
它抱怨的问题是,当您创建 SSL 连接时,服务器必须向客户端提供有效的证书。您可以在 Java 中编写一个适当的端点(我认为 HTTPServerSocket 会这样做),但它需要一些黑客来设置它。使用可正确处理 SSL 的任何东西(Apache、lighttp 等)设置本地 Web 服务器可能更容易,并使用 openssl 工具创建自签名证书。
更新这是 Java Almanac 中的一个示例。http://www.exampledepot.com/egs/javax.net.ssl/Server.html
SSL 服务器套接字需要将发送给客户端进行身份验证的证书。证书必须包含在必须明确指定其位置的密钥库中(没有默认值)。在示例之后,我们将描述如何创建和指定要使用的 SSL 服务器套接字的密钥库。
try {
int port = 443;
ServerSocketFactory ssocketFactory = SSLServerSocketFactory.getDefault();
ServerSocket ssocket = ssocketFactory.createServerSocket(port);
// Listen for connections
Socket socket = ssocket.accept();
// Create streams to securely send and receive data to the client
InputStream in = socket.getInputStream();
OutputStream out = socket.getOutputStream();
// Read from in and write to out...
// Close the socket
in.close();
out.close();
} catch(IOException e) {
}
使用 javax.net.ssl.keyStore 系统属性指定证书的密钥库:
> java -Djavax.net.ssl.keyStore=mySrvKeystore -Djavax.net.ssl.keyStorePassword=123456 MyServer
它还可以帮助您使用“keytool”实用程序将 localhost 服务器正在使用的证书(我假设它是自签名的)添加到 JVM 的密钥库中。这应该有告诉JVM“你可以信任这个证书”的效果。
我最终运行了一个静态方法(仅在 dev 上),它安装了一个非常信任的 TrustManager(接受所有内容),并且还添加了一个始终返回 true 的 hostnameVerifier(感谢sblundy)。