Moduł, który dodam do naszej dużej aplikacji Java, musi komunikować się z witryną innej firmy zabezpieczoną protokołem SSL. Problem polega na tym, że witryna używa certyfikatu z podpisem własnym. Mam kopię certyfikatu, aby sprawdzić, czy nie mam ataku typu man-in-the-middle i muszę włączyć ten certyfikat do naszego kodu w taki sposób, aby połączenie z serwerem zakończyło się sukcesem.
Oto podstawowy kod:
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();
}
Bez żadnej dodatkowej obsługi certyfikatu z podpisem własnym umiera to w conn.getOutputStream () z następującym wyjątkiem:
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
Idealnie, mój kod musi nauczyć Javę akceptować ten jeden certyfikat z podpisem własnym dla tego jednego miejsca w aplikacji i nigdzie indziej.
Wiem, że mogę zaimportować certyfikat do magazynu urzędu certyfikacji JRE, a to pozwoli Javie go zaakceptować. To nie jest podejście, które chcę przyjąć, jeśli mogę pomóc; wydaje się to bardzo inwazyjne na wszystkich maszynach naszych klientów dla jednego modułu, którego mogą nie używać; wpłynęłoby to na wszystkie inne aplikacje Java korzystające z tego samego środowiska JRE i nie podoba mi się to, mimo że prawdopodobieństwo uzyskania dostępu do tej witryny przez jakąkolwiek inną aplikację Java jest zerowe. Nie jest to również prosta operacja: na UNIX-ie muszę uzyskać prawa dostępu, aby zmodyfikować środowisko JRE w ten sposób.
Widziałem również, że mogę utworzyć instancję TrustManager, która wykonuje niestandardowe sprawdzanie. Wygląda na to, że mógłbym nawet stworzyć TrustManager, który deleguje do prawdziwego TrustManagera we wszystkich przypadkach z wyjątkiem tego jednego certyfikatu. Ale wygląda na to, że TrustManager jest instalowany globalnie i przypuszczam, że wpłynie to na wszystkie inne połączenia z naszej aplikacji, i to też nie pachnie dobrze.
Jaki jest preferowany, standardowy lub najlepszy sposób skonfigurowania aplikacji Java tak, aby akceptowała certyfikat z podpisem własnym? Czy mogę osiągnąć wszystkie cele, o których myślę powyżej, czy będę musiał pójść na kompromis? Czy jest opcja obejmująca pliki i katalogi oraz ustawienia konfiguracyjne i mało kodu?