import java.io.FileOutputStream;import java.io.InputStream;import java.nio.file.Files;import java.nio.file.Path;import java.security.KeyFactory;import java.security.KeyStore;import java.security.PrivateKey;import java.security.cert.Certificate;import java.security.cert.CertificateFactory;import java.security.spec.PKCS8EncodedKeySpec;import java.util.Base64; public class CreateP12 { public static void main(String[] args) throws Exception { char[] password = System.getenv("P12_PASSWORD").toCharArray(); String keyPem = Files.readString(Path.of("key.pem")); byte[] pkcs8 = Base64.getMimeDecoder().decode(keyPem.replaceAll("-----[^-]+-----", "")); PrivateKey key = KeyFactory.getInstance("EC").generatePrivate(new PKCS8EncodedKeySpec(pkcs8)); CertificateFactory x509 = CertificateFactory.getInstance("X.509"); Certificate leaf, intermediate; try (InputStream a = Files.newInputStream(Path.of("leaf.pem")); InputStream b = Files.newInputStream(Path.of("intermediate.pem"))) { leaf = x509.generateCertificate(a); intermediate = x509.generateCertificate(b); } KeyStore p12 = KeyStore.getInstance("PKCS12"); p12.load(null, null); // a new, empty store // The alias becomes the friendly name; the chain is leaf first p12.setKeyEntry("api.example.com", key, password, new Certificate[] {leaf, intermediate}); try (FileOutputStream out = new FileOutputStream("bundle.p12")) { p12.store(out, password); } }} import java.io.FileInputStream;import java.security.KeyStore;import java.security.PrivateKey;import java.security.cert.Certificate;import java.security.cert.X509Certificate;import java.util.Collections; public class ReadP12 { public static void main(String[] args) throws Exception { char[] password = System.getenv("P12_PASSWORD").toCharArray(); KeyStore p12 = KeyStore.getInstance("PKCS12"); try (FileInputStream in = new FileInputStream("bundle.p12")) { p12.load(in, password); // checks the MAC: a wrong password fails here } for (String alias : Collections.list(p12.aliases())) { PrivateKey key = (PrivateKey) p12.getKey(alias, password); Certificate[] chain = p12.getCertificateChain(alias); System.out.println("alias " + alias + ": " + key.getAlgorithm() + " key, chain of " + chain.length); for (Certificate cert : chain) { System.out.println(" " + ((X509Certificate) cert).getSubjectX500Principal().getName()); } } }} How it works
KeyStore.getInstance("PKCS12")followed byload(null, null)creates an empty store;setKeyEntry(alias, key, password, chain)adds the key with its chain, leaf first.store(out, password)writes the file. JDK 25 protects it with PBES2 — PBKDF2-HMAC-SHA256 and AES-256-CBC, 10 000 iterations — and an HMAC-SHA-256 MAC, as OpenSSL's-infoconfirms in the run.- The alias becomes the PKCS#12 friendly name (
api.example.com), which is whatkeytool -listand OpenSSL show. - Reading is
load(in, password)followed bygetKey(alias, password)andgetCertificateChain(alias).
Gotchas
- A wrong password fails in
load()withIOException: keystore password was incorrect, before any entry is touched. - The JDK reads old RC2-protected files without any switch — the run loads one written by
openssl pkcs12 -legacy— while OpenSSL 3 needs-legacyfor them. A Java service that reads a bundle fine is no proof that OpenSSL-based software can. - The protection follows the JDK: JDK 25 writes AES-256 with 10 000 iterations, as
-infoshows, while older JDKs wrote RC2 and 3DES. Check what a long-lived build server actually produces before you hand the file to other software.
Related recipes
- How to create a PKCS#12 (.p12/.pfx) file from a key and certificate chain in Kotlin
- How to create a PKCS#12 (.p12/.pfx) file from a key and certificate chain in C# (.NET)
- How to create a PKCS#12 (.p12/.pfx) file from a key and certificate chain with OpenSSL
- How to convert a certificate and a private key between PEM and DER in Java
- How to verify a certificate chain against your own trust store in Java