Назад | Перейти на главную страницу

Ошибка установления связи, если указан URL-путь

Я пытаюсь использовать REST API через HTTPS, но у меня возникает ошибка установления связи, только если я укажу путь URL (не получаю ошибку только с именем домена).

Следуя инструкциям владельца API, я получил файл .PEM (неподписанный сертификат?), С помощью которого я создал CSR, который я отправил обратно, который, в свою очередь, вернул мне файл закрытого ключа, который я затем инкапсулировал с сертификатом и цепочкой подписи в файл PFX.

Я импортировал PFX в хранилище сертификатов Internet Explorer, и теперь я могу без проблем получить доступ как к «корневому» URL (только доменное имя), так и к URL API (включая путь URL). Он также работает в Google Chrome и Postman.

Теперь я пытаюсь заставить его работать с 3 другими приложениями:

В любом случае я могу получить доступ к «корневому» URL-адресу и получить ответ, но если я добавлю путь в URL-адрес, у меня возникнет ошибка установления связи:

Возьмем, что я делал для каждого из своих приложений ...

1) CA Technologies - Управление API

Я импортировал PFX в CA API Gateway с помощью мастера импорта «Manage Certifactes». Он добавил 3 записи сертификата: фактический сертификат, промежуточный центр сертификации и корневой центр сертификации, настроенный как якорь доверия. Сертификат проверяется для использования с «Исходящими SSL-соединениями».

Но когда я использую утверждение «Маршрут через HTTPS» к моему URL с путем, я получаю ошибку рукопожатия. Я также попытался добавить сертификат в магазин, используя мастер добавления «Управление сертификатами» и указав ему URL-адрес с путем для загрузки сертификата. Он добавил 3 разных сертификата в мой магазин, но все равно не работает.

2) Software AG - Сервер интеграции

Поскольку эта ESB не может читать файлы PFX, я экспортировал 3 сертификата (фактическая + цепочка подписи) в файлы .CER через экспорт сертификатов Internet Explorer. В консоли администратора сервера интеграции WebMethods я добавил сертификаты в меню «Настроить сертификаты клиента». Я также настроил хранилище ключей и хранилище доверенных сертификатов с их значениями по умолчанию (DEFAULT_IS_KEYSTORE и DEFAULT_IS_TRUSTSTORE).

Но использование сервиса pub.client: http дало мне ту же ошибку, что и раньше. Затем я попытался добавить свои сертификаты непосредственно в хранилище ключей запущенного jvm (% SAG_PATH% / jvm / jvm / jre / lib / security), но это не решило проблему.

3) Java 8 через HttpsURLConnection

В крайнем случае, я попытался заставить его работать прямо на "чистой" Java. Я добавил сертификаты в свой магазин cacerts и создал класс для подключения к моему URL-адресу и печати информации:

HttpsURLConnection con = (HttpsURLConnection) new URL(url).openConnection();

System.out.println("Response Code : " + con.getResponseCode());
System.out.println("Cipher Suite : " + con.getCipherSuite());
System.out.println("\n");

Certificate[] certs = con.getServerCertificates();
for (Certificate cert : certs) {
    System.out.println("Cert Type : " + cert.getType());
    System.out.println("Cert Hash Code : " + cert.hashCode());
    System.out.println("Cert Public Key Algorithm : " + cert.getPublicKey().getAlgorithm());
    System.out.println("Cert Public Key Format : " + cert.getPublicKey().getFormat());
    System.out.println("\n");
}

Если я попытаюсь получить доступ к корневому URL-адресу, я получу ответ ОК:

Response Code : 200
Cipher Suite : TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256

Cert Type : X.509
Cert Hash Code : -952876714
Cert Public Key Algorithm : RSA
Cert Public Key Format : X.509

Cert Type : X.509
Cert Hash Code : 272760578
Cert Public Key Algorithm : RSA
Cert Public Key Format : X.509

Cert Type : X.509
Cert Hash Code : -1335658159
Cert Public Key Algorithm : RSA
Cert Public Key Format : X.509

Но если я добавлю путь к фактическому API, я тоже получу ошибку рукопожатия:

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:2023)
    at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1125)
    at sun.security.ssl.SSLSocketImpl.readDataRecord(SSLSocketImpl.java:930)
    at sun.security.ssl.AppInputStream.read(AppInputStream.java:105)
    at java.io.BufferedInputStream.fill(BufferedInputStream.java:246)
    at java.io.BufferedInputStream.read1(BufferedInputStream.java:286)
    at java.io.BufferedInputStream.read(BufferedInputStream.java:345)
    at sun.net.www.http.HttpClient.parseHTTPHeader(HttpClient.java:704)
    at sun.net.www.http.HttpClient.parseHTTP(HttpClient.java:647)
    at sun.net.www.http.HttpClient.parseHTTP(HttpClient.java:675)
    at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1569)
    at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1474)
    at java.net.HttpURLConnection.getResponseCode(HttpURLConnection.java:480)
    at sun.net.www.protocol.https.HttpsURLConnectionImpl.getResponseCode(HttpsURLConnectionImpl.java:338)
    at test.Test.print_https_cert(Test.java:60)
    at test.Test.main(Test.java:23)

Вопросы

Нужен ли мне еще один сертификат для фактического URL-адреса API (содержащий полный путь)?
Как он может работать в моем браузере с сертификатами, но не в других приложениях? Как исправить проблему, чтобы она заработала?

Спасибо.


РЕДАКТИРОВАТЬ

Следующий @EnricoBasseti предложения, я попытался включить / отключить (было отключено по умолчанию) параметр «Follow Redirect» в CA API Gateway. Это не изменило сообщение об ошибке.

Затем я попытался заставить Java отправить SNI, изменив свой код:

SSLContext sslContext = SSLContext.getDefault();
SSLParameters sslParameters = new SSLParameters();
List<SNIServerName> sniHostNames = new ArrayList<>(1);
sniHostNames.add(new SNIHostName(url.getHost()));
sslParameters.setServerNames(sniHostNames);
SSLSocketFactory wrappedSSLSocketFactory = new SSLSocketFactoryWrapper(sslContext.getSocketFactory(), sslParameters);
HttpsURLConnection.setDefaultSSLSocketFactory(wrappedSSLSocketFactory);

HttpsURLConnection con = (HttpsURLConnection) url.openConnection();

Но даже с SNI я все равно получаю сбой при рукопожатии, если использую полный URL, а не только доменное имя.


РЕДАКТИРОВАТЬ 2

С участием -Djavax.net.debug.all, Я получил дополнительную информацию о сбое рукопожатия:

verify_data:  { 216, 178, 49, 39, 2, 6, 100, 218, 54, 81, 137, 239 }
***
[write] MD5 and SHA1 hashes:  len = 16
0000: 14 00 00 0C D8 B2 31 27   02 06 64 DA 36 51 89 EF  ......1'..d.6Q..
Padded plaintext before ENCRYPTION:  len = 16
0000: 14 00 00 0C D8 B2 31 27   02 06 64 DA 36 51 89 EF  ......1'..d.6Q..
main, WRITE: TLSv1.2 Handshake, length = 40
[Raw write]: length = 45
0000: 16 03 03 00 28 00 00 00   00 00 00 00 00 44 C8 8F  ....(........D..
0010: A5 85 F7 18 F4 14 E3 9F   79 C6 4C 7B E3 50 61 59  ........y.L..PaY
0020: B0 83 38 DA 16 91 49 41   76 B3 E0 CD 17           ..8...IAv....
[Raw read]: length = 5
0000: 15 03 03 00 1A                                     .....
[Raw read]: length = 26
0000: 00 00 00 00 00 00 00 07   15 66 D8 8D F8 A5 88 D2  .........f......
0010: 44 24 BE 7B E4 1D 75 F7   58 C2                    D$....u.X.
main, READ: TLSv1.2 Alert, length = 26
Padded plaintext after DECRYPTION:  len = 2
0000: 02 28                                              .(
main, RECV TLSv1.2 ALERT:  fatal, handshake_failure
%% Invalidated:  [Session-1, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256]
%% Invalidated:  [Session-2, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256]
main, called closeSocket()
main, handling exception: javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
main, called close()
main, called closeInternal(true)
Ignoring unavailable cipher suite: TLS_DHE_DSS_WITH_AES_256_GCM_SHA384
Ignoring unavailable cipher suite: TLS_RSA_WITH_AES_256_CBC_SHA
Ignoring unavailable cipher suite: TLS_DHE_RSA_WITH_AES_256_GCM_SHA384
Ignoring unavailable cipher suite: TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA
Ignoring unavailable cipher suite: TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
Ignoring unavailable cipher suite: TLS_RSA_WITH_AES_256_CBC_SHA256
Ignoring unavailable cipher suite: TLS_DHE_DSS_WITH_AES_256_CBC_SHA
Ignoring unavailable cipher suite: TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384
Ignoring unavailable cipher suite: TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384
Ignoring unavailable cipher suite: TLS_RSA_WITH_AES_256_GCM_SHA384
Ignoring unavailable cipher suite: TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384
Ignoring unavailable cipher suite: TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
Ignoring unavailable cipher suite: TLS_ECDH_RSA_WITH_AES_256_CBC_SHA
Ignoring unavailable cipher suite: TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384
Ignoring unavailable cipher suite: TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
Ignoring unavailable cipher suite: TLS_DHE_RSA_WITH_AES_256_CBC_SHA256
Ignoring unavailable cipher suite: TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
Ignoring unavailable cipher suite: TLS_DHE_DSS_WITH_AES_256_CBC_SHA256
Ignoring unavailable cipher suite: TLS_DHE_RSA_WITH_AES_256_CBC_SHA
Ignoring unavailable cipher suite: TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
Ignoring unavailable cipher suite: TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384

Не уверен, означает ли это, что мне не хватает определенного шифра.


РЕДАКТИРОВАТЬ 3

С "корневым" URL я получаю этот журнал:

verify_data:  { 40, 57, 242, 4, 89, 211, 6, 190, 109, 98, 13, 50 }
***
[write] MD5 and SHA1 hashes:  len = 16
0000: 14 00 00 0C 28 39 F2 04   59 D3 06 BE 6D 62 0D 32  ....(9..Y...mb.2
Padded plaintext before ENCRYPTION:  len = 16
0000: 14 00 00 0C 28 39 F2 04   59 D3 06 BE 6D 62 0D 32  ....(9..Y...mb.2
main, WRITE: TLSv1.2 Handshake, length = 40
[Raw write]: length = 45
0000: 16 03 03 00 28 00 00 00   00 00 00 00 00 5B 4A 01  ....(........[J.
0010: 6B 3B C3 43 29 8F EF CA   4B 85 85 93 BD 6C E3 9A  k;.C)...K....l..
0020: 2C D0 73 32 2A 33 1A 4A   5B 09 D1 A7 9C           ,.s2*3.J[....
[Raw read]: length = 5
0000: 14 03 03 00 01                                     .....
[Raw read]: length = 1
0000: 01                                                 .
main, READ: TLSv1.2 Change Cipher Spec, length = 1
[Raw read]: length = 5
0000: 16 03 03 00 28                                     ....(
[Raw read]: length = 40
0000: 00 00 00 00 00 00 00 00   72 1F F6 DD 23 77 96 2D  ........r...#w.-
0010: DB BC 1E 10 CC 8A 64 6E   8B C1 A9 04 8A 08 62 20  ......dn......b 
0020: 2A 13 E3 DD 56 57 C3 AB                            *...VW..
main, READ: TLSv1.2 Handshake, length = 40
Padded plaintext after DECRYPTION:  len = 16
0000: 14 00 00 0C 6E 14 F6 CB   16 CA D8 B2 68 96 70 19  ....n.......h.p.
*** Finished
verify_data:  { 110, 20, 246, 203, 22, 202, 216, 178, 104, 150, 112, 25 }
***
%% Didn't cache non-resumable client session: [Session-1, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256]
[read] MD5 and SHA1 hashes:  len = 16
[...] OMMITED (actual content of the URL) [...]

РЕДАКТИРОВАТЬ 4

Первоначально я не мог импортировать свой PFX прямо в мои cacerts, потому что получил ошибку Illegal Key Size. Но поскольку я настроил «UnlimitedJCEPolicyJDK8», я смог его импортировать.

Затем, следуя предложениям @ dave_thompson_085, я указал хранилище ключей для использования в моем Java-коде:

System.setProperty("javax.net.ssl.trustStore", "XXXXX/jdk1.8.0_71/jre/lib/security/cacerts");
System.setProperty("javax.net.ssl.trustStorePassword", "XXXXX");
System.setProperty("javax.net.ssl.keyStore", "XXXXX/jdk1.8.0_71/jre/lib/security/cacerts");
System.setProperty("javax.net.ssl.keyStorePassword", "XXXXX");

Я сначала получил ошибку java.security.UnrecoverableKeyException: Cannot recover key потому что я сохранил ключ с паролем, отличным от пароля хранилища ключей. Но когда я использовал один и тот же пароль для двух элементов, он работал правильно.

Если я правильно понимаю, я отправлял только сертификат, но не свой закрытый ключ. Я знаю, что нужно найти способ сделать то же самое в CA API Gateway и Software AG Integration Server.

Благодаря @ dave_thompson_085 и @EnricoBasseti, Мне удалось заставить его работать с моими 3 приложениями.

Вот что я сделал для каждого:

1) CA Technologies - Управление API

В мастере импорта «Управление закрытым ключом» я добавил ключ из файла PFX. Затем в мастере добавления «Управление сертификатом» я импортировал сертификат из цепочки сертификатов закрытого ключа (4-й вариант). Затем я использовал мастер добавления «Управление сертификатом», чтобы импортировать сертификат из URL-адреса API (в моем случае другой сертификат).

Затем в своей политике я щелкнул правой кнопкой мыши свое утверждение «Маршрут к HTTP (S)» и нажал «Выбрать закрытый ключ», чтобы заставить CA API Gateway использовать закрытый ключ, который я импортировал ранее, вместо ключа по умолчанию.

2) Software AG - Сервер интеграции

В веб-консоли myWebmethod в разделах «Безопасность» и «Хранилище ключей» я щелкнул «Создать псевдоним хранилища ключей». Я настроил новое хранилище ключей PCKS12 и дал ему свой файл PFX (нужно было указать как пароль хранилища ключей, так и пароль ключа отдельно).

Затем в разделах «Безопасность» и «Сертификаты» я щелкнул «Настроить клиентские сертификаты» и добавил корневой сертификат из своей цепочки сертификатов (ранее я экспортировал с помощью IE). Я также добавил корневой сертификат из цепочки сертификации URL (в моем случае другой).

Наконец, в моем Flow Service перед использованием компонента pub.client: http я добавил компонент pub.security.keystore: setKeyAndChain, настроенный на использование моего нового псевдонима KeyStore и его псевдонима Key.

3) Java 8 через HttpsURLConnection

Мне пришлось заменить файлы политики безопасности JDK на файлы, предоставленные oracle: БезлимитныйJCEPolicyJDK8 (только при использовании JDK8 <161). Затем я импортировал пару ключей в свой файл cacerts (% JAVA_HOME% / jre / lib / security / cacerts). Я также импортировал корневой сертификат URL-адреса API (который отличается от того, который содержится в моем файле PFX).

Затем в моем коде я заставил Java использовать мои cacerts в качестве хранилища ключей (см. EDIT 4 из моего исходного сообщения).

TLS-соединение устанавливается перед HTTP-запросом, поэтому невозможно определить (на стороне сервера), какую конечную точку вы запрашиваете (только имя хоста с использованием SNI). Вот почему сертификаты не включают полный URL-адрес.

Итак, сбой при рукопожатии для другого URL-адреса очень странный - возможно, URL-адрес конечной точки выполняет 302 перенаправить на другой сервер? Посмотрите, есть ли в используемом вами приложении параметры для отключения этой опции Follow HTTP redirects (или что-то вроде того).

Вы также можете использовать Internet Explorer, чтобы проверить это: посмотрите, изменится ли URL-адрес при попытке доступа https://api.xxx.com/method/