PKCS#8

PKCS#8 is a standard for encoding asymmetric private keys, such as RSA or ECC, so that they can be stored or exchanged. The private key can either be encrypted with a passphrase or left in the clear.

Example of how to encrypt an ECC private key (even though normally you would use the export_key method of the key itself):

from Crypto.PublicKey import ECC
from Crypto.IO import PKCS8

key = ECC.generate(curve='p256')
pkey = key.export_key(format='DER')
passphrase = b'secret santa'
encrypted_key = PKCS8.wrap(
                pkey,
                "1.2.840.10045.2.1",  # unrestricted ECC
                passphrase=passphrase,
                protection='PBKDF2WithHMAC-SHA256AndAES256-CBC'
                )

Encryption parameters

When creating an encrypted PKCS#8 container, the two parameters protection and prot_params drive the encryption algorithm:

  • protection (optional, the default is 'PBKDF2WithHMAC-SHA256AndAES256-CBC'), a string that defines how the encryption key is derived from the passphrase, and which cipher to use. The string must follow one of the two patterns:

    1. 'PBKDF2WithHMAC-' + hash + 'And' + cipher

    2. 'scryptAnd' + cipher

    where hash is the name of the cryptographic hash (recommended: 'SHA256') and cipher is the name of the cipher mode to use (recommended: 'AES256-CBC').

    Other values for hash are 'SHA1', 'SHA224', 'SHA256', 'SHA384', 'SHA512-224', 'SHA512-256', 'SHA3-224', 'SHA3-256', 'SHA3-384', 'SHA3-512'.

    Other values for cipher are 'AES128-GCM', 'AES192-GCM', 'AES256-GCM', 'AES128-CBC', 'AES192-CBC' or 'DES-EDE3-CBC'.

  • prot_params (optional), a dictionary to override the parameters of the key derivation function. The defaults meet the OWASP recommendations for scrypt and for PBKDF2 with SHA-256 or SHA-512 (but not SHA-1, which should not be used): there is no need to change them, except to make the key cheaper to decrypt (for instance, on a slow device).

    Key

    Description

    iteration_count

    The KDF algorithm is repeated several times to slow down brute force attacks on passwords (called N or CPU/memory cost in scrypt).

    The default value is 600 000 for PBKDF2 (whatever the hash), and 131 072 for scrypt.

    salt_size

    Salt is used to thwart dictionary and rainbow attacks on passwords. The default value is 8 bytes.

    block_size

    (scrypt only) Memory-cost (r). The default value is 8.

    parallelization

    (scrypt only) CPU-cost (p). The default value is 1.

With scrypt, the parallelization parameter (p) defines independent computations, which can run on several CPU cores: use the threads parameter of wrap() and unwrap(), of import_key() for RSA, DSA and ECC keys, and of export_key() for RSA and ECC keys. With threads=0, as many threads as the CPU cores available are used. Each thread needs its own block of memory (128 · r · N bytes), at the same time. The result does not depend on the number of threads.

Limits for decryption

To protect against malicious keys, unwrap() rejects keys whose key derivation is too expensive (parameters max_iteration_count and max_memory). See Limits when decrypting private keys for the default limits and how to change them.

Legacy algorithms

The following protection mechanisms are only supported for importing keys. They are much weaker than the ones listed above, and they are provided for backward compatibility only:

  • pbeWithMD5AndRC2-CBC

  • pbeWithMD5AndDES-CBC

  • pbeWithSHA1AndRC2-CBC

  • pbeWithSHA1AndDES-CBC

class Crypto.IO.PKCS8.UnwrappedKey(iterable=(), /)

The result of unwrap().

It is a tuple (oid, private_key, params), so that it can be indexed or unpacked into three variables, and it has the attributes listed below.

property oid: str

The algorithm identifier of the wrapped key (OID, dotted string).

property params: bytes | None

The parameters of the AlgorithmIdentifier (bytes, DER encoded), or None if they are absent or NULL.

property private_key: bytes

The private key (bytes, DER encoded).

public_key: Optional[bytes]

The publicKey field of a OneAsymmetricKey version 2 (RFC 5958), as bytes (the content of the BIT STRING), or None if it is absent. It is returned also if the version of the structure is 1, where the field is not allowed: it is up to the caller to reject it or to check that it matches the private key.

Crypto.IO.PKCS8.unwrap(p8_private_key, passphrase=None, max_iteration_count=None, *, threads=1, max_memory=None)

Unwrap a private key from a PKCS#8 blob (clear or encrypted).

Parameters:

p8_private_key (bytes) – The private key wrapped into a PKCS#8 container, DER encoded.

Keyword Arguments:
  • passphrase (byte string or string) – The passphrase to use to decrypt the blob (if it is encrypted).

  • max_iteration_count (integer) – The maximum value allowed for the PBKDF1/PBKDF2 iteration count, or for the scrypt cost N·r·p/8 (that is, N for the usual r=8 and p=1). An attacker could craft a malicious key blob that requires an extremely high number of iterations, tying up the CPU for hours. The default value is None: a limit for each KDF, which keeps the key derivation to about one minute at most (see the limits for decryption). Set it to 0 to disable the check.

  • threads (integer) – The maximum number of threads used to derive the unwrapping key with scrypt (default: 1, no extra threads; 0 for all CPU cores). Only a blob protected with scrypt and a parallelization parameter larger than 1 can use them. See Crypto.Protocol.KDF.scrypt().

  • max_memory (integer) – The maximum memory (in bytes) for the key derivation, as a limit on the total of all threads together (not on each thread). Only scrypt uses a significant amount: 128 · (N + 2) · r bytes for each thread, plus 256 · r · p bytes for all the flows together. If the requested threads do not fit, fewer are used (down to one); if a single thread does not fit, Crypto.KdfMemoryLimitError is raised. The default value is None (a limit of 256 MiB). Set it to 0 to disable the check.

Returns:

a tuple containing

  1. the algorithm identifier of the wrapped key (OID, dotted string)

  2. the private key (bytes, DER encoded)

  3. the associated parameters (bytes, DER encoded) or None (also if they are NULL)

with the same items as attributes, plus the attribute public_key.

Return type:

UnwrappedKey

Raises:
Crypto.IO.PKCS8.wrap(private_key, key_oid, passphrase=None, protection=None, prot_params=None, key_params=<Crypto.Util.asn1.DerNull object>, randfunc=None, *, threads=1, public_key=None)

Wrap a private key into a PKCS#8 blob (clear or encrypted).

Parameters:
  • private_key (bytes) – The private key encoded in binary form. The actual encoding is algorithm specific. In most cases, it is DER.

  • key_oid (string) – The object identifier (OID) of the private key to wrap. It is a dotted string, like '1.2.840.113549.1.1.1' (for RSA keys) or '1.2.840.10045.2.1' (for ECC keys).

Keyword Arguments:
  • passphrase (bytes or string) – The secret passphrase from which the wrapping key is derived. Set it only if encryption is required.

  • protection (string) – The identifier of the algorithm to use for securely wrapping the key. Refer to the encryption parameters . The default value is 'PBKDF2WithHMAC-SHA256AndAES256-CBC'.

  • prot_params (dictionary) – Parameters for the key derivation function (KDF). Refer to the encryption parameters .

  • key_params (DER object or None) – The parameters field to use in the AlgorithmIdentifier SEQUENCE. If None, no parameters field will be added. By default, the ASN.1 type NULL is used.

  • randfunc (callable) – Random number generation function; it should accept a single integer N and return a string of random data, N bytes long. If not specified, a new RNG will be instantiated from Crypto.Random.

  • threads (integer) – The maximum number of threads used to derive the wrapping key with scrypt (default: 1, no extra threads; 0 for all CPU cores). Only scrypt with a parallelization larger than 1 can use them. See Crypto.Protocol.KDF.scrypt().

  • public_key (bytes) – The public key, to store in the field publicKey. The actual encoding is algorithm specific. If set, the result is a OneAsymmetricKey version 2 (RFC 5958), otherwise it is a PrivateKeyInfo (version 1, the default).

Returns:

The PKCS#8-wrapped private key (possibly encrypted).

Return type:

bytes