Programmatically Obtain KeyStore from PEM

蹲街弑〆低调 提交于 2019-11-27 18:47:50

I figured it out. The problem is that the X509Certificate by itself isn't sufficient. I needed to put the private key into the dynamically generated keystore as well. It doesn't seem that BouncyCastle PEMReader can handle a PEM file with both cert and private key all in one go, but it can handle each piece separately. I can read the PEM into memory myself and break it into two separate streams and then feed each one to a separate PEMReader. Since I know that the PEM files I'm dealing with will have the cert first and the private key second I can simplify the code at the cost of robustness. I also know that the END CERTIFICATE delimiter will always be surrounded with five hyphens. The implementation that works for me is:

protected static SSLSocketFactory getSocketFactoryPEM(String pemPath) throws Exception {        
    Security.addProvider(new BouncyCastleProvider());

    SSLContext context = SSLContext.getInstance("TLS");

    byte[] certAndKey = fileToBytes(new File(pemPath));

    String delimiter = "-----END CERTIFICATE-----";
    String[] tokens = new String(certAndKey).split(delimiter);

    byte[] certBytes = tokens[0].concat(delimiter).getBytes();
    byte[] keyBytes = tokens[1].getBytes();

    PEMReader reader;

    reader = new PEMReader(new InputStreamReader(new ByteArrayInputStream(certBytes)));
    X509Certificate cert = (X509Certificate)reader.readObject();        

    reader = new PEMReader(new InputStreamReader(new ByteArrayInputStream(keyBytes)));
    PrivateKey key = (PrivateKey)reader.readObject();        

    KeyStore keystore = KeyStore.getInstance("JKS");
    keystore.load(null);
    keystore.setCertificateEntry("cert-alias", cert);
    keystore.setKeyEntry("key-alias", key, "changeit".toCharArray(), new Certificate[] {cert});

    KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
    kmf.init(keystore, "changeit".toCharArray());

    KeyManager[] km = kmf.getKeyManagers(); 

    context.init(km, null, null);

    return context.getSocketFactory();
}

Update: It seems this can be done without BouncyCastle:

    byte[] certAndKey = fileToBytes(new File(pemPath));
    byte[] certBytes = parseDERFromPEM(certAndKey, "-----BEGIN CERTIFICATE-----", "-----END CERTIFICATE-----");
    byte[] keyBytes = parseDERFromPEM(certAndKey, "-----BEGIN PRIVATE KEY-----", "-----END PRIVATE KEY-----");

    X509Certificate cert = generateCertificateFromDER(certBytes);              
    RSAPrivateKey key  = generatePrivateKeyFromDER(keyBytes);

...

protected static byte[] parseDERFromPEM(byte[] pem, String beginDelimiter, String endDelimiter) {
    String data = new String(pem);
    String[] tokens = data.split(beginDelimiter);
    tokens = tokens[1].split(endDelimiter);
    return DatatypeConverter.parseBase64Binary(tokens[0]);        
}

protected static RSAPrivateKey generatePrivateKeyFromDER(byte[] keyBytes) throws InvalidKeySpecException, NoSuchAlgorithmException {
    PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);

    KeyFactory factory = KeyFactory.getInstance("RSA");

    return (RSAPrivateKey)factory.generatePrivate(spec);        
}

protected static X509Certificate generateCertificateFromDER(byte[] certBytes) throws CertificateException {
    CertificateFactory factory = CertificateFactory.getInstance("X.509");

    return (X509Certificate)factory.generateCertificate(new ByteArrayInputStream(certBytes));      
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!