В Java, что является самым простым способом создания SSLContext только с PEM файлом?

Я использовал LetsEncrypt CertBot для бесплатного создания файлов PEM. На других языках легко запустить HTTPS-сервер, используя только пару строк кода и файлов PEM/key. Решения, которые я нашел до сих пор в java, слишком сложны, и я ищу что-то более простое.

  1. Я не хочу использовать java-команду "keytool". Я просто хочу перетащить свои файлы PEM/key в мое затмение и программно запустить HTTPS-сервер, используя SSLContext.
  2. Я не хочу включать массивные внешние библиотеки, такие как BouncyCastle. См. Следующую ссылку для предполагаемого решения с помощью BouncyCastle: Как создать SSLSocketFactory из сертификата PEM и ключа без преобразования в хранилище ключей?

Есть ли лучший/более простой способ сделать это?

Ответ 1

Следующий код показывает, как создать SSLContext для HTTPS-сервера путем анализа файла PEM с несколькими записями, например нескольких сертификатов и одного RSA PRIVATE KEY. Однако он неполный, потому что простая Java 8 не может проанализировать данные частного ключа RSA PKCS # 1. Поэтому кажется, что ваше желание сделать это без какой-либо библиотеки невозможно. Требуется, по крайней мере, BouncyCastle для разбора данных PKCS # 1 (а также может использоваться анализатор PEM BouncyCastle).

private SSLContext createSslContext() throws Exception {
    URL url = getClass().getResource("/a.pem");
    InputStream in = url.openStream();
    String pem = new String(in.readAllBytes(), StandardCharsets.UTF_8);
    Pattern parse = Pattern.compile("(?m)(?s)^---*BEGIN ([^-]+)---*$([^-]+)^---*END[^-]+-+$");
    Matcher m = parse.matcher(pem);
    CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
    Decoder decoder = Base64.getMimeDecoder();
    List<Certificate> certList = new ArrayList<>(); // java.security.cert.Certificate

    PrivateKey privateKey = null;

    int start = 0;
    while (m.find(start)) {
        String type = m.group(1);
        String base64Data = m.group(2);
        byte[] data = decoder.decode(base64Data);
        start += m.group(0).length();
        type = type.toUpperCase();
        if (type.contains("CERTIFICATE")) {
            Certificate cert = certFactory.generateCertificate(new ByteArrayInputStream(data));
            certList.add(cert);
        } else if (type.contains("RSA PRIVATE KEY")) {
            // TODO: load and parse PKCS1 data structure to get the RSA private key  
            privateKey = ...
        } else {
            System.err.println("Unsupported type: " + type);
        }

    }
    if (privateKey == null)
        throw new RuntimeException("RSA private key not found in PEM file");

    char[] keyStorePassword = new char[0];

    KeyStore keyStore = KeyStore.getInstance("JKS");
    keyStore.load(null, null);

    int count = 0;
    for (Certificate cert : certList) {
        keyStore.setCertificateEntry("cert" + count, cert);
        count++;
    }
    Certificate[] chain = certList.toArray(new Certificate[certList.size()]);
    keyStore.setKeyEntry("key", privateKey, keyStorePassword, chain);

    TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
    tmf.init(keyStore);
    KeyManagerFactory kmf = KeyManagerFactory.getInstance("RSA");
    kmf.init(keyStore, keyStorePassword);
    SSLContext sslContext = SSLContext.getInstance("TLS");
    sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), new SecureRandom());
    return sslContext;
}

Ответ 2

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

  • Этот код не нуждается ни в каком сертификате.

  • Вопрос в том, почему вы пытаетесь избежать этих ошибок, если требуемый случай не должен использовать небезопасный сервер?


logger.info("Starting instance ");
        TrustManager[] tm = new TrustManager[]{new X509TrustManager() {
            public X509Certificate[] getAcceptedIssuers(){return new X509Certificate[]{};}
            public void checkClientTrusted(X509Certificate[] chain, String authType) {logger.info(" checkClientTrusted");}
            public void checkServerTrusted(X509Certificate[] chain, String authType) {logger.info(" checkServerTrusted");}

        }};

        SSLContext sslContext = SSLContext.getInstance("SSL");
        sslContext.init(null, tm , new SecureRandom());
        HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());