未找到 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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 15:58:10  来源:igfitidea点击:

Java HttpClient error for no SSL certificate found, using certificate as String within code?

javasslhttpshttpclient

提问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 foundI 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 PEMReaderto turn it into an X509Certificateinstance. Once this is done, create a KeyStoreinstance in memory and put this X.509 certificate in it. Then, instantiate a new SSLContextusing that KeyStoreas 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 SSLContextfor 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 pemfile somewhere.

如果您只想接受这个单一证书而不是所有自签名证书,那么您应该下载证书并将pem文件存储在某处。

Now you can use this code to load the pemfile, 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.

提醒:当您信任自签名证书时,您很容易受到攻击。