Ошибка Java HttpClient для сертификата SSL не найден, используя сертификат в качестве строки в коде? - PullRequest
7 голосов
/ 23 июня 2011

Я немного запутался, пытаясь использовать 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 ]

4 голосов
/ 23 июня 2011

Если ваш сертификат в формате PEM.Вы можете встроить его в код и использовать BouncyCastle PEMReader, чтобы превратить его в 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 для подключения.Вы можете сделать это с помощью SSLSocketFactory Apache HttpClient, если вы используете версию 4.x (или это , если вы используете версию 3.x).Я бы предложил использовать Apache HttpClient 4.x в настоящее время.

2 голосов
/ 09 ноября 2013

Опираясь на ответ Александр Чжен и для 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();
1 голос
/ 24 января 2014

Если вы хотите принять только этот отдельный сертификат, но не все самозаверяющие сертификаты, вам следует загрузить сертификат и где-то сохранить файл 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());
        }
    }
}
0 голосов
/ 29 мая 2014

Во многих ситуациях закрепление сертификата может быть предпочтительнее жесткого кодирования конкретного сертификата.

Да, вы можете жестко закодировать сертификат в свой код, и это будет работать и быть безопасным. Это совершенно разумный подход. Однако у него есть некоторые недостатки. Единственный подводный камень в том, что в конце концов этот сертификат истечет, и тогда ваше приложение перестанет работать. Кроме того, если вы когда-нибудь захотите изменить свой закрытый ключ, вы не сможете.

Таким образом, во многих случаях использование закрепления сертификата является более гибким и может быть предпочтительным. См. здесь для получения информации о том, как реализовать закрепление сертификата.

0 голосов
/ 22 сентября 2013

Вот как сделать 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 используйте стандартный метод execute:

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

Напоминание: вы уязвимы, когда доверяете самоподписанному сертификату.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...