| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
|
|
| from Crypto.Util.py3compat import * |
|
|
| from Crypto.Util.asn1 import ( |
| DerNull, |
| DerSequence, |
| DerObjectId, |
| DerOctetString, |
| ) |
|
|
| from Crypto.IO._PBES import PBES1, PBES2, PbesError |
|
|
|
|
| __all__ = ['wrap', 'unwrap'] |
|
|
|
|
| def wrap(private_key, key_oid, passphrase=None, protection=None, |
| prot_params=None, key_params=DerNull(), randfunc=None): |
| """Wrap a private key into a PKCS#8 blob (clear or encrypted). |
| |
| Args: |
| |
| private_key (byte string): |
| 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). |
| |
| passphrase (bytes string 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. |
| The default value is ``PBKDF2WithHMAC-SHA1AndDES-EDE3-CBC``. |
| |
| prot_params (dictionary): |
| Parameters for the protection algorithm. |
| |
| +------------------+-----------------------------------------------+ |
| | 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 for PBKDF2 is 1000. | |
| | | The default value for scrypt is 16384. | |
| +------------------+-----------------------------------------------+ |
| | 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. | |
| +------------------+-----------------------------------------------+ |
| |
| 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 :mod:`Crypto.Random`. |
| |
| Return: |
| The PKCS#8-wrapped private key (possibly encrypted), as a byte string. |
| """ |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| if key_params is None: |
| algorithm = DerSequence([DerObjectId(key_oid)]) |
| else: |
| algorithm = DerSequence([DerObjectId(key_oid), key_params]) |
|
|
| pk_info = DerSequence([ |
| 0, |
| algorithm, |
| DerOctetString(private_key) |
| ]) |
| pk_info_der = pk_info.encode() |
|
|
| if passphrase is None: |
| return pk_info_der |
|
|
| if not passphrase: |
| raise ValueError("Empty passphrase") |
|
|
| |
| passphrase = tobytes(passphrase) |
| if protection is None: |
| protection = 'PBKDF2WithHMAC-SHA1AndDES-EDE3-CBC' |
| return PBES2.encrypt(pk_info_der, passphrase, |
| protection, prot_params, randfunc) |
|
|
|
|
| def unwrap(p8_private_key, passphrase=None): |
| """Unwrap a private key from a PKCS#8 blob (clear or encrypted). |
| |
| Args: |
| p8_private_key (byte string): |
| The private key wrapped into a PKCS#8 blob, DER encoded. |
| passphrase (byte string or string): |
| The passphrase to use to decrypt the blob (if it is encrypted). |
| |
| Return: |
| A tuple containing |
| |
| #. the algorithm identifier of the wrapped key (OID, dotted string) |
| #. the private key (byte string, DER encoded) |
| #. the associated parameters (byte string, DER encoded) or ``None`` |
| |
| Raises: |
| ValueError : if decoding fails |
| """ |
|
|
| if passphrase: |
| passphrase = tobytes(passphrase) |
|
|
| found = False |
| try: |
| p8_private_key = PBES1.decrypt(p8_private_key, passphrase) |
| found = True |
| except PbesError as e: |
| error_str = "PBES1[%s]" % str(e) |
| except ValueError: |
| error_str = "PBES1[Invalid]" |
|
|
| if not found: |
| try: |
| p8_private_key = PBES2.decrypt(p8_private_key, passphrase) |
| found = True |
| except PbesError as e: |
| error_str += ",PBES2[%s]" % str(e) |
| except ValueError: |
| error_str += ",PBES2[Invalid]" |
|
|
| if not found: |
| raise ValueError("Error decoding PKCS#8 (%s)" % error_str) |
|
|
| pk_info = DerSequence().decode(p8_private_key, nr_elements=(2, 3, 4, 5)) |
| if len(pk_info) == 2 and not passphrase: |
| raise ValueError("Not a valid clear PKCS#8 structure " |
| "(maybe it is encrypted?)") |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| if pk_info[0] == 0: |
| if len(pk_info) not in (3, 4): |
| raise ValueError("Not a valid PrivateKeyInfo SEQUENCE") |
| elif pk_info[0] == 1: |
| if len(pk_info) not in (3, 4, 5): |
| raise ValueError("Not a valid PrivateKeyInfo SEQUENCE") |
| else: |
| raise ValueError("Not a valid PrivateKeyInfo SEQUENCE") |
|
|
| algo = DerSequence().decode(pk_info[1], nr_elements=(1, 2)) |
| algo_oid = DerObjectId().decode(algo[0]).value |
| if len(algo) == 1: |
| algo_params = None |
| else: |
| try: |
| DerNull().decode(algo[1]) |
| algo_params = None |
| except: |
| algo_params = algo[1] |
|
|
| |
| private_key = DerOctetString().decode(pk_info[2]).payload |
|
|
| |
|
|
| return (algo_oid, private_key, algo_params) |
|
|