tl; dr: Использование пользовательского CA без добавления его в постоянное хранилище ключей.Загрузить корневой сертификат CA во время выполнения в Java
Я пишу приложение Java, которое должно подключаться к удаленному серверу с помощью HTTPS. Код для подключения готов, однако SSL-сертификат сервера был подписан StartSSL, который не находится в корневом хранилище сертификатов CA.
Использование this code, я получаю достоверную информацию сертификата из веб-сайтов, как https://www.google.com/
:
Response Code : 200
Cipher Suite : TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
Cert Type : X.509
Cert Hash Code : -1643391404
Cert Public Key Algorithm : RSA
Cert Public Key Format : X.509
Cert Type : X.509
Cert Hash Code : 771393018
Cert Public Key Algorithm : RSA
Cert Public Key Format : X.509
Cert Type : X.509
Cert Hash Code : 349192256
Cert Public Key Algorithm : RSA
Cert Public Key Format : X.509
Тот же самый код бросает SSLHandshakeException
для моего домена (назовем его https://www.example.com/
).
Конечно, я мог бы вручную добавить сертификат в хранилище ключей, используя keytool
(возможно, даже с Runtime.exec("keytool ...")
), но это путь к загрязнению. Теперь я планирую добавить корневой сертификат StartSSL в свои файлы приложений для распространения, а затем загрузить его во временное хранилище ключей во время выполнения. Таким образом, «реальное» хранилище остается нетронутым.
Из того, что я прочел here и here, мне придется общаться с некоторыми классами, такими как TrustManager
. Я даже нашел способ для completely turn off the validation, но это не то, что я хочу, поскольку он, кажется, устраняет всю цель зашифрованной связи.
Я даже нашел несколько строк кода, которые, кажется, делать именно то, что я хочу, но я не могу понять, как назвать этот метод т.е. какие значения для передачи в качестве аргументов:
public class SSLClasspathTrustStoreLoader {
public static void setTrustStore(String trustStore, String password) throws Exception {
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("X509");
KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
InputStream keystoreStream = SSLClasspathTrustStoreLoader.class.getResourceAsStream(trustStore);
keystore.load(keystoreStream, password.toCharArray());
trustManagerFactory.init(keystore);
TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustManagers, null);
SSLContext.setDefault(sc);
}
}
Кто-нибудь был в аналогичная ситуация раньше? Я буду признателен за любые советы о том, как справиться с этой проблемой. Если бы вы могли даже помочь мне получить код выше, я был бы действительно счастлив!
Что касается 'keytool' части вашего ответа: приложение будет распространяться на несколько клиентских систем. Изменение системы в хранилище ключей вручную невозможно для каждой отдельной системы и ее модификации во время выполнения сложно, так как пользователь может изменить пароль (который по умолчанию является «changeit», поэтому кто-то действительно может это сделать). В этом случае соединение не будет работать, или мне нужно будет запросить пароль, который не является удобным для пользователя imho.Но спасибо за ваш ответ, я дам ему еще один выстрел в ближайшее время. – Hexaholic
Вы можете создать пользовательскую среду хранения ключей, если она не существует, или вы можете упаковать хранилище ключей с вашим приложением. Я не знаю деталей вашего проекта, но должно быть более элегантное решение. Удачи в любом случае :) – Boris