If https://docs.aws.amazon.com/acm/latest/APIReference/API_ExportCertificate.html is what you are using, it describes and shows these values as being in PEM formats, which is consistent with your use in openssl pkcs12 -export
. However the spec says that privatekey is labelled BEGIN/END PRIVATE KEY
which is PKCS8-unencrypted, while the example shows ENCRYPTED PRIVATE KEY
which is (bet you couldn't guess!) PKCS8-encrypted, although the example has clear errors that make me distrust it.
The leaf certificate and chain are easy, just feed them to CertificateFactory
which can handle either PEM or 'DER' (binary). To put them in a Java keystore (of any format), you need to combine them in a single array, leaf-then-chain:
// note: use java.security.cert.Certificate, not obsoleted java.security.Certificate or javax.security.cert.Certificate
CertificateFactory cf = CertificateFactory.getInstance("X.509");
Certificate leaf = cf.generateCertificate(new ByteArrayInputStream(certString.getBytes()));
Collection<Certificate> chain = cf.generateCertificates(new ByteArrayInputStream(chainString.getBytes()));
// for general data String.getBytes() omitting/defaulting charset
// can be dangerous, but PEM data is a strict subset of ASCII and safe
Certificate[] combine = chain.toArray( new Certificate[chain.size()+1] );
System.arraycopy(combine,0,combine,1,combine.length-1);
combine[0] = leaf;
or it can be easier to combine the inputs, but the specs indicate they might not provide the final linebreak (on the PEM END line) of the cert; if so you must add it to have a valid PEM sequence:
String temp = certString.endsWith("\n")? certString: certString + "\n";
Certificate[] combine = CertificateFactory.getInstance("X.509")
.generateCertificates(new ByteArrayInputStream(temp+chainString))
.toArray(new Certificate[0]);
If the privatekey is in fact PKCS8-unencrypted, it's almost as easy. KeyFactory
handles that, but only as 'DER' not PEM, so you need to undo the PEM 'wrapping'. One popular way is
String justb64 = privkeyString.replaceAll("-----(BEGIN|END) PRIVATE KEY-----","").replaceAll("\\r?\\n","");
byte[] binary = Base64.getDecoder().decode(justb64);
// or can leave the linebreaks in the data and use getMimeDecoder()
// prior to j8 other base64 decoders like Apache commons were popular,
// although in a pinch you can write your own by hand
// Java wants to know the algorithm of the key _before_ parsing it;
// since we have a known-matching cert, we can use that
PrivateKey pkey = KeyFactory.getInstance(combine[0].getPublicKey().getAlgorithm())
.generatePrivate(new PKCS8EncodedKeySpec(binary));
Another method is something like
String[] lines = privkeyString.split("\r?\n");
// or if reading from a file use BufferedReader or nio.Files.readAllLines
// may want to check that lines[0] and lines[lines.length-1] are in fact
// the desired BEGIN and END lines, if there is any chance the data is wrong
String justb64 = String.join("",Arrays.copyOfRange(lines,1,lines.length-1));
// continue as above
You can now put these in a PKCS12 keystore with
KeyStore p12 = KeyStore.getInstance("PKCS12"); p12.load(null);
p12.setKeyEntry(alias, privkey, password, combine);
p12.store(/*OutputStream to desired file or other writable location*/, password);
If the privatekey is in fact encrypted, standard Java can't easily read it as a key. However, if it is encrypted with one of the algorithms supported by (your) Java in a PKCS12 store, as a bypass you can use the 'pre-protected' API:
byte[] priv_pkcs8enc = // un-PEM privkeyString as before, but _don't_ pass to KeyFactory
...
p12.setKeyEntry(alias, priv_pkcs8enc, combine);
I say 'your' Java, because the set of PKCS8 encryptions supported by Java has varied over time (versions) and across implementations (i.e. providers), and I suspect will continue to.
If the privatekey is encrypted with an algorithm not supported in PKCS12 by your Java, you're probably out of luck with standard (Oracle, OpenJDK, etc) Java. But if you want to pursue that, add to your Q details of a test key -- bearing in mind if Amazon doesn't document what algorithm(s?) they use, it might vary (perhaps across regions or service offerings) and might change in the future.