Pular para o conteúdo
ecdsa.com

PKCS#1 vs PKCS#8: BEGIN RSA PRIVATE KEY ou PRIVATE KEY

Os dois guardam os mesmos números RSA. O PKCS#8 acrescenta um identificador de algoritmo e criptografia opcional, e é por isso que toda biblioteca moderna o prefere e algumas recusam qualquer outro formato.

PKCS#1 vs PKCS#8 é a questão por trás de muitos erros de “unsupported key format”, que costumam surgir quando uma chave gerada anos atrás com openssl genrsa encontra uma biblioteca moderna. Um arquivo PKCS#1 (-----BEGIN RSA PRIVATE KEY-----) contém apenas uma estrutura de chave RSA; um arquivo PKCS#8 (-----BEGIN PRIVATE KEY-----) envolve a chave privada de qualquer algoritmo com um identificador de algoritmo, e a forma criptografada dele (-----BEGIN ENCRYPTED PRIVATE KEY-----) acrescenta proteção por senha padronizada.

Visão geral

PKCS#1 RSAPrivateKeyPKCS#8 PrivateKeyInfo / OneAsymmetricKey
EspecificaçãoRFC 8017, apêndice A.1.2RFC 5208, RFC 5958
Rótulo PEMRSA PRIVATE KEYPRIVATE KEY; criptografada: ENCRYPTED PRIVATE KEY
AlgoritmosApenas RSAQualquer um: RSA, EC, Ed25519, X25519, ML-DSA, ML-KEM …
Identificador de algoritmoNenhum (implícito no rótulo)rsaEncryption, id-ecPublicKey, 1.3.101.112 …
CriptografiaCabeçalhos PEM específicos do OpenSSL (Proc-Type, DEK-Info), KDF fracaPBES2 padronizado (PBKDF2 mais AES), conforme a RFC 8018
Saída do OpenSSL 3Só com -traditionalPadrão em genpkey, pkey, rsa
PKCS8EncodedKeySpec do JavaNão aceitoAceito
Gox509.ParsePKCS1PrivateKeyx509.ParsePKCS8PrivateKey
createPrivateKey do Node.jsAceito (rótulo PEM detectado)Aceito
importKey da WebCryptoNão aceitoFormato pkcs8

Os equivalentes para chaves públicas são o RSAPublicKey do PKCS#1 (BEGIN RSA PUBLIC KEY) e o SubjectPublicKeyInfo (BEGIN PUBLIC KEY); este último é o que todo o resto espera.

O que há dentro

Uma chave privada RSA PKCS#1 é uma SEQUENCE DER com versão, módulo n, expoente público e, expoente privado d, os primos p e q e os valores CRT dP, dQ e qInv. Como a estrutura não diz nada sobre o algoritmo, quem precisa dizer é o rótulo PEM; não existe forma PKCS#1 para chaves de curva elíptica (o formato análogo do OpenSSL, BEGIN EC PRIVATE KEY, vem do SEC 1, RFC 5915).

O PKCS#8 envolve uma chave específica de um algoritmo em um PrivateKeyInfo: uma versão, um AlgorithmIdentifier e uma OCTET STRING com a chave interna, que, no caso do RSA, é exatamente a estrutura PKCS#1. A RFC 5958 (versão 2, OneAsymmetricKey) também pode carregar a chave pública. O EncryptedPrivateKeyInfo acrescenta um algoritmo de criptografia e o texto cifrado; o OpenSSL 3.6 usa PBES2 com PBKDF2-HMAC-SHA256 e AES-256-CBC quando você pede criptografia.

Conversão

O script abaixo, executado com o OpenSSL 3.6.5, mostra os padrões e uma conversão de ida e volta sem perdas.

OpenSSL 3.6: PKCS#8, PKCS#1 e PKCS#8 criptografado
#!/bin/sh
dir=$(mktemp -d)
cd "$dir" || exit 1
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out pkcs8.pem 2>/dev/null
openssl pkey -in pkcs8.pem -traditional -out pkcs1.pem
openssl pkey -in pkcs8.pem -aes256 -passout pass:demo -out enc.pem
head -1 pkcs8.pem; head -1 pkcs1.pem; head -1 enc.pem
openssl pkcs8 -topk8 -nocrypt -in pkcs1.pem -out back.pem && head -1 back.pem
cmp -s pkcs8.pem back.pem && echo "round trip PKCS#1 -> PKCS#8: identical"
cd / && rm -rf "$dir"
Saída
-----BEGIN PRIVATE KEY-----
-----BEGIN RSA PRIVATE KEY-----
-----BEGIN ENCRYPTED PRIVATE KEY-----
-----BEGIN PRIVATE KEY-----
round trip PKCS#1 -> PKCS#8: identical

Os mesmos comandos convertem chaves EC SEC 1 (BEGIN EC PRIVATE KEY) para PKCS#8. As chaves privadas do OpenSSH (BEGIN OPENSSH PRIVATE KEY) são um terceiro formato; ssh-keygen -p -m PKCS8 -f key regrava uma delas no próprio arquivo como PKCS#8.

Erros que você vai encontrar

O Java é o leitor mais rígido: PKCS8EncodedKeySpec aceita apenas PKCS#8, e o texto do erro mudou entre as versões. O programa abaixo carrega um arquivo PEM de cada tipo, e os comandos seguintes o executam no Java 17, 21 e 25 nas imagens oficiais do Eclipse Temurin.

LoadKey.java: carregar chaves privadas PEM com PKCS8EncodedKeySpec
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.KeyFactory;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;

public class LoadKey {
  public static void main(String[] args) throws Exception {
    System.out.println("== Java " + Runtime.version());
    for (String file : args) {
      String pem = Files.readString(Path.of(file));
      byte[] der = Base64.getMimeDecoder().decode(pem.replaceAll("-----[A-Z ]+-----", ""));
      try {
        KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(der));
        System.out.println(file + ": loaded");
      } catch (Exception e) {
        System.out.println(file + ": " + e);
        for (Throwable t = e.getCause(); t != null; t = t.getCause()) System.out.println("  Caused by: " + t);
      }
    }
  }
}
Execute no Java 17, 21 e 25, ao lado de uma chave PKCS#8 e de uma PKCS#1
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out pkcs8.pem 2>/dev/null
openssl pkey -in pkcs8.pem -traditional -out pkcs1.pem
for v in 17.0.20.1_1 21.0.12.1_1 25.0.4.1_1; do
  docker run --rm -v "$PWD":/work:ro -w /work "eclipse-temurin:$v-jdk-noble" java LoadKey.java pkcs8.pem pkcs1.pem
done
Saída (20 de outubro de 2026)
== Java 17.0.20.1+1
pkcs8.pem: loaded
pkcs1.pem: java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: IOException : algid parse error, not a sequence
  Caused by: java.security.InvalidKeyException: IOException : algid parse error, not a sequence
== Java 21.0.12.1+1-LTS
pkcs8.pem: loaded
pkcs1.pem: java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: Unable to decode key
  Caused by: java.security.InvalidKeyException: Unable to decode key
  Caused by: java.io.IOException: algid parse error, not a sequence
== Java 25.0.4.1+1-LTS
pkcs8.pem: loaded
pkcs1.pem: java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: Unable to decode key
  Caused by: java.security.InvalidKeyException: Unable to decode key
  Caused by: java.io.IOException: algid parse error, not a sequence

Respostas mais antigas na web citam o texto do Java 17; no Java 21 e no 25, a mensagem de nível superior é Unable to decode key, e “algid parse error, not a sequence” sobrevive apenas na causa. Procure as duas nos logs.

Os outros runtimes, reproduzidos no mesmo dia com chaves geradas pelo OpenSSL 3.6.5:

  • Go: x509.ParsePKCS8PrivateKey retorna x509: failed to parse private key (use ParsePKCS1PrivateKey instead for this key format) (Go 1.25), o que diz exatamente o que aconteceu.
  • Node.js 26 (createPrivateKey, OpenSSL 3.6.5): um DER PKCS#1 declarado como type: "pkcs8" falha com error:068000A8:asn1 encoding routines::wrong tag (código ERR_OSSL_ASN1_WRONG_TAG); um corpo PKCS#1 sob um rótulo BEGIN PRIVATE KEY falha com error:1E08010C:DECODER routines::unsupported; os mesmos bytes PKCS#1 passados a createPublicKey como spki resultam em Failed to read asymmetric key. Uma chave criptografada carregada sem frase secreta falha com error:07880109:common libcrypto routines::interrupted or cancelled.
  • Chaves criptografadas com algoritmos legados: uma chave PEM tradicional criptografada com DES, ou uma chave PKCS#8 sob PBES1 com MD5 e DES, só carrega no OpenSSL 3 com o provedor legacy. Sem ele, openssl pkey imprime error:0308010C:digital envelope routines:inner_evp_generic_fetch:unsupported seguido do algoritmo que não conseguiu obter (DES-CBC, ou PBKDF1 no caso do PBES1), e o Node.js informa error:0308010C:digital envelope routines::unsupported (ERR_OSSL_EVP_UNSUPPORTED).
  • Python (cryptography 46): load_pem_private_key lê PKCS#1 e PKCS#8 da mesma forma e lança TypeError: Password was not given but private key is encrypted para uma chave criptografada sem senha.
  • WebCrypto: importKey("pkcs8", …) com bytes PKCS#1 é rejeitado com um DataError; o Node.js 26 formula a mensagem como Invalid keyData, e os navegadores usam o próprio texto.

O catálogo de erros tem uma página para cada um, com links abaixo.

A mesma divisão nas chaves públicas

As chaves públicas têm as mesmas duas formas. O RSAPublicKey do PKCS#1 (BEGIN RSA PUBLIC KEY) contém apenas o módulo e o expoente; o SubjectPublicKeyInfo (BEGIN PUBLIC KEY, RFC 5280) envolve a chave com um identificador de algoritmo, funciona com qualquer algoritmo e é o que esperam os certificados, os conversores de JWK, o X509EncodedKeySpec do Java, o ParsePKIXPublicKey do Go e a importação spki da WebCrypto. openssl rsa -pubin -in key.pem -RSAPublicKey_out e openssl pkey -pubin -in key.pem -pubout convertem entre os dois. A confusão vai nos dois sentidos: ferramentas que gravam SPKI muitas vezes dão ao arquivo um nome no estilo id_rsa.pub, e as chaves públicas de uma linha do próprio OpenSSH (ssh-rsa AAAA…) são mais um formato, que pode ser convertido com ssh-keygen -e -m PKCS8.

Quando escolher cada um

  • Chaves novas, qualquer algoritmo: PKCS#8 (criptografado em disco, sem criptografia dentro de um gerenciador de segredos que já criptografa).
  • Java, Go, .NET, WebCrypto, importações em KMS de nuvem: PKCS#8 (DER ou PEM).
  • Software antigo que só lê RSA PRIVATE KEY (alguns appliances, código Ruby e PHP mais antigo, ferramentas SSH legadas): PKCS#1 via -traditional, de preferência só no ponto de entrega.
  • Chave mais certificado para o Windows, o macOS ou keystores Java: PKCS#12, que contém chaves PKCS#8 internamente.
  • Chaves SSH: o formato do OpenSSH para o próprio ssh; PKCS#8 quando outra ferramenta precisa da mesma chave.

Notas de migração

  1. Encontre os arquivos PKCS#1 e SEC 1: grep -l "BEGIN RSA PRIVATE KEY\|BEGIN EC PRIVATE KEY" nos repositórios de configuração e nos cofres de segredos.
  2. Converta uma vez com openssl pkcs8 -topk8 -nocrypt (ou -v2 aes-256-cbc para criptografar) e atualize os consumidores; o material da chave e o certificado correspondente não mudam.
  3. Troque a criptografia das chaves antigas: a criptografia PEM tradicional (cabeçalhos Proc-Type e DEK-Info) deriva a chave com uma única iteração de MD5, e chaves criptografadas com DES ou RC2, em qualquer um dos dois formatos, precisam do provedor legacy do OpenSSL; criptografe-as de novo com PBES2 e AES-256.
  4. Atualize o código para os parsers genéricos (ParsePKCS8PrivateKey, load_pem_private_key, createPrivateKey), para que novos algoritmos, incluindo Ed25519 e ML-DSA, funcionem sem mudanças no código.
  5. Verifique assinando com a chave convertida e conferindo a assinatura com o certificado existente; o gerador de chaves e o explorador de ASN.1 mostram a estrutura sem enviar nada.

Referências

Todas as comparações