SSLHandshakeException при вызове защищенной службы REST

Мне был предоставлен файл JKS (keystore.jks), чтобы сделать безопасный вызов RESTful из моего Java REST-клиента. Вот что я сделал.

<сильный>1. Получить псевдоним из файла JKS

keytool -list -v -keystore keystore.jks

<сильный>2. Экспорт сертификата из JKS

keytool -export -alias aliasName -file certName.cer -keystore keystore.jks

<сильный>3. Импорт сертификата в доверительный фонд JRE

keytool -keystore cacerts -importcert -noprompt -trustcacerts -alias aliasName -file certName.cer

<сильный>4. Убедитесь, что сертификат добавлен в хранилище доверенных сертификатов

keytool -list -v -keystore cacerts

КЛИЕНТ JAVA

package hello;

import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

import javax.net.ssl.SSLContext;

import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContexts;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;

public class HttpClientSSLTest {

    public static void main(String[] args) throws Exception {

        String trustStore = System.getProperty("javax.net.ssl.trustStore");
        if (trustStore == null) {
            System.out.println("javax.net.ssl.trustStore is not defined");
        } else {
            System.out.println("javax.net.ssl.trustStore = " + trustStore);
        }

        String keyStore = System.getProperty("javax.net.ssl.keyStore");
        if (keyStore == null) {
            System.out.println("javax.net.ssl.keyStore is not defined");
        } else {
            System.out.println("javax.net.ssl.keyStore = " + keyStore);
        }

        // Trust all certs
        SSLContext sslcontext = buildSSLContext();

        // Allow TLSv1 protocol only
        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
                sslcontext, new String[] { "TLSv1" }, null,
                SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);

        CloseableHttpClient httpclient = HttpClients.custom()
                .setSSLSocketFactory(sslsf).build();
        try {
            HttpGet httpget = new HttpGet(
                      "https://devmachine12:12212/tools/reference-id/xyz"
            );
            httpget.addHeader("ApiKey", "XYZ");
            httpget.addHeader("UserId:", "XYZ");
            httpget.addHeader("Content-Type", "application/json;v=3");
            httpget.addHeader("Accept", "application/json;v=3");

            System.out.println("executing request " + httpget.getRequestLine());

            CloseableHttpResponse response = httpclient.execute(httpget);
            try {
                HttpEntity entity = response.getEntity();
                if (entity != null) {
                    System.out.println("Response content length: "
                            + entity.getContentLength());
                }
                for (Header header : response.getAllHeaders()) {
                    System.out.println(header);
                }
                EntityUtils.consume(entity);
            } finally {
                response.close();
            }
        } finally {
            httpclient.close();
        }
    }

    private static SSLContext buildSSLContext()
            throws NoSuchAlgorithmException, KeyManagementException,
            KeyStoreException {
        SSLContext sslcontext = SSLContexts.custom()
                .setSecureRandom(new SecureRandom())
                .loadTrustMaterial(null, new TrustStrategy() {

                    public boolean isTrusted(X509Certificate[] chain,
                                             String authType) throws CertificateException {
                        return true;
                    }
                }).build();
        return sslcontext;
    }

}

Я все еще получаю это исключение.

javax.net.ssl.trustStore = /Library/Java/JavaVirtualMachines/jdk1.8.0_45.jdk/Contents/Home/jre/lib/security/cacerts
javax.net.ssl.keyStore = /Users/Path/To/keystore.jks
trustStore is: /Library/Java/JavaVirtualMachines/jdk1.8.0_45.jdk/Contents/Home/jre/lib/security/cacerts
trustStore type is : jks
main, handling exception: javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
17:00:25.312 [main] DEBUG o.a.h.i.c.DefaultManagedHttpClientConnection - http-outgoing-0: Shutdown connection
17:00:25.312 [main] DEBUG o.a.h.impl.execchain.MainClientExec - Connection discarded
17:00:25.312 [main] DEBUG o.a.h.i.c.DefaultManagedHttpClientConnection - http-outgoing-0: Close connection
17:00:25.313 [main] DEBUG o.a.h.i.c.PoolingHttpClientConnectionManager - Connection released: [id: 0][route: {s}->https://devmachine12:12212][total kept alive: 0; route allocated: 0 of 2; total allocated: 0 of 20]
17:00:25.313 [main] DEBUG o.a.h.i.c.PoolingHttpClientConnectionManager - Connection manager is shutting down
17:00:25.313 [main] DEBUG o.a.h.i.c.PoolingHttpClientConnectionManager - Connection manager shut down
Exception in thread "main" javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
    at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)
    at sun.security.ssl.Alerts.getSSLException(Alerts.java:154)
    at sun.security.ssl.SSLSocketImpl.recvAlert(SSLSocketImpl.java:2011)
    at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1113)
    at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1363)
    at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1391)
    at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1375)
    at org.apache.http.conn.ssl.SSLConnectionSocketFactory.createLayeredSocket(SSLConnectionSocketFactory.java:290)
    at org.apache.http.conn.ssl.SSLConnectionSocketFactory.connectSocket(SSLConnectionSocketFactory.java:259)
    at org.apache.http.impl.conn.HttpClientConnectionOperator.connect(HttpClientConnectionOperator.java:125)

ПРИМЕЧАНИЕ. Используя тот же файл Keystore.jks в SOAP-UI, я могу успешно выполнить вызов REST.

Обновление 1: я также попробовал собственное хранилище доверенных сертификатов.

   KeyStore trustStore  = KeyStore.getInstance(KeyStore.getDefaultType());
    //FileInputStream instream = new FileInputStream(new File("/Users/xyz/Documents/keystore.jks"));
    FileInputStream instream = new FileInputStream(new File("/Library/Java/JavaVirtualMachines/jdk1.8.0_45.jdk/Contents/Home/jre/lib/security/cacerts"));


    try {
        trustStore.load(instream, "password".toCharArray());
    } finally {
        instream.close();
    }

// Trust own CA and all self-signed certs
SSLContext sslcontext = SSLContexts.custom().loadTrustMaterial(trustStore,new TrustSelfSignedStrategy()).build();


// Allow TLSv1 protocol only
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
        sslcontext, new String[] { "TLSv1" }, null,
        SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);

CloseableHttpClient httpclient = HttpClients.custom()
        .setSSLSocketFactory(sslsf).build();

Получение этой ошибки..

Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    at sun.security.provider.certpath.SunCertPathBuilder.build(SunCertPathBuilder.java:145)
    at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:131)
    at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:280)
    at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:382)

-Djavax.net.debug=все

javax.net.ssl.trustStore = /Path/To/keystore.jks
javax.net.ssl.keyStore = /Library/Java/Home/lib/security/cacerts
trustStore is: /Library/Java/Home/lib/security/cacerts
trustStore type is : jks
trustStore provider is : 
init truststore
adding as trusted cert:
  Subject: CN=SecureTrust CA, O=SecureTrust Corporation, C=US
  Issuer:  CN=SecureTrust CA, O=SecureTrust Corporation, C=US
  Algorithm: RSA; Serial number: 0xcf08e5c0444a5xxxv7ff0eb271859d0
  Valid from Tue Nov 07 14:31:18 EST 2006 until Mon Dec 31 14:40:55 EST 2029

adding as trusted cert:
  Subject: CN=DigiCert Global Root CA, OU=www.digicert.com, O=DigiCert Inc, C=US
  Issuer:  CN=DigiCert Global Root CA, OU=www.digicert.com, O=DigiCert Inc, C=US
  Algorithm: RSA; Serial number: 0x83be056904df661a1743ac95991c74a
  Valid from Thu Nov 09 19:00:00 EST 2006 until Sun Nov 09 19:00:00 EST 2031

....
*** **ClientHello, TLSv1**
RandomCookie:  GMT: 1420323139 bytes = { 245, 155, 164, 46, 144, 29, 159, 19, 144, 152, 111, 67, 67, 81, 155, 132, 11, 444, 43, 777, 64, 110, 38, 59, 105, 57, 218, 148 }
Session ID:  {}
Cipher Suites: [TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, TLS_RSA_WITH_AES_128_CBC_SHA, TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDH_RSA_WI

*** ServerHello, TLSv1
RandomCookie:  GMT: 1420323139 bytes = { 169, 124, 555, 87, 44, 71, 222, 62, 1, 171, 150, 217, 12, 44, 50, 35, 77, 76, 33, 219, 123, 191, 87, 188, 888, 99, 115, 158 }
Session ID:  {133, 155, 225, 44, 44, 111, 105, 25, 229, 223, 99, 7, 12, 66, 184, 227}
Cipher Suite: TLS_RSA_WITH_AES_128_CBC_SHA
Compression Method: 0
***
Warning: No renegotiation indication extension in ServerHello
%% Initialized:  [Session-1, TLS_RSA_WITH_AES_128_CBC_SHA]

main, READ: TLSv1 Handshake, length = 1664
*** 
**Certificate chain**
chain [0] = [
[
  Version: V3
  Subject: CN=xxx-qa.xxx.xxx.com, OU=Web Servers, O=xxx, C=US
  Signature Algorithm: SHA1withRSA, OID = 1.2.840.113549.1.1.5

  Key:  Sun RSA public key, 1024 bits
  modulus: 13102849046627232962710284400322858861706811412350472430303415237614110859833765308993228833198516749796429689145995898905457746791810550642537672313
  public exponent: 65537
  Validity: [From: Fri Jun 18 10:56:04 EDT 2010,
               To: Sun Aug 11 12:50:23 EDT 2019]
  Issuer: O=xxx, C=US
  SerialNumber: [    494dadbd]

chain [1] = [
[
  Version: V3
  Subject: O=xxx, C=US
  Signature Algorithm: SHA1withRSA, OID = 1.2.840.113549.1.1.5

  Key:  Sun RSA public key, 1024 bits
  modulus: 15728074885629223656589726231564957982308943232383883470077767024196824781883586292405962205030193006305258215264230938869191345249508973458673148381
  public exponent: 3
  Validity: [From: Wed Aug 11 12:20:23 EDT 1999,
               To: Sun Aug 11 12:50:23 EDT 2019]
  Issuer: O=xxx, C=US
  SerialNumber: [    37b1a9ce]
...
0000: 0E 00 00 00                                        ....
main, READ: TLSv1 Handshake, length = 4
*** ServerHelloDone

person Himalay Majumdar    schedule 14.07.2015    source источник
comment
Поскольку вы скопировали сертификат в хранилище доверенных сертификатов по умолчанию, вам не нужно определять SSLContext, поскольку HttpClient и Java должны вернуться к хранилищу доверенных сертификатов по умолчанию. Вы пытались исключить конфигурацию SSL? Кроме того, вы уже пытались определить собственное хранилище доверенных сертификатов и посмотреть, получаете ли вы все еще SSLHandshakeException? Я создал пример сервера и клиентского приложения, в котором используется собственное хранилище ключей/доверенное хранилище для быстрого тестирования правильного рукопожатия   -  person Roman Vottner    schedule 15.07.2015
comment
@RomanVottner Я использую SSLContext, чтобы заставить его использовать TLSv1, иначе я получаю другую ошибку. Я попробовал ваше предложение использовать собственное хранилище доверенных сертификатов и получил еще одну ошибку. Пожалуйста, смотрите мое обновление выше.   -  person Himalay Majumdar    schedule 15.07.2015
comment
Является ли сертификат, который вы получили, доверенным (доверенным государственным органом)? Вы также можете включить отладку во время установления связи через -Djavax.net.debug=all или -Djavax.net.debug=ssl. Вы также можете попробовать проверить, является ли сертификат доверенным или что-то вроде этого, но я думаю, что они должны быть действительными, иначе SoapUI уже должен дать сбой   -  person Roman Vottner    schedule 15.07.2015
comment
Есть ли у вас ненадежные сертификаты в хранилище доверенных сертификатов по умолчанию? Или почему вы определяете инструкцию доверия всем сертификатам в обеих попытках? Кроме того, я не уверен, следует ли использовать хранилище ключей в качестве хранилища доверия (как в вашей второй попытке). Поскольку сертификату явно доверяют, не могли бы вы попробовать один из этих непринятых ответов< /а>   -  person Roman Vottner    schedule 15.07.2015
comment
Не используйте один и тот же файл в качестве хранилища ключей и хранилища доверенных сертификатов. Они служат совершенно разным целям и должны подчиняться совершенно разным режимам доступа.   -  person user207421    schedule 15.07.2015
comment
@RomanVottner Спасибо, что поделились разными идеями, я попробовал эти две, но ошибка не устранена :(   -  person Himalay Majumdar    schedule 15.07.2015
comment
Что означает «местный доверенный магазин»? А сертификат вашего сервера подписан ЦС или нет? Если это так, вы делаете правильные вещи, хотя и не обязательно правильным путем. Если это не так, вам не нужно делать ни один из них. Вам не нужен небезопасный код TrustManager в любом случае. У вас нет проблемы с брандмауэром, иначе не было бы соединения для закрытия однорангового узла. Может быть, вы используете установленную JRE, а не JRE JDK?   -  person user207421    schedule 15.07.2015
comment
@EJP Под локальным хранилищем доверенных сертификатов я имел в виду «lib/security/cacerts» в своем jdk и подтвердил, что использую JRE JDK. Я думаю, что мой Keystore.jks является самоподписанным сертификатом, и поэтому я экспортировал файл Keystore.cert из Keystore.jks и импортировал его в «lib/security/cacerts» в моем jdk.   -  person Himalay Majumdar    schedule 15.07.2015


Ответы (1)


SunCertPathBuilderException: unable to find valid certification path to requested target

Ваше хранилище доверенных сертификатов не доверяет сертификату сервера. Если он самоподписанный, импортируйте его; если вы уже сделали это, вы сделали это неправильно.

Если он не подписан самостоятельно, не импортируйте его и вообще не используйте собственное хранилище доверенных сертификатов. Используйте тот, который поставляется с JRE.

И не используйте код, который доверяет всем сертификатам или пытается это сделать. Это не безопасно. Это не решение этой проблемы. Лекарство хуже болезни.

person user207421    schedule 15.07.2015
comment
Не могли бы вы сообщить мне, если вы видите какую-либо ошибку при импорте сертификата в мои cacerts Java. Я уже делал это несколько раз и в разных версиях JRE JDK, выполнив 4 шага, которые я перечислил в своем вопросе. Я все еще продолжаю получать SSLHandshakeException. - person Himalay Majumdar; 15.07.2015
comment
Я добавил -Djavax.net.debug=all logs, не могли бы вы сказать мне, должны ли все сертификаты в цепочке сертификатов после ServerHello, TLSv1 присутствовать в моем хранилище доверенных сертификатов клиента? Мне был предоставлен файл Keystore.jks сервером, у которого было несколько сертификатов (которые я сохранил в хранилище доверенных сертификатов клиента), но не содержал только один сертификат в цепочке сертификатов, которая следует после ServerHello, TLSv1 может быть причиной того, что я получаю SSLHandshakeException? - person Himalay Majumdar; 17.07.2015
comment
Интересно, что файл Keystore.jks работает, когда я использую его из SoapUI, может ли сервер выдавать разные сертификаты для вызовов SoapUI по сравнению с обычными вызовами java-клиента? - person Himalay Majumdar; 17.07.2015
comment
Один из сертификатов в цепочке, представленной сервером, должен находиться в хранилище доверенных сертификатов, предпочтительно самый верхний. Если в цепочке более одного элемента, это не самозаверяющий сертификат, и вам не нужно ничего импортировать в какое-либо хранилище доверенных сертификатов. Сделайте это снова с чистыми cacerts и опубликуйте здесь всю цепочку сертификатов сервера и сообщите нам, присутствовал ли какой-либо из этих сертификатов в чистых cacerts. На самом деле, если есть такой, перечислите его с помощью keytool (-v) и опубликуйте его htoo. Я не понимаю, что на самом деле здесь keystore.jks и откуда оно взялось. - person user207421; 17.07.2015
comment
1. keystore.jks — это файл, который я получил от людей со стороны сервера вручную по электронной почте, я экспортировал сертификаты из этого файла (как упоминалось в начале моего вопроса) и поместил его в cacerts. 2. По вашему предложению я удалил сертификаты (которые я ранее экспортировал из jks и импортировал в cacerts) и сделал его чистым, моя цепочка сертификатов сервера осталась прежней и теперь я не вижу ни один из сертификатов в cacerts. Я ищу свой серийный номер сертификата в cacerts. - person Himalay Majumdar; 17.07.2015
comment
1. Если, как это кажется вероятным, файл keystore.jks содержит закрытый ключ, они не должны были отправлять его вам в первую очередь. Они только что скомпрометировали свой закрытый ключ и не должны начинать весь процесс заново с новым ключом, новым CSR, новой подписью и т. д. Поскольку это сертификат, подписанный ЦС, они должны были отправить вам то, что им отправил CA, а не хранилище ключей. Весь процесс здесь вызывает большие подозрения и должен быть расследован кем-то компетентным. - person user207421; 18.07.2015
comment
2. Про удаление сертификатов я ничего не говорил. Вам нужно получить чистую копию cacerts из другой установки JRE. Удаление вещей, которые, возможно, должны быть там, только усугубит ситуацию. 3. Я не знаю, что означает ваше последнее предложение. - person user207421; 18.07.2015
comment
1. Я согласен, просто указав мой профессиональный инструмент SoapUI на файл keystore.jks, я могу совершать безопасные вызовы REST, но не используя Java. 2. Помимо удаления сертификатов, я также получил новую установку Java и попытался использовать новые cacerts. 3. Если вы заметили ** цепочку сертификатов ** в моем журнале отладки, вы заметите, что к каждому сертификату прикреплен серийный номер, вот как я ищу, присутствует ли сертификат в моих cacerts или нет. - person Himalay Majumdar; 19.07.2015
comment
По-видимому, кто-то еще из моей компании может вызвать ту же защищенную службу REST, следуя точному процессу, как я, используя тот же файл jks. Я дам вам знать на следующей неделе. - person Himalay Majumdar; 19.07.2015