Как моя библиотека со встроенным сертификатом ssl также позволяет использовать с сертификатами по умолчанию

Я распределяю библиотеку jar для внутренних клиентов, а в библиотеку входит сертификат, который он использует для вызова службы, которая также является внутренней для нашей сети.

Менеджер доверия настроен следующим образом

    TrustManagerFactory trustManagerFactory = 
      TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
    KeyStore keystore = KeyStore.getInstance("JKS");
    InputStream keystoreStream = 
      clazz.getClassLoader().getResourceAsStream("certs.keystore"); // (on classpath)
    keystore.load(keystoreStream, "pa55w0rd".toCharArray());
    trustManagerFactory.init(keystore);
    TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
    SSLContext context = SSLContext.getInstance("SSL");
    context.init(null, trustManagers, null);

    SSLSocketFactory socketFact = context.getSocketFactory();
    connection.setSSLSocketFactory(socketFact);

Все это прекрасно работает, за исключением случаев, когда пользователям нужны другие сертификаты или сертификат по умолчанию.

Я пробовал это Зарегистрировать несколько хранилищ ключей в JVM без везения (у меня возникают проблемы с обобщением его для моего случая)

Как я могу использовать свой сертификат и все еще разрешать библиотекам пользователей использовать свои собственные сертификаты?

Ответы

Ответ 1

Вы настраиваете соединение с пользовательским хранилищем ключей, выступающим в качестве доверительного хранилища (сертификат вашего сервера, которому вы доверяете). Вы не переопределяете поведение JVM по умолчанию, поэтому остальное соединение, которое могут выполнять другие приложения, содержащие вашу библиотеку, не будет затронуто.

Поэтому вам не нужен многократный диспетчер ключей, на самом деле ваш код работает отлично.

Я google.jks полный пример ниже, используя google.jks который включает в себя корневой google.jks Google, и соединение с использованием стандартного хранилища JVM по умолчанию. Это результат

request("https://www.google.com/", "test/google.jks", "pa55w0rd"); //OK 
request("https://www.aragon.es/", "test/google.jks", "pa55w0rd");  // FAIL sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
request("https://www.aragon.es/", null, null); //OK

Проблема не в том коде, который вы приложили, поэтому проверьте в своем коде следующее:

  • certs.keystore действительно найден в вашем пути к классам

  • Настройки -Djavax.net.ssl.trustStore не установлены на уровне JVM с помощью -Djavax.net.ssl.trustStore

  • Обнаруженные ошибки (пожалуйста, включите его в свой вопрос) действительно связаны с SSL-соединением


package test;

import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.KeyStore;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;


public class HTTPSCustomTruststore {

    public final static void main (String argv[]) throws Exception{
        request("https://www.google.com/", "test/google.jks", "pa55w0rd"); //Expected OK 
        request("https://www.aragon.es/","test/google.jks","pa55w0rd");  // Expected  FAIL
        request("https://www.aragon.es/",null,null); //using default truststore. OK 
    }

    public static void configureCustom(HttpsURLConnection connection, String truststore, String pwd)throws Exception{
        TrustManagerFactory trustManagerFactory = 
                TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        KeyStore keystore = KeyStore.getInstance("JKS");
        InputStream keystoreStream = HTTPSCustomTruststore.class.getClassLoader().getResourceAsStream(truststore);
        keystore.load(keystoreStream, pwd.toCharArray());
        trustManagerFactory.init(keystore);
        TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
        SSLContext context = SSLContext.getInstance("SSL");
        context.init(null, trustManagers,  new java.security.SecureRandom());

        SSLSocketFactory socketFact = context.getSocketFactory();
        connection.setSSLSocketFactory(socketFact);
    }


    public static void request(String urlS, String truststore, String pwd) {
        try {
            URL url = new URL(urlS);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("GET");
            if (truststore != null) {
                configureCustom((HttpsURLConnection) conn, truststore, pwd);
            }   
            conn.connect();

            int statusCode = conn.getResponseCode();
            if (statusCode != 200) {
                System.out.println(urlS + " FAIL");
            } else {
                System.out.println(urlS + " OK");
            }
        } catch (Exception e) {
            System.out.println(urlS + " FAIL " + e.getMessage());
        }
    }
}

Ответ 2

Вы можете импортировать сертификаты по умолчанию в свой собственный магазин, чтобы иметь комбинированный пользовательский магазин и использовать его.