Formats PEM en Python : clés privées, publiques et certificats avec la bibliothèque Cryptography

Présentation des formats PEM et DER

Le format PEM (Privacy Enhanced Mail) représente les certificats, clés privées et clés publiques sous forme de texte. Il se compose d'un encodage Base64 de données binaires, précédé et suivi d'en-têtes de délimitation.

Le format DER (Distinguished Encoding Rules) fournit une représentation binaire standardisée, souvent utilisée pour le stockage ou la transmission. La conversion PEM implique l'encodage Base64 des données DER avec des balises lisibles.

Structure PEM = Balise de début (--BEGIN TYPE--) + Données DER encodées en Base64 (avec sauts de ligne tous les 64 caractères) + Balise de fin (--END TYPE--)

Les extensions de fichier ne garantissent pas le format : .pem, .cer, .crt, .key peuvent contenir du PEM, tandis que .der indique généralement du DER. Le contenu détermine le format réel.

Identifiants PEM courants

Certificats

  • X.509 : -----BEGIN CERTIFICATE----- ... -----END CERTIFICATE-----
  • CSR (Certificate Signing Request) : -----BEGIN CERTIFICATE REQUEST----- ... -----END CERTIFICATE REQUEST-----
  • CRL (Certificate Revocation List) : -----BEGIN X509 CRL----- ... -----END X509 CRL-----

Clés privées

  • PKCS#1 (RSA) : -----BEGIN RSA PRIVATE KEY----- ... -----END RSA PRIVATE KEY----- (peut inclure des en-têtes d'encryption)
  • PKCS#1 (EC) : -----BEGIN EC PRIVATE KEY----- ... -----END EC PRIVATE KEY-----
  • PKCS#8 : -----BEGIN PRIVATE KEY----- ... -----END PRIVATE KEY----- ou -----BEGIN ENCRYPTED PRIVATE KEY----- ... -----END ENCRYPTED PRIVATE KEY-----
  • OpenSSH : -----BEGIN OPENSSH PRIVATE KEY----- ... -----END OPENSSH PRIVATE KEY-----

Clés publiques

  • PKCS#1 (RSA) : -----BEGIN RSA PUBLIC KEY----- ... -----END RSA PUBLIC KEY-----
  • SubjectPublicKeyInfo (X.509) : -----BEGIN PUBLIC KEY----- ... -----END PUBLIC KEY-----

Exemples de contenu PEM

Certificat X.509

-----BEGIN CERTIFICATE-----
[Données Base64 du certificat]
-----END CERTIFICATE-----

Liste de révocation de certificats (CRL)

-----BEGIN X509 CRL-----
[Données Base64 de la CRL]
-----END X509 CRL-----

Clé privée RSA (PKCS#1, non chiffrée)

-----BEGIN RSA PRIVATE KEY-----
[Données Base64 de la clé privée]
-----END RSA PRIVATE KEY-----

Clé privée RSA (PKCS#1, chiffrée)

-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-256-CBC,IV_INITIAL
[Données Base64 chiffrées]
-----END RSA PRIVATE KEY-----

Clé privée elliptique (EC, PKCS#8, non chiffrée)

-----BEGIN PRIVATE KEY-----
[Données Base64 de la clé EC]
-----END PRIVATE KEY-----

Clé publique RSA (PKCS#1)

-----BEGIN RSA PUBLIC KEY-----
[Données Base64 de la clé publique]
-----END RSA PUBLIC KEY-----

Chargement et sérialisation avec Python (bibliothèque Cryptography)

Certificat X.509

from cryptography import x509
from cryptography.hazmat.primitives import serialization

# Données PEM d'un certificat
cert_data = b'''-----BEGIN CERTIFICATE-----
[Contenu PEM du certificat]
-----END CERTIFICATE-----'''

# Chargement depuis le format PEM
loaded_certificate = x509.load_pem_x509_certificate(cert_data)

# Sérialisation vers PEM
pem_output = loaded_certificate.public_bytes(encoding=serialization.Encoding.PEM)
print(pem_output.decode())

Liste de révocation (CRL)

from cryptography import x509
from cryptography.hazmat.primitives import serialization

# Données PEM d'une CRL
crl_data = b'''-----BEGIN X509 CRL-----
[Contenu PEM de la CRL]
-----END X509 CRL-----'''

# Chargement depuis PEM
revocation_list = x509.load_pem_x509_crl(crl_data)

# Sérialisation vers PEM
crl_pem = revocation_list.public_bytes(encoding=serialization.Encoding.PEM)
print(crl_pem.decode())

Clé privée (chargement et conversion)

from cryptography.hazmat.primitives import serialization

# Clé privée non chiffrée (EC)
ec_key_data = b'''-----BEGIN EC PRIVATE KEY-----
[Contenu PEM de la clé EC]
-----END EC PRIVATE KEY-----'''

# Clé privée chiffrée (PKCS#8)
encrypted_key_data = b'''-----BEGIN ENCRYPTED PRIVATE KEY-----
[Contenu PEM chiffré]
-----END ENCRYPTED PRIVATE KEY-----'''

# Chargement sans mot de passe
unprotected_key = serialization.load_pem_private_key(ec_key_data, password=None)

# Chargement avec mot de passe
protected_key = serialization.load_pem_private_key(encrypted_key_data, password=b'secret')

# Sérialisation en PKCS#1 (format traditionnel OpenSSL) sans chiffrement
pkcs1_pem = unprotected_key.private_bytes(
    encoding=serialization.Encoding.PEM,
    format=serialization.PrivateFormat.TraditionalOpenSSL,
    encryption_algorithm=serialization.NoEncryption()
)
print(pkcs1_pem.decode())

# Sérialisation en PKCS#8 avec chiffrement
pkcs8_encrypted_pem = unprotected_key.private_bytes(
    encoding=serialization.Encoding.PEM,
    format=serialization.PrivateFormat.PKCS8,
    encryption_algorithm=serialization.BestAvailableEncryption(b'secret')
)
print(pkcs8_encrypted_pem.decode())

Clé publique (chargement et conversion)

from cryptography.hazmat.primitives import serialization

# Clé publique RSA (PKCS#1)
rsa_pub_data = b'''-----BEGIN RSA PUBLIC KEY-----
[Contenu PEM de la clé publique RSA]
-----END RSA PUBLIC KEY-----'''

# Chargement depuis PEM
public_key = serialization.load_pem_public_key(rsa_pub_data)

# Sérialisation en PKCS#1
pkcs1_format = public_key.public_bytes(
    encoding=serialization.Encoding.PEM,
    format=serialization.PublicFormat.PKCS1
)
print(pkcs1_format.decode())

# Sérialisation en SubjectPublicKeyInfo (X.509)
spki_format = public_key.public_bytes(
    encoding=serialization.Encoding.PEM,
    format=serialization.PublicFormat.SubjectPublicKeyInfo
)
print(spki_format.decode())

Étiquettes: Python cryptography pem der x509

Publié le 17 juin à 21h28