Spaces:
Running
Running
# | |
# PublicKey/PKCS8.py : PKCS#8 functions | |
# | |
# =================================================================== | |
# | |
# Copyright (c) 2014, Legrandin <[email protected]> | |
# All rights reserved. | |
# | |
# Redistribution and use in source and binary forms, with or without | |
# modification, are permitted provided that the following conditions | |
# are met: | |
# | |
# 1. Redistributions of source code must retain the above copyright | |
# notice, this list of conditions and the following disclaimer. | |
# 2. Redistributions in binary form must reproduce the above copyright | |
# notice, this list of conditions and the following disclaimer in | |
# the documentation and/or other materials provided with the | |
# distribution. | |
# | |
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS | |
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE | |
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, | |
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, | |
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | |
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN | |
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | |
# POSSIBILITY OF SUCH DAMAGE. | |
# =================================================================== | |
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. | |
""" | |
# | |
# PrivateKeyInfo ::= SEQUENCE { | |
# version Version, | |
# privateKeyAlgorithm PrivateKeyAlgorithmIdentifier, | |
# privateKey PrivateKey, | |
# attributes [0] IMPLICIT Attributes OPTIONAL | |
# } | |
# | |
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") | |
# Encryption with PBES2 | |
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?)") | |
# RFC5208, PKCS#8, version is v1(0) | |
# | |
# PrivateKeyInfo ::= SEQUENCE { | |
# version Version, | |
# privateKeyAlgorithm PrivateKeyAlgorithmIdentifier, | |
# privateKey PrivateKey, | |
# attributes [0] IMPLICIT Attributes OPTIONAL | |
# } | |
# | |
# RFC5915, Asymmetric Key Package, version is v2(1) | |
# | |
# OneAsymmetricKey ::= SEQUENCE { | |
# version Version, | |
# privateKeyAlgorithm PrivateKeyAlgorithmIdentifier, | |
# privateKey PrivateKey, | |
# attributes [0] Attributes OPTIONAL, | |
# ..., | |
# [[2: publicKey [1] PublicKey OPTIONAL ]], | |
# ... | |
# } | |
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] | |
# PrivateKey ::= OCTET STRING | |
private_key = DerOctetString().decode(pk_info[2]).payload | |
# We ignore attributes and (for v2 only) publickey | |
return (algo_oid, private_key, algo_params) | |