Модуль, который я добавляю к нашему большому Java-приложению, должен общаться с другим сайтом, защищенным SSL-сайтом. Проблема в том, что сайт использует самозаверяющий сертификат. У меня есть копия сертификата, чтобы убедиться, что я не сталкиваюсь с атакой "человек в середине", и мне нужно включить этот сертификат в наш код таким образом, чтобы соединение с сервером было успешным.
Вот базовый код:
void sendRequest(String dataPacket) {
String urlStr = "https://host.example.com/";
URL url = new URL(urlStr);
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
conn.setMethod("POST");
conn.setRequestProperty("Content-Length", data.length());
conn.setDoOutput(true);
OutputStreamWriter o = new OutputStreamWriter(conn.getOutputStream());
o.write(data);
o.flush();
}
Без какой-либо дополнительной обработки для самоподписанного сертификата это умирает при conn.getOutputStream() со следующим исключением:
Exception in thread "main" javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
....
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
....
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
В идеале, мой код должен научить Java принимать этот самозаверяющий сертификат, для этого одного места в приложении и нигде больше.
Я знаю, что могу импортировать сертификат в хранилище сертификатов JRE, и это позволит Java принять его. Это не тот подход, который я хочу принять, если смогу помочь; это кажется очень инвазивным для всех наших клиентских машин для одного модуля, который они не могут использовать; это повлияет на все другие Java-приложения, используя одну и ту же JRE, и мне это не нравится, хотя шансы любого другого приложения Java, когда-либо обращающегося к этому сайту, равны нулю. Это также не тривиальная операция: в UNIX я должен получить права доступа для изменения JRE таким образом.
Я также видел, что я могу создать экземпляр TrustManager, который выполняет некоторую пользовательскую проверку. Похоже, что я даже могу создать TrustManager, который делегирует реальный TrustManager во всех случаях, кроме этого одного сертификата. Но похоже, что TrustManager устанавливается глобально, и я полагаю, что это повлияет на все другие соединения из нашего приложения, и это тоже не совсем мне нравится.
Какой предпочтительный, стандартный или лучший способ настроить приложение Java для принятия самозаверяющего сертификата? Могу ли я выполнить все цели, которые я имею в виду выше, или мне придется идти на компромисс? Есть ли опция, включающая файлы и каталоги и параметры конфигурации, а также код "маленький-на-нет"?