Programmatically Obtain KeyStore from PEM

前端 未结 1 1758
-上瘾入骨i
-上瘾入骨i 2020-12-05 00:06

How can one programmatically obtain a KeyStore from a PEM file containing both a certificate and a private key? I am attempting to provide a client certificate to a server i

相关标签:
1条回答
  • 2020-12-05 01:09

    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));      
    }
    
    0 讨论(0)
提交回复
热议问题