未找到 SSL 证书的 Java HttpClient 错误,在代码中使用证书作为字符串?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/6457471/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Java HttpClient error for no SSL certificate found, using certificate as String within code?
提问by Rick
I'm a bit confused in trying to use HttpClient to call an https site that uses a self-signed certificate. I have the code like below, which is enabling me to make the call but then I am getting the error like javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: No trusted certificate found
I have downloaded the certificate from my web browser and understand I can import it to the keystore but I would rather just put it into the code and use it that way, is there a way to do this?
我在尝试使用 HttpClient 调用使用自签名证书的 https 站点时有点困惑。我有如下代码,它使我能够拨打电话,但随后出现错误,就像javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: No trusted certificate found
我从 Web 浏览器下载了证书并了解我可以将其导入密钥库,但我宁愿将其放入代码并以这种方式使用它,有没有办法做到这一点?
HttpClient client = new HttpClient();
EasySSLProtocolSocketFactory easySSLProtocolSocketFactory = new EasySSLProtocolSocketFactory();
Protocol https = new Protocol("https", easySSLProtocolSocketFactory,
443);
Protocol.registerProtocol("https", https);
BufferedReader br = null;
String responseString = "";
GetMethod method = new GetMethod(path);
int returnCode = client.executeMethod(method);
采纳答案by Bruno
Assuming your certificate is in PEM format. You can embed it in the code and use BouncyCastle's PEMReader
to turn it into an X509Certificate
instance. Once this is done, create a KeyStore
instance in memory and put this X.509 certificate in it. Then, instantiate a new SSLContext
using that KeyStore
as the trust store and make your HTTP client use it.
假设您的证书是 PEM 格式。您可以在代码中嵌入它,并使用BouncyCastle的的PEMReader
把它变成一个X509Certificate
实例。完成后,KeyStore
在内存中创建一个实例并将此 X.509 证书放入其中。然后,SSLContext
使用它KeyStore
作为信任存储实例化一个新的,并让您的 HTTP 客户端使用它。
This would look like this (not tried, remember to close readers and catch exceptions...):
这看起来像这样(没有尝试过,记得关闭阅读器并捕获异常......):
PEMReader pemReader = new PEMReader(new StringReader("----- BEGIN ......");
X509Certificate cert = (X509Certificate) pemReader.readObject();
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
ks.load(null, null);
ks.setCertificateEntry("some name", cert);
TrustManagerFactory tmf = TrustManagerFactory.getInstance(
TrustManagerFactory.getDefaultAlgorithm());
tmf.init(ks);
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(null, tmf.getTrustManagers(), null);
Then, use this SSLContext
for your connection. You can do this with Apache HttpClient's SSLSocketFactoryif you're using version 4.x (or thisif you're using version 3.x). I'd suggest using Apache HttpClient 4.x nowadays.
然后,将其SSLContext
用于您的连接。你可以使用Apache的HttpClient我们做到这一点的SSLSocketFactory如果您使用4.x版(或本如果你使用3.x版)。我建议现在使用 Apache HttpClient 4.x。
回答by MartinMarco
Building on answer by Alexander Chzhenand for HttpClient 4.3 I first create a context that trusts all:
基于Alexander Chzhen和 HttpClient 4.3 的回答,我首先创建了一个信任所有上下文的上下文:
SSLContextBuilder sslctxb = new SSLContextBuilder();
sslctxb.loadTrustMaterial(KeyStore.getInstance(KeyStore.getDefaultType()),
new TrustSelfSignedStrategy() {
@Override
public boolean isTrusted(X509Certificate[] chain,
String authType)
throws CertificateException {
return true;
}
});
SSLContext sslctx = sslctxb.build();
Then the client builder:
然后客户端构建器:
HttpClientBuilder hcb = HttpClients.custom();
and I only set the context. I don't use setSSLSocketFactorybecause it will interfere with setHostnameVerifierbelow:
我只设置上下文。我不使用setSSLSocketFactory因为它会干扰下面的setHostnameVerifier:
hcb.setSslcontext(sslctx);
Finally I set a hostname verifier that verifies all:
最后我设置了一个主机名验证器来验证所有:
hcb.setHostnameVerifier(new X509HostnameVerifier() {
@Override
public void verify(String host, SSLSocket ssl)
throws IOException {
}
@Override
public void verify(String host, X509Certificate cert)
throws SSLException {
}
@Override
public void verify(String host, String[] cns, String[] subjectAlts)
throws SSLException {
}
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
});
Finally build the client:
最后构建客户端:
HttpClient c = hcb.build();
回答by Ralph
If you want to accept only this single certificate but not all self signed certificates, then you should download the certificate and store the pem
file somewhere.
如果您只想接受这个单一证书而不是所有自签名证书,那么您应该下载证书并将pem
文件存储在某处。
Now you can use this code to load the pem
file, create a new truststore with this certificate and use the truststore for your HttpClient
.
现在您可以使用此代码加载pem
文件,使用此证书创建一个新的信任库并将信任库用于您的HttpClient
.
//use java. ... .X509Certificate, not javax. ... .X509Certificate
import java.security.cert.X509Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
@Test
public void testSslCertificate()
throws IOException, KeyStoreException, NoSuchAlgorithmException,
CertificateException, KeyManagementException {
X509Certificate cert;
try (FileInputStream pemFileStream = new FileInputStream(newFile("your.pem"))) {
CertificateFactory certFactory = CertificateFactory.getInstance("X509");
cert = (X509Certificate) certFactory.generateCertificate(pemFileStream);
}
//create truststore
KeyStore trustStore = KeyStore.getInstance("JKS");
trustStore.load(null); //create an empty trustore
//add certificate to truststore - you can use a simpler alias
String alias = cert.getSubjectX500Principal().getName() + "["
+ cert.getSubjectX500Principal().getName().hashCode() + "]";
trustStore.setCertificateEntry(alias, cert);
//configure http client
TrustManagerFactory trustManagerFactory =
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(trustStore);
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, trustManagerFactory.getTrustManagers(), null);
HttpClientBuilder httpClientBuilder = HttpClientBuilder.
create().setSslcontext(sslContext);
try (CloseableHttpClient httpClient = httpClientBuilder.build()) {
HttpGet httpGetRequest = new HttpGet("https://yourServer");
try (CloseableHttpResponse httpResponse =
httpClient.execute(httpGetRequest)) {
Assert.assertEquals(200,
httpResponse.getStatusLine().getStatusCode());
}
}
}
回答by D.W.
In many situations, certificate pinning might be preferable to hardcoding a particular certificate.
在许多情况下,证书锁定可能比硬编码特定证书更可取。
Yes, you can hardcode a certificate into your code, and that will work and be secure. It's a perfectly reasonable approach. However, it does have some disadvantages. One pitfall is that eventually that certificate will expire, and then your app will stop working. Also, if you ever want to change your private key, you won't be able to.
是的,您可以将证书硬编码到您的代码中,这将有效且安全。这是一个完全合理的方法。但是,它确实有一些缺点。一个陷阱是该证书最终会过期,然后您的应用程序将停止工作。此外,如果您想更改您的私钥,您将无法更改。
Therefore, in many scenarios, using certificate pinning is more flexible and might be preferable. See herefor references on how to implement certificate pinning.
因此,在许多情况下,使用证书锁定更灵活,可能更可取。有关如何实施证书锁定的参考,请参见此处。
回答by Alexander Chzhen
This is how to make Apache HttpClient 4.3 that accept self-signed certificates:
这是使 Apache HttpClient 4.3 接受自签名证书的方法:
HttpClientBuilder cb = HttpClientBuilder.create();
SSLContextBuilder sslcb = new SSLContextBuilder();
sslcb.loadTrustMaterial(KeyStore.getInstance(KeyStore.getDefaultType()),
new TrustSelfSignedStrategy());
cb.setSslcontext(sslcb.build());
HttpClient client = cb.build()
Now to execute POST or GET requests use standard execute method:
现在执行 POST 或 GET 请求使用标准执行方法:
HttpResponse response = client.execute(...);
Reminder: You are vulnerable when you trust self-signed certificate.
提醒:当您信任自签名证书时,您很容易受到攻击。