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

在Java中,用PEM文件创建SSLContext最简单的方法是什么?,java,ssl,https,java-8,lets-encrypt,Java,Ssl,Https,Java 8,Lets Encrypt,我使用LetsEncrypt的CertBot免费生成PEM文件。在其他语言中,只需几行代码和PEM/key文件就可以轻松启动HTTPS服务器。到目前为止,我在java中找到的解决方案过于复杂,我正在寻找更简单的解决方案 我不想使用java的命令行“keytool”。我只想将PEM/key文件拖放到eclipse中,并使用SSLContext以编程方式启动HTTPS服务器 我不想包括像BouncyCastle这样的大型外部库。有关使用BouncyCastle的假定解决方案,请参见以下链接: 有更好

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

  • 我不想使用java的命令行“keytool”。我只想将PEM/key文件拖放到eclipse中,并使用SSLContext以编程方式启动HTTPS服务器
  • 我不想包括像BouncyCastle这样的大型外部库。有关使用BouncyCastle的假定解决方案,请参见以下链接:

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

    下面的代码一般显示了如何通过解析具有多个条目(例如多个证书和一个
    RSA私钥)的PEM文件来为HTTPS服务器创建SSLContext。但是,它是不完整的,因为普通Java 8无法解析PKCS#1 RSA私钥数据。因此,似乎您希望在没有任何库的情况下完成这项工作是不可能的。至少需要BouncyCastle来解析PKCS#1数据(然后也可以使用BouncyCastle的PEM解析器)

    private SSLContext createSslContext()引发异常{
    URL=getClass().getResource(“/a.pem”);
    InputStream in=url.openStream();
    String pem=新字符串(in.readAllBytes(),StandardCharsets.UTF_8);
    模式解析=模式。编译(“(?m)(?s)^-*开始([^-]+)-*$([^-]+)^-*结束[^-]+-+$”;
    Matcher m=parse.Matcher(pem);
    CertificateFactory=CertificateFactory.getInstance(“X.509”);
    解码器解码器=Base64.getMimeDecoder();
    List certList=new ArrayList();//java.security.cert.Certificate
    PrivateKey PrivateKey=null;
    int start=0;
    while(m.find(start)){
    字符串类型=m.组(1);
    字符串base64Data=m.group(2);
    字节[]数据=解码器.decode(base64Data);
    start+=m.group(0.length();
    type=type.toUpperCase();
    if(type.contains(“证书”)){
    Certificate cert=certFactory.generateCertificate(新的ByteArrayInputStream(数据));
    证书列表。添加(证书);
    }else if(type.contains(“RSA私钥”)){
    //TODO:加载并解析PKCS1数据结构以获取RSA私钥
    私钥=。。。
    }否则{
    System.err.println(“不支持的类型:“+type”);
    }
    }
    if(privateKey==null)
    抛出新的运行时异常(“在PEM文件中找不到RSA私钥”);
    char[]keystrepassword=新字符[0];
    KeyStore KeyStore=KeyStore.getInstance(“JKS”);
    load(null,null);
    整数计数=0;
    用于(证书:证书列表){
    密钥库.setCertificateEntry(“证书”+计数,证书);
    计数++;
    }
    证书[]链=certList.toArray(新证书[certList.size()]);
    keyStore.setKeyEntry(“key”、privateKey、keyStorePassword、chain);
    TrustManagerFactory tmf=TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
    tmf.init(密钥库);
    KeyManagerFactory kmf=KeyManagerFactory.getInstance(“RSA”);
    kmf.init(keyStore,keyStorePassword);
    SSLContext SSLContext=SSLContext.getInstance(“TLS”);
    sslContext.init(kmf.getKeyManager(),tmf.getTrustManager(),new SecureRandom());
    返回sslContext;
    }
    
    • 这是一个临时解决方案,因为下面的代码允许您接受任何服务器,因此,在尝试这些解决方案时,您应该深入检查代码

    • 此代码根本不需要任何证书

    • 问题是,如果情况需要,您为什么要避免此过程?您不应该使用非安全服务器吗



    我目前使用的完整解决方案:

  • 在服务器上使用certbot生成证书。我使用命令“certbot certonly-d myawesomedomain.com”
  • 我使用以下代码将certbot证书转换为java SSLContext:
  • packagebowser;
    导入静态com.google.common.base.premissions.checkState;
    导入静态ox.util.Utils.propagate;
    导入java.io.File;
    导入java.security.KeyStore;
    导入javax.net.ssl.KeyManagerFactory;
    导入javax.net.ssl.SSLContext;
    导入javax.net.ssl.TrustManagerFactory;
    导入com.google.common.base.Splitter;
    进口牛油;
    输入牛日志;
    公共类方案{
    公共静态SSLContext createContext(字符串域){
    字符串pass=“spamspam”;
    File dir=新文件(“/etc/letsencrypt/live/”+域);
    如果(!dir.exists()){
    Log.warn(“找不到letsencrypt目录:“+dir”);
    返回null;
    }
    File keystoreFile=新文件(dir,“keystore.jks”);
    File pemFile=新文件(dir,“fullchain.pem”);
    布尔generateKeystore=false;
    if(keystoreFile.exists()){
    if(keystoreFile.lastModified()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;
    }
    
    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());
    
    package bowser;
    
    import static com.google.common.base.Preconditions.checkState;
    import static ox.util.Utils.propagate;
    
    import java.io.File;
    import java.security.KeyStore;
    
    import javax.net.ssl.KeyManagerFactory;
    import javax.net.ssl.SSLContext;
    import javax.net.ssl.TrustManagerFactory;
    
    import com.google.common.base.Splitter;
    
    import ox.IO;
    import ox.Log;
    
    public class SSLUtils {
    
      public static SSLContext createContext(String domain) {
        String pass = "spamspam";
    
        File dir = new File("/etc/letsencrypt/live/" + domain);
        if (!dir.exists()) {
          Log.warn("Could not find letsencrypt dir: " + dir);
          return null;
        }
    
        File keystoreFile = new File(dir, "keystore.jks");
        File pemFile = new File(dir, "fullchain.pem");
    
        boolean generateKeystore = false;
    
        if (keystoreFile.exists()) {
          if (keystoreFile.lastModified() < pemFile.lastModified()) {
            Log.info("SSUtils: It looks like a new PEM file was created. Regenerating the keystore.");
            keystoreFile.delete();
            generateKeystore = true;
          }
        } else {
          generateKeystore = true;
        }
    
        if (generateKeystore) {
          Splitter splitter = Splitter.on(' ');
          try {
            String command = "openssl pkcs12 -export -out keystore.pkcs12 -in fullchain.pem -inkey privkey.pem -passout pass:"
                + pass;
            Log.debug(command);
            Process process = new ProcessBuilder(splitter.splitToList(command))
                .directory(dir).inheritIO().start();
            checkState(process.waitFor() == 0);
    
            command = "keytool -importkeystore -srckeystore keystore.pkcs12 -srcstoretype PKCS12 -destkeystore keystore.jks -srcstorepass "
                + pass + " -deststorepass " + pass;
            Log.debug(command);
            process = new ProcessBuilder(splitter.splitToList(command))
                .directory(dir).inheritIO().start();
            checkState(process.waitFor() == 0);
    
            new File(dir, "keystore.pkcs12").delete();// cleanup
          } catch (Exception e) {
            throw propagate(e);
          }
        }
    
        try {
          KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
          keystore.load(IO.from(keystoreFile).asStream(), pass.toCharArray());
    
          KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
          keyManagerFactory.init(keystore, pass.toCharArray());
    
          SSLContext ret = SSLContext.getInstance("TLSv1.2");
          TrustManagerFactory factory = TrustManagerFactory.getInstance(
              TrustManagerFactory.getDefaultAlgorithm());
          factory.init(keystore);
          ret.init(keyManagerFactory.getKeyManagers(), factory.getTrustManagers(), null);
    
          return ret;
        } catch (Exception e) {
          throw propagate(e);
        }
      }
    
    }
    
    
    final String ca1 = "..load PEM file in string..";
    final CertificateFactory cf = CertificateFactory.getInstance("X.509");
    final Certificate cert1 = cf.generateCertificate(new ByteArrayInputStream(ca1.getBytes()));
    final KeyStore keyStore = KeyStore.getInstance("JKS");
    keyStore.load(null, null);
    // Can add multiple truststore certificates here...
    keyStore.setCertificateEntry("my-ca-1", cert1);
    final SSLContext sslContext = SSLContexts.custom().setKeyStoreType(saToken)
            .loadTrustMaterial(keyStore, null)
            .build();
    
    X509ExtendedKeyManager keyManager = PemUtils.loadIdentityMaterial("certificate-chain.pem", "private-key.pem", "private-key-password".toCharArray());
    X509ExtendedTrustManager trustManager = PemUtils.loadTrustMaterial("some-trusted-certificate.pem");
    
    SSLFactory sslFactory = SSLFactory.builder()
              .withIdentityMaterial(keyManager)
              .withTrustMaterial(trustManager)
              .build();
    
    SSLContext sslContext = sslFactory.getSslContext();