Мы используем Java-клиент (openJDK 1.8.0) для вызова API, который требует взаимной аутентификации. Для этого мы используем стандартный JKS-файл java в качестве хранилища ключей и хранилища доверенных сертификатов (один и тот же файл, содержащий как trustcerts, так и удостоверения личности / privatekey). Пример Java, который мы используем для тестирования, выглядит следующим образом: *
KeyStore clientKeyStore = KeyStore.getInstance("JKS");
clientKeyStore.load(new FileInputStream("./client.keystore"),
password.toCharArray());
// create a client connection manager to use in creating httpclients
PoolingHttpClientConnectionManager mgr = new PoolingHttpClientConnectionManager();
SSLContext sslContext = SSLContextBuilder.create()
.loadKeyMaterial(clientKeyStore, password.toCharArray())
.loadTrustMaterial(clientKeyStore, null).build();
// create the client based on the manager, and use it to make the call
CloseableHttpClient httpClient = HttpClientBuilder.create()
.setConnectionManager(mgr)
.setSslcontext(sslContext)
.setSSLHostnameVerifier(new NoopHostnameVerifier())
.build();
HttpPost httppost = new HttpPost("https://someUrl");
String params = "";
StringEntity param = new StringEntity(params);
httppost.setEntity(param);
System.out.println("Sending request...............");
HttpResponse response = httpClient.execute(httppost);
Во время SSL-рукопожатия, как последнего шага «serverhello», сервер запрашивает личность клиента, выдав «CertificateRequest» - пожалуйста, найдите ниже запрос ::
*** CertificateRequest
Cert Types: RSA, ECDSA, DSS
Supported Signature Algorithms: SHA512withRSA, Unknown (hash:0x6, signature:0x2), SHA512withECDSA, SHA384withRSA, Unknown (hash:0x5, signature:0x2), SHA384withECDSA, SHA256withRSA, SHA256withDSA, SHA256withECDSA, SHA224withRSA, SHA224withDSA, SHA224withECDSA, SHA1withRSA, SHA1withDSA, SHA1withECDSA
Cert Authorities:
<CN=Intermediate CA, OU=ourCA.com, O=ourCA Inc, C=US>
Сразу после этого мы видим строки ниже, указывающие на то, что keyManager java не может найти ничего, подписанного тем же подписывающим лицом.
*** ServerHelloDone
[read] MD5 and SHA1 hashes: len = 4
0000: 0E 00 00 00 ....
Warning: no suitable certificate found - continuing without client authentication
*** Certificate chain
<Empty>
***
Мы проверили, что сертификат присутствует в хранилище ключей и является действительным сертификатом (открывая его в окне Windows, он не открывается, если его недействительный сертификат). Таким образом, наше хранилище ключей имеет цепочку: myIdentity >> с подписью Intermediate CA >> с подписью Root CA
Несколько вещей, которые мы попробовали (без удачи):
- Попытка переопределения keystoremanager вернуть жестко закодированный псевдоним, т.е. псевдоним сертификата в keystore.jks
- Попытка разделения удостоверений личности и сертификатов ЦС в двух отдельных файлах, т.е. в отдельные keystore.jks и truststore.jks
Стоит поделиться, что такая же связь работает хорошо, если мы используем cURL. В случае cURL, мы должны явно передать сертификат клиента в качестве аргумента (cURL не имеет понятия о хранилище ключей) и мы используем хранилище ключей по умолчанию в linux (/etc/pki/tls/certs/ca-bundle.crt)
curl -vvv GET https://api.someone.com/some/path -E /home/certificates/client.test.pem --key /home/certificates/client.test.key
Я не уверен, какие другие детали могут добавить ценность, но я буду рад поделиться всеми возможными необходимыми деталями (кроме моего личного ключа :-P)