问题
我知道,关于这个问题有很多不同的问题和很多答案......但是我无法理解......
我有:ubuntu-9.10-desktop-amd64 NetBeans6.7.1从"关闭"按"原样"安装。代表。我需要通过HTTPS连接到某个站点。为此,我使用Apache的HttpClient。
从教程我读到:
"一旦正确安装了JSSE,通过SSL进行的安全HTTP通信就应该如此
简单的HTTP通信。"还有一些例子:
HttpClient httpclient = new HttpClient();
GetMethod httpget = new GetMethod("https://www.verisign.com/");
try {
httpclient.executeMethod(httpget);
System.out.println(httpget.getStatusLine());
} finally {
httpget.releaseConnection();
}
到现在为止,我写道:
HttpClient client = new HttpClient();
HttpMethod get = new GetMethod("https://mms.nw.ru");
//get.setDoAuthentication(true);
try {
int status = client.executeMethod(get);
System.out.println(status);
BufferedInputStream is = new BufferedInputStream(get.getResponseBodyAsStream());
int r=0;byte[] buf = new byte[10];
while((r = is.read(buf)) > 0) {
System.out.write(buf,0,r);
}
} catch(Exception ex) {
ex.printStackTrace();
}
结果我有一组错误:
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
at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)
at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1627)
at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:204)
at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:198)
at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:994)
at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:142)
at sun.security.ssl.Handshaker.processLoop(Handshaker.java:533)
at sun.security.ssl.Handshaker.process_record(Handshaker.java:471)
at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:904)
at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1132)
at sun.security.ssl.SSLSocketImpl.writeRecord(SSLSocketImpl.java:643)
at sun.security.ssl.AppOutputStream.write(AppOutputStream.java:78)
at java.io.BufferedOutputStream.flushBuffer(BufferedOutputStream.java:82)
at java.io.BufferedOutputStream.flush(BufferedOutputStream.java:140)
at org.apache.commons.httpclient.HttpConnection.flushRequestOutputStream(HttpConnection.java:828)
at org.apache.commons.httpclient.HttpMethodBase.writeRequest(HttpMethodBase.java:2116)
at org.apache.commons.httpclient.HttpMethodBase.execute(HttpMethodBase.java:1096)
at org.apache.commons.httpclient.HttpMethodDirector.executeWithRetry(HttpMethodDirector.java:398)
at org.apache.commons.httpclient.HttpMethodDirector.executeMethod(HttpMethodDirector.java:171)
at org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:397)
at org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:323)
at simpleapachehttp.Main.main(Main.java:41)
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:302)
at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:205)
at sun.security.validator.Validator.validate(Validator.java:235)
at sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:147)
at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:230)
at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:270)
at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:973)
... 17 more
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:191)
at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:255)
at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:297)
... 23 more
我该怎么做才能创建最简单的SSL连接? (可能没有KeyManager和信任经理等。)
#1 热门回答(147 赞)
https://mms.nw.ru使用自签名证书,该证书显然不包含在默认的信任管理器集中。
你需要执行以下操作之一:
- 使用接受任何证书的TrustManager配置SSLContext(见下文)
- 使用包含证书的适当信任存储配置SSLContext
- 将该站点的证书添加到默认的Java信任库。
这是一个示例程序,它创建一个(几乎毫无价值的)SSL上下文,它接受任何证书:
import java.net.URL;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
public class SSLTest {
public static void main(String [] args) throws Exception {
// configure the SSLContext with a TrustManager
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(new KeyManager[0], new TrustManager[] {new DefaultTrustManager()}, new SecureRandom());
SSLContext.setDefault(ctx);
URL url = new URL("https://mms.nw.ru");
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
conn.setHostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String arg0, SSLSession arg1) {
return true;
}
});
System.out.println(conn.getResponseCode());
conn.disconnect();
}
private static class DefaultTrustManager implements X509TrustManager {
@Override
public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {}
@Override
public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {}
@Override
public X509Certificate[] getAcceptedIssuers() {
return null;
}
}
}
#2 热门回答(42 赞)
https://mms.nw.ru可以使用非证书颁发机构颁发的证书。因此,你需要将证书添加到受信任的Java密钥存储区,如unable to find valid certification path to requested target中所述:
在使用以https协议运行的启用SSL的服务器的客户端上工作时,如果服务器证书不是由证书颁发机构颁发,而是自签名或签发,则可能会出现"无法找到所请求目标的有效证书路径"的错误通过私人CMS。不要惊慌。如果客户端是用Java编写的,那么你需要做的就是将服务器证书添加到受信任的Java密钥存储区。你可能想知道如何无法访问安装服务器的计算机。有一个简单的程序可以帮助你。请下载Java程序并运行%java InstallCert web_site_hostname
此程序打开与指定主机的连接并启动SSL握手。它打印出现的错误的异常堆栈跟踪,并显示服务器使用的证书。现在,它会提示你将证书添加到受信任的KeyStore。如果你改变主意,请输入"q"。如果你确实要添加证书,请输入"1"或其他数字以添加其他证书,甚至是CA证书,但你通常不希望这样做。一旦做出选择,程序将显示完整的证书,然后将其添加到当前目录中名为"jssecacerts"的Java KeyStore。要在程序中使用它,请将JSSE配置为将其用作信任库,或将其复制到$ JAVA_HOME / jre / lib / security目录中。如果你希望所有Java应用程序将证书识别为受信任而不仅仅是JSSE,你还可以覆盖该目录中的cacerts文件。毕竟,JSSE将能够与主机完成握手,你可以通过再次运行程序来验证。要获得更多详细信息,你可以查看Leeland的博客不再"无法找到有效的认证路径到所请求的目标"
#3 热门回答(22 赞)
除了Pascal Thivent的正确答案,另一种方法是从Firefox(查看证书 - >详细信息 - >导出)或openssl s_client
保存证书并将其导入信任库。
只有在有办法验证证书时才应该这样做。如果失败了,在第一次连接时执行此操作,如果证书在后续连接中意外更改,则至少会给你一个错误。
要在信任库中导入它,请使用:
keytool -importcert -keystore truststore.jks -file servercert.pem
默认情况下,默认信任库应为lib/security/cacerts
,密码应为changeit
,详见JSSE Reference guide。
如果你不想全局允许该证书,但仅限于这些连接,则可以为它创建一个SSLContext
:
TrustManagerFactory tmf = TrustManagerFactory
.getInstance(TrustManagerFactory.getDefaultAlgorithm());
KeyStore ks = KeyStore.getInstance("JKS");
FileInputStream fis = new FileInputStream("/.../truststore.jks");
ks.load(fis, null);
// or ks.load(fis, "thepassword".toCharArray());
fis.close();
tmf.init(ks);
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, tmf.getTrustManagers(), null);
然后,你需要通过实现一个来为Apache HTTP Client 3.x设置它,如果它的SecureProtocolSocketFactory
使用这个SSLContext
。 (有例子here)。
Apache HTTP Client 4.x(除了最早的版本)直接支持传递anSSLContext
。