Pular para o conteúdo principal

Gerar uma chave e um arquivo PKCS #12 - PKI Express

O PKI Express pode gerar um par de chaves RSA no seu servidor, com uma requisição de assinatura de certificado (CSR) para ele, e, depois que uma AC emitir o certificado, juntar a chave e o certificado num arquivo PKCS #12 (.pfx) com o qual você pode assinar. Duas classes fazem isso: KeyGenerator, que executa o comando gen-key, e Pkcs12Generator, que executa o create-pfx.

  • Os dois comandos exigem o PKI Express 1.11.0 ou posterior. As bibliotecas cliente pedem essa versão; com um PKI Express mais antigo, a operação falha com uma dica que informa a versão (veja Códigos de retorno).
  • As bibliotecas de Java, Node.js, PHP e Python têm essas classes; a de Ruby (pki_express 1.3.2) não tem.
  • Os samples do PKI Suite as usam para emitir um certificado com o Amplia e depois assinar um PDF com ele (Issue a certificate with the key on the server e Sign a PDF with a server key).

Fluxo​

  1. KeyGenerator.generate(), com genCsr definido, devolve a chave privada e um CSR.
  2. Guarde a chave no seu servidor e envie o CSR à sua AC, como o Amplia, que devolve o certificado.
  3. Pkcs12Generator.generate() recebe a chave e o arquivo do certificado e devolve o arquivo PKCS #12, protegido pela senha que você informar.

Em PHP, com o pacote lacuna/pki-express (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()

Os samples chamam Util::setPkiDefaults() nos dois objetos, como em todo operador do PKI Express, e guardam a chave e o certificado entre os passos 1 e 3. Eles alertam que a chave privada deve ser criptografada antes de ser guardada.

Opções da geração de chave​

OpçãoPHPValoresPadrão
Tamanho da chave, em bitskeySizeSupportedKeySizes::S1024, S2048, S4096 (1024, 2048, 4096)2048
Formato da chavekeyFormat, ou o argumento de generate()KeyFormats::JSON (json), KeyFormats::BLOB (blob), KeyFormats::XML (xml)json
CSRgenCsrtrue para gerar um CSR; deixe sem valor para gerar só a chavesem CSR
  • A chave é RSA. Só 1024, 2048 e 4096 bits são aceitos: as bibliotecas cliente recusam outro tamanho antes de executar o PKI Express (o PHP lança uma RuntimeException), e o próprio PKI Express devolve 36 - KeySizeNotSupported.
  • Os três formatos guardam a mesma chave privada, serializada de formas diferentes: json é um objeto JSON com os parâmetros RSA, xml um documento <RSAKeyValue> e blob um blob de chave CSP em Base64. Os samples usam json.
  • O CSR é uma requisição PKCS #10, codificada em PEM e assinada com SHA-256 com RSA; ele leva só a chave pública, e por isso o subject do certificado vem da AC (no Amplia, do pedido).
  • generate() devolve um KeyGenerationResult com duas strings: key e csr (null sem genCsr).

Opções do PKCS #12​

EntradaPHP
A chave, como o KeyGenerator a devolveu, em qualquer dos três formatospropriedade key (ou o primeiro argumento do construtor)
O arquivo do certificadosetCertFile($path) (alias de setCertFileFromPath), setCertFileFromContentRaw($bytes), setCertFileFromContentBase64($base64)
A senha do arquivo PKCS #12argumento de generate($password), ou a propriedade password
  • O PKI Express reconhece o formato da chave pelo conteúdo; uma chave que ele não consegue ler dá 37 - BadKeyFormat ou 38 - BadKey.
  • Sem senha, o arquivo PKCS #12 é criado com uma senha vazia.
  • generate() devolve um Pkcs12GenerationResult cujo pfx é um Pkcs12Certificate: getContentRaw() dá os bytes, getContentBase64() o Base64, e writeToFile($path) grava o arquivo. Para assinar com ele, passe os bytes a setPkcs12Content() e a senha a setCertPassword(), como faz o sample de assinatura PAdES com chave no servidor.
  • A biblioteca cliente passa a chave e a senha ao pkie como argumentos de linha de comando.
  • Se você preferir que a AC gere a chave, o Amplia pode devolver o próprio arquivo PKCS #12: veja Emitir um arquivo PKCS #12.

Em cada linguagem​

As classes têm o mesmo nome em todas as bibliotecas que as têm; os setters e os objetos de resultado mudam:

BibliotecaOpções da chaveResultado da chaveCertificado e senhaResultado do PKCS #12
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) (também byte[], InputStream), generate(password)getPfxContent() (byte[]), getPfxContentBase64()
Node.jsconstrutor { keySize, keyFormat, genCsr }, ou os setterskey, csrconstrutor { 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 e Python usam por padrão uma chave de 2048 bits no formato json; o PHP não envia nenhuma opção, e o PKI Express aplica os mesmos padrões.
  • Em Python, defina key_size com as constantes de SupportedKeySizes, que são strings ('2048'); em Node.js, keySize aceita 2048 ou '2048'.
  • Todas as bibliotecas exportam KeyFormats; SupportedKeySizes, só PHP e Python.
  • Em Node.js, generate() devolve uma 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);

Erros​

Um comando que falha dá um dos códigos de retorno do PKI Express: 36 - KeySizeNotSupported, 37 - BadKeyFormat ou 38 - BadKey nesses comandos, e 04 - BadCertificate quando não é possível decodificar o arquivo do certificado. Em PHP, trate a CommandException e leia $e->returnCode: veja Códigos de retorno nas bibliotecas cliente.

TODO(docs): se o create-pfx verifica se o certificado corresponde à chave, e qual código de retorno ele dá quando não corresponde.

Veja também​

Ver em Markdown