<?php$password = getenv('P12_PASSWORD'); $ok = openssl_pkcs12_export_to_file( file_get_contents('leaf.pem'), // the certificate 'bundle.p12', file_get_contents('key.pem'), // its private key $password, [ 'extracerts' => [file_get_contents('intermediate.pem')], // the chain, without the leaf 'friendly_name' => 'api.example.com', ],);if (!$ok) { throw new RuntimeException('PKCS#12 export failed: ' . openssl_error_string());} <?php$password = getenv('P12_PASSWORD'); if (!openssl_pkcs12_read(file_get_contents('bundle.p12'), $bundle, $password)) { throw new RuntimeException('cannot read bundle.p12: ' . openssl_error_string());}// $bundle['pkey'] and $bundle['cert'] are PEM strings, $bundle['extracerts'] a list of themecho 'certificate: ', openssl_x509_parse($bundle['cert'])['name'], "\n";foreach ($bundle['extracerts'] ?? [] as $extra) { echo 'chain: ', openssl_x509_parse($extra)['name'], "\n";}echo 'key matches certificate: ', var_export(openssl_x509_check_private_key($bundle['cert'], $bundle['pkey']), true), "\n"; echo 'wrong password: ', var_export(openssl_pkcs12_read(file_get_contents('bundle.p12'), $unused, 'wrong-password'), true);echo ' (', openssl_error_string(), ")\n"; How it works
openssl_pkcs12_export_to_file($cert, $file, $key, $password, $options)writes the bundle;extracertsholds the chain without the leaf,friendly_namethe alias.- The protection is OpenSSL's default: with OpenSSL 3.5 that is AES-256-CBC with PBKDF2-HMAC-SHA256 and an HMAC-SHA-256 MAC, 2048 iterations.
openssl_pkcs12_read($data, $out, $password)fills$out['pkey'],$out['cert']and$out['extracerts']with PEM strings that every other ext/openssl function accepts.openssl_x509_check_private_keyconfirms the key belongs to the certificate.
Gotchas
- Old RC2-protected files cannot be read on OpenSSL 3:
openssl_pkcs12_readreturnsfalsewitherror:0308010C:digital envelope routines::unsupported(the run reads a file written byopenssl pkcs12 -legacy). Convert such files once with OpenSSL's-legacyswitch. - Both functions return
falseon failure and never throw; check the result and readopenssl_error_string()— a wrong password reportserror:11800071:PKCS12 routines::mac verify failure.
Related recipes
- How to create a PKCS#12 (.p12/.pfx) file from a key and certificate chain in Ruby
- How to create a PKCS#12 (.p12/.pfx) file from a key and certificate chain with OpenSSL
- How to create a PKCS#12 (.p12/.pfx) file from a key and certificate chain in Python
- How to convert a certificate and a private key between PEM and DER in PHP
- How to verify a certificate chain against your own trust store in PHP