java - 在 Java 中,仅使用 PEM 文件创建 SSLContext 的最简单方法是什么?

标签 java ssl https java-8 lets-encrypt

我使用 LetsEncrypt 的 CertBot 免费生成 PEM 文件。在其他语言中,只需使用几行代码和 PEM/ key 文件即可轻松启动 HTTPS 服务器。到目前为止,我在 Java 中找到的解决方案过于复杂,我正在寻找更简单的解决方案。

  1. 我不想使用 java 的命令行“keytool”。我只想将我的 PEM/ key 文件拖放到我的 eclipse 中,然后使用 SSLContext 以编程方式启动 HTTPS 服务器。
  2. 我不想包含像 BouncyCaSTLe 这样的大量外部库。请参阅以下链接,了解使用 BouncyCaSTLe 的假定解决方案:How to build a SSLSocketFactory from PEM certificate and key without converting to keystore?

有更好/更简单的方法吗?

最佳答案

以下代码大体展示了如何通过解析具有多个条目的 PEM 文件为 HTTPS 服务器创建 SSLContext,例如几个证书和一个 RSA PRIVATE KEY。然而它是不完整的,因为纯 Java 8 无法解析 PKCS#1 RSA 私钥数据。因此,您似乎无法在没有任何图书馆的情况下做到这一点。至少需要用于解析 PKCS#1 数据的 BouncyCaSTLe(然后也可以使用 BouncyCaSTLe 的 PEM 解析器)。

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;
}

关于java - 在 Java 中,仅使用 PEM 文件创建 SSLContext 的最简单方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50025086/

相关文章:

ssl - 您如何使用 Namecheap 和 Heroku 强制使用 HTTPS?

javascript - 本地网络的 Websocket 证书

php - Curl 无法验证 Verisign 颁发的通配符 SSL 证书

grails - 可以用 g :form and g:link in grails 2. x 建立安全的 https 链接吗?

PHP获取http url并返回https url

java - Camel 路由测试中忽略模拟且未跳过端点

java - 使用 UDP(数据报)在 Java 中实现 TCP

java - OSX/Eclipse/JDK/无法从 bash 运行 HelloWorld

WCF 服务、SSL、消息安全和数字证书

java - Spring MVC - 当整数值为零时如何显示空白文本框