Generate a key and a PKCS #12 file - PKI Express
PKI Express can make an RSA key pair on your server, with a certificate signing request (CSR) for it, and, once a CA has
issued the certificate, combine the key and the certificate into a PKCS #12 (.pfx) file that you can sign with.
Two classes do this: KeyGenerator, which runs the gen-key command, and Pkcs12Generator, which runs create-pfx.
- Both commands need PKI Express 1.11.0 or later. The client libraries ask for that version; with an older PKI Express, the operation fails with a tip naming the version (see Return codes).
- The Java, Node.js, PHP and Python libraries have these classes; the Ruby library (
pki_express1.3.2) does not. - The PKI Suite samples use them to issue a certificate with Amplia and then sign a PDF with it (Issue a certificate with the key on the server and Sign a PDF with a server key).
Flow
KeyGenerator.generate()withgenCsrset returns the private key and a CSR.- Store the key on your server and send the CSR to your CA, such as Amplia, which returns the certificate.
Pkcs12Generator.generate()takes the key and the certificate file and returns the PKCS #12 file, protected by the password you give it.
In PHP, with the lacuna/pki-express package (1.17.0):
use Lacuna\PkiExpress\KeyGenerator;
use Lacuna\PkiExpress\KeyFormats;
use Lacuna\PkiExpress\Pkcs12Generator;
use Lacuna\PkiExpress\SupportedKeySizes;
// 1. Make the key pair and the CSR.
$keyGenerator = new KeyGenerator();
$keyGenerator->keySize = SupportedKeySizes::S2048;
$keyGenerator->keyFormat = KeyFormats::JSON;
$keyGenerator->genCsr = true;
$genKey = $keyGenerator->generate();
// $genKey->key: the private key, as a string in the chosen format
// $genKey->csr: the PKCS #10 CSR, PEM-encoded
// 2. Store $genKey->key (encrypted) and send $genKey->csr to the CA,
// which returns the certificate, saved here as $certPath.
// 3. Make the PKCS #12 file.
$pkcs12Generator = new Pkcs12Generator();
$pkcs12Generator->key = $genKey->key;
$pkcs12Generator->setCertFile($certPath);
$result = $pkcs12Generator->generate($password);
$result->pfx->writeToFile('/path/to/signer.pfx'); // or getContentRaw(), getContentBase64()
The samples call Util::setPkiDefaults() on both objects, as on every PKI Express operator, and keep the key and the certificate in
storage between steps 1 and 3.
They warn that the private key should be encrypted before it is stored.
Key generation options
| Option | PHP | Values | Default |
|---|---|---|---|
| Key size, in bits | keySize | SupportedKeySizes::S1024, S2048, S4096 (1024, 2048, 4096) | 2048 |
| Key format | keyFormat, or the argument of generate() | KeyFormats::JSON (json), KeyFormats::BLOB (blob), KeyFormats::XML (xml) | json |
| CSR | genCsr | true to make a CSR; leave it unset for the key alone | no CSR |
- The key is RSA. Only 1024, 2048 and 4096 bits are supported: the client libraries reject another size before running PKI Express
(PHP throws a
RuntimeException), and PKI Express itself returns36 - KeySizeNotSupported. - The three formats hold the same private key, serialized differently:
jsonis a JSON object of the RSA parameters,xmlan<RSAKeyValue>document, andbloba Base64 CSP key blob. The samples usejson. - The CSR is a PKCS #10 request, PEM-encoded, signed with SHA-256 with RSA; it carries only the public key, so the certificate's subject comes from the CA (in Amplia, from the order).
generate()returns aKeyGenerationResultwith two strings:keyandcsr(nullwithoutgenCsr).
PKCS #12 options
| Input | PHP |
|---|---|
The key, as returned by KeyGenerator, in any of the three formats | key property (or the first constructor argument) |
| The certificate file | setCertFile($path) (alias of setCertFileFromPath), setCertFileFromContentRaw($bytes), setCertFileFromContentBase64($base64) |
| The password of the PKCS #12 file | argument of generate($password), or the password property |
- PKI Express recognizes the key format by its content; a key it cannot read gives
37 - BadKeyFormator38 - BadKey. - Without a password, the PKCS #12 file is created with an empty password.
generate()returns aPkcs12GenerationResultwhosepfxis aPkcs12Certificate:getContentRaw()gives the bytes,getContentBase64()the Base64, andwriteToFile($path)saves it. To sign with it, pass the bytes tosetPkcs12Content()and the password tosetCertPassword(), as the PAdES server-key sample does.- The client library passes the key and the password to
pkieas command-line arguments. - If you would rather have the CA make the key, Amplia can return the PKCS #12 file itself: see Issue a PKCS #12 file.
In each language
The classes have the same names in every library that has them; the setters and the result objects differ:
| Library | Key options | Key result | Certificate and password | PKCS #12 result |
|---|---|---|---|---|
| PHP | keySize, keyFormat, genCsr | $r->key, $r->csr | key, setCertFile($path), generate($password) | $r->pfx->getContentRaw(), writeToFile($path) |
| Java | setKeySize(int), setKeyFormat(KeyFormats), setGenCsr(boolean) | getKey(), getCsr() | setKey, setCertFile(path) (also byte[], InputStream), generate(password) | getPfxContent() (byte[]), getPfxContentBase64() |
| Node.js | constructor { keySize, keyFormat, genCsr }, or the setters | key, csr | constructor { key }, setCertFileFromPathSync(path), generate(password) | pfx.contentRaw (Buffer), pfx.contentBase64, pfx.writeToFileSync(path) |
| Python | key_size, key_format, gen_csr | key, csr | key, set_cert_file_from_path(path), generate(password) | pfx.content (bytes), pfx.write_to_file(path) |
- Java, Node.js and Python default to a 2048-bit key in
jsonformat; PHP sends no option, so PKI Express applies the same defaults. - In Python, set
key_sizewith theSupportedKeySizesconstants, which are strings ('2048'); in Node.js,keySizetakes2048or'2048'. KeyFormatsis exported by every library;SupportedKeySizesby PHP and Python.- In Node.js,
generate()returns a promise.
- Java
- Node.js
- Python
KeyGenerator keyGenerator = new KeyGenerator();
keyGenerator.setKeySize(2048);
keyGenerator.setKeyFormat(KeyFormats.JSON);
keyGenerator.setGenCsr(true);
KeyGenerationResult genKey = keyGenerator.generate();
// genKey.getKey(), genKey.getCsr()
Pkcs12Generator pkcs12Generator = new Pkcs12Generator();
pkcs12Generator.setKey(keyJson); // the key from genKey.getKey(), read back from storage
pkcs12Generator.setCertFile(certPath);
Pkcs12GenerationResult pkcs12GenResult = pkcs12Generator.generate(password);
signer.setPkcs12(pkcs12GenResult.getPfxContent());
signer.setCertPassword(password);
const { KeyGenerator, KeyFormats, Pkcs12Generator } = require('pki-express');
const keyGenerator = new KeyGenerator({ keySize: 2048, keyFormat: KeyFormats.JSON, genCsr: true });
const genKey = await keyGenerator.generate();
// genKey.key, genKey.csr
const pkcs12Generator = new Pkcs12Generator({ key: keyJson }); // the key from genKey.key, read back from storage
pkcs12Generator.setCertFileFromPathSync(certPath);
const pkcs12GenResult = await pkcs12Generator.generate(password);
await signer.setPkcs12FromRaw(pkcs12GenResult.pfx.contentRaw);
signer.certPassword = password;
from pkiexpress import KeyGenerator, Pkcs12Generator, SupportedKeySizes
key_generator = KeyGenerator()
key_generator.key_size = SupportedKeySizes.S2048
key_generator.gen_csr = True
gen_key = key_generator.generate()
# gen_key.key, gen_key.csr
pkcs12_generator = Pkcs12Generator()
pkcs12_generator.key = gen_key.key
pkcs12_generator.set_cert_file_from_base64(cert.content_base64)
pkcs12_gen_result = pkcs12_generator.generate('1234')
pkcs12_gen_result.pfx.write_to_file('signer.pfx')
Errors
A failed command gives one of the PKI Express return codes: 36 - KeySizeNotSupported, 37 - BadKeyFormat or
38 - BadKey for these commands, 04 - BadCertificate when the certificate file cannot be decoded. In PHP, catch CommandException
and read $e->returnCode: see Return codes in the client libraries.
TODO(docs): whether create-pfx checks that the certificate matches the key, and which return code it gives when they do not match.