Pular para o conteúdo principal

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_express 1.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​

  1. KeyGenerator.generate() with genCsr set returns the private key and a CSR.
  2. Store the key on your server and send the CSR to your CA, such as Amplia, which returns the certificate.
  3. 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​

OptionPHPValuesDefault
Key size, in bitskeySizeSupportedKeySizes::S1024, S2048, S4096 (1024, 2048, 4096)2048
Key formatkeyFormat, or the argument of generate()KeyFormats::JSON (json), KeyFormats::BLOB (blob), KeyFormats::XML (xml)json
CSRgenCsrtrue to make a CSR; leave it unset for the key aloneno 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 returns 36 - KeySizeNotSupported.
  • The three formats hold the same private key, serialized differently: json is a JSON object of the RSA parameters, xml an <RSAKeyValue> document, and blob a Base64 CSP key blob. The samples use json.
  • 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 a KeyGenerationResult with two strings: key and csr (null without genCsr).

PKCS #12 options​

InputPHP
The key, as returned by KeyGenerator, in any of the three formatskey property (or the first constructor argument)
The certificate filesetCertFile($path) (alias of setCertFileFromPath), setCertFileFromContentRaw($bytes), setCertFileFromContentBase64($base64)
The password of the PKCS #12 fileargument of generate($password), or the password property
  • PKI Express recognizes the key format by its content; a key it cannot read gives 37 - BadKeyFormat or 38 - BadKey.
  • Without a password, the PKCS #12 file is created with an empty password.
  • generate() returns a Pkcs12GenerationResult whose pfx is a Pkcs12Certificate: getContentRaw() gives the bytes, getContentBase64() the Base64, and writeToFile($path) saves it. To sign with it, pass the bytes to setPkcs12Content() and the password to setCertPassword(), as the PAdES server-key sample does.
  • The client library passes the key and the password to pkie as 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:

LibraryKey optionsKey resultCertificate and passwordPKCS #12 result
PHPkeySize, keyFormat, genCsr$r->key, $r->csrkey, setCertFile($path), generate($password)$r->pfx->getContentRaw(), writeToFile($path)
JavasetKeySize(int), setKeyFormat(KeyFormats), setGenCsr(boolean)getKey(), getCsr()setKey, setCertFile(path) (also byte[], InputStream), generate(password)getPfxContent() (byte[]), getPfxContentBase64()
Node.jsconstructor { keySize, keyFormat, genCsr }, or the setterskey, csrconstructor { key }, setCertFileFromPathSync(path), generate(password)pfx.contentRaw (Buffer), pfx.contentBase64, pfx.writeToFileSync(path)
Pythonkey_size, key_format, gen_csrkey, csrkey, 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 json format; PHP sends no option, so PKI Express applies the same defaults.
  • In Python, set key_size with the SupportedKeySizes constants, which are strings ('2048'); in Node.js, keySize takes 2048 or '2048'.
  • KeyFormats is exported by every library; SupportedKeySizes by PHP and Python.
  • In Node.js, generate() returns a promise.
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);

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.

See also​

View as Markdown