简体   繁体   中英

Load certificate to KeyStore (JAVA)

I have problem with loading certifiacate to key store. I can create that certificate in console with this command openssl pkcs12 -export -out cloudCA.p12 -inkey Cloud\\ privateLey.key -in cloudCa.pem -certfile rootCa.pem -name "cloudCA" I figured out how to load cloudCA.pem with privateKey.key but I can't find a way how to add there rootCA.pem. This is my current code. Thank 's for help.

//Regular patterns for certificate.
private static final Pattern CERT_PATTERN = Pattern.compile(
        "-+BEGIN\\s+.*CERTIFICATE[^-]*-+(?:\\s|\\r|\\n)+" + // Header
                "([a-z0-9+/=\\r\\n]+)" +                    // Base64 text
                "-+END\\s+.*CERTIFICATE[^-]*-+",            // Footer
        CASE_INSENSITIVE);

private static final Pattern KEY_PATTERN = Pattern.compile(
        "-+BEGIN\\s+.*PRIVATE\\s+KEY[^-]*-+(?:\\s|\\r|\\n)+" + // Header
                "([a-z0-9+/=\\r\\n]+)" +                       // Base64 text
                "-+END\\s+.*PRIVATE\\s+KEY[^-]*-+",            // Footer
        CASE_INSENSITIVE);


public static KeyStore loadKeyStore(String certificate, String privateKey, Optional<String> keyPassword)
        throws IOException, GeneralSecurityException {

    List<X509Certificate> certificateChain = readCertificateChain(certificate);
    if (certificateChain.isEmpty()) {
        throw new CertificateException("Certificate file string does not contain any certificates: ");
    }

    //Load and customize key string to byte array.
    byte[] data = Base64.getDecoder().decode(privateKey.replace("\n","")
            .replace("-----BEGIN RSA PRIVATE KEY-----", "")
            .replace("-----END RSA PRIVATE KEY-----", "")
            .replace(" ", ""));

    /* Add PKCS#8 formatting */
    ASN1EncodableVector v = new ASN1EncodableVector();
    v.add(new ASN1Integer(0));
    ASN1EncodableVector v2 = new ASN1EncodableVector();
    v2.add(new ASN1ObjectIdentifier(PKCSObjectIdentifiers.rsaEncryption.getId()));
    v2.add(DERNull.INSTANCE);
    v.add(new DERSequence(v2));
    v.add(new DEROctetString(data));
    ASN1Sequence seq = new DERSequence(v);
    byte[] privKey = seq.getEncoded("DER");

    PKCS8EncodedKeySpec spec = new  PKCS8EncodedKeySpec(privKey);
    KeyFactory fact = KeyFactory.getInstance("RSA");
    PrivateKey key = fact.generatePrivate(spec);

    KeyStore keyStore = KeyStore.getInstance("JKS");
    keyStore.load(null, null);
    keyStore.setKeyEntry("CloudCA", key, keyPassword.orElse("").toCharArray(), certificateChain.stream().toArray(Certificate[]::new));
    return keyStore;
}


private static List<X509Certificate> readCertificateChain(String contents) throws GeneralSecurityException {

    Matcher matcher = CERT_PATTERN.matcher(contents);
    CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
    List<X509Certificate> certificates = new ArrayList<>();

    int start = 0;
    while (matcher.find(start)) {
        byte[] buffer = Base64.getMimeDecoder().decode(matcher.group(1).getBytes(US_ASCII));
        certificates.add((X509Certificate) certificateFactory.generateCertificate(new ByteArrayInputStream(buffer)));
        start = matcher.end();
    }

    return certificates;
}

First, your PKCS12 file created with openssl pkcs12 -export is already a Java keystore; although Java versions below 9 default to JKS format for keystores they also support PKCS12, and j9 up now defaults to PKCS12. So you could just use the PKCS12 keystore and you're done. But SO is about programming so a solution that doesn't require doing anything at all might be off-topic :)

If you want (or somehow need) to build the keystore yourself, and you have the entity cert in one file (if really a subordinate CA as your name suggests, and directly under the root) and the root cert in another, you can read each of them in and form the chain, like:

// read privatekey, reformat to PKCS8 and process-in as you do now

CertificateFactory fact = CertificateFactory.getInstance("X.509");
InputStream file1 = new FileInputStream("mycert.pem"), file2 = new FileInputStream("rootcert.pem");
Certificate[] chain = { fact.generateCertificate(file1), fact.generateCertificate(file2) };
file1.close(); file2.close(); // or use try-with-resources
// if already in memory use ByteArrayInputStream's instead, 
// and maybe don't bother closing

// basically unchanged
KeyStore keyStore = KeyStore.getInstance("JKS"); // or maybe "PKCS12" ?
keyStore.load(null, null);
keyStore.setKeyEntry("name", key, (keypass), chain);

// either use this keystore as is, or store it (to a file, 
// or maybe somewhere else like a database) for later use

Note you don't need to remove the PEM header/trailer and convert base64 to binary yourself, CertificateFactory has been able to read PEM since last century. Although if your PEM files contain extra 'comment' info before the PEM block, which files created by OpenSSL often do, you need a fairly recent Java version (IIRC j6 or maybe j7) to handle that .

Alternatively if you concatenate the certs -- either in a file, or in memory -- you could use CertificateFactory.generateCertificates (note the s) to read both of them to a Collection which you then convert to an array. Again this handles PEM already so you don't have to parse and convert it. Note you can convert either a Collection or a List (as in your code above) to an array directly, without needing to go through a Stream first.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM