找不到SSL证书的Java HttpClient错误,在代码中使用证书作为String?

时间:2011-06-23 16:28:58

标签: java ssl https httpclient

我在尝试使用HttpClient来调用使用自签名证书的https站点时感到有点困惑。我有下面的代码,这使我能够拨打电话,但后来我收到错误,如javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: No trusted certificate found我已从网络浏览器下载了证书,并了解我可以将其导入密钥库但我宁愿只需将它放入代码并以这种方式使用它,有没有办法做到这一点?

    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);

5 个答案:

答案 0 :(得分:4)

假设您的证书是PEM格式。您可以将其嵌入代码中,并使用BouncyCastlePEMReader将其转换为X509Certificate实例。完成此操作后,在内存中创建一个KeyStore实例并将此X.509证书放入其中。然后,使用SSLContext作为信任存储来实例化新的KeyStore,并让您的HTTP客户端使用它。

这看起来像这样(没试过,记得关闭读者并捕捉异常......):

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);

然后,使用此SSLContext进行连接。如果您使用的是版本4.x(或SSLSocketFactory,如果您使用的是版本3.x),则可以使用Apache HttpClient的this执行此操作。我建议现在使用Apache HttpClient 4.x.

答案 1 :(得分:2)

基于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();

然后是客户端构建器:

HttpClientBuilder hcb = HttpClients.custom();

我只设置了上下文。我不使用setSSLSocketFactory,因为它会干扰下面的setHostnameVerifier

hcb.setSslcontext(sslctx);

最后,我设置了一个验证所有的主机名验证程序:

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;
  }
});

最后建立客户端:

HttpClient c = hcb.build();

答案 2 :(得分:1)

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

现在,您可以使用此代码加载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());
        }
    }
}

答案 3 :(得分:0)

这是如何使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()

现在执行POST或GET请求使用标准执行方法:

HttpResponse response = client.execute(...);

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

答案 4 :(得分:0)

在许多情况下,证书固定可能比硬编码特定证书更可取。

是的,您可以将证书硬编码到您的代码中,这样可以正常运行。这是一种非常合理的方法。但是,它确实有一些缺点。一个陷阱是最终证书将过期,然后您的应用程序将停止工作。此外,如果您想更改私钥,则无法使用。

因此,在许多情况下,使用证书锁定更灵活,可能更可取。有关如何实施证书固定的参考,请参阅here