pycryptodome/lib/Crypto/IO/PKCS8.py

227 lines
7.6 KiB
Python
Raw Permalink Normal View History

#
# PublicKey/PKCS8.py : PKCS#8 functions
#
# ===================================================================
#
2014-06-23 22:20:10 +02:00
# Copyright (c) 2014, Legrandin <helderijs@gmail.com>
# 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.
# ===================================================================
2014-06-23 22:20:10 +02:00
from Crypto.Util.py3compat import *
2014-06-11 15:46:22 +02:00
from Crypto.Util.asn1 import (
DerNull,
DerSequence,
DerObjectId,
DerOctetString,
)
from Crypto.IO._PBES import PBES1, PBES2, PbesError
2016-01-24 15:13:22 +01:00
__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).
2017-08-18 17:26:20 +02:00
Args:
2024-01-07 11:15:44 +01:00
private_key (bytes):
The private key encoded in binary form. The actual encoding is
algorithm specific. In most cases, it is DER.
2017-08-18 17:26:20 +02:00
key_oid (string):
The object identifier (OID) of the private key to wrap.
2024-01-07 11:15:44 +01:00
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).
2024-01-07 11:15:44 +01:00
Keyword Args:
passphrase (bytes or string):
The secret passphrase from which the wrapping key is derived.
Set it only if encryption is required.
2017-08-18 17:26:20 +02:00
protection (string):
The identifier of the algorithm to use for securely wrapping the key.
2024-01-07 11:15:44 +01:00
Refer to :ref:`the encryption parameters<enc_params>` .
The default value is ``'PBKDF2WithHMAC-SHA1AndDES-EDE3-CBC'``.
2017-08-18 17:26:20 +02:00
prot_params (dictionary):
2024-01-07 11:15:44 +01:00
Parameters for the key derivation function (KDF).
Refer to :ref:`the encryption parameters<enc_params>` .
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.
2017-08-18 17:26:20 +02:00
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
2017-08-18 17:26:20 +02:00
from :mod:`Crypto.Random`.
2024-01-07 11:15:44 +01:00
Returns:
bytes: The PKCS#8-wrapped private key (possibly encrypted).
"""
#
# PrivateKeyInfo ::= SEQUENCE {
# version Version,
# privateKeyAlgorithm PrivateKeyAlgorithmIdentifier,
# privateKey PrivateKey,
# attributes [0] IMPLICIT Attributes OPTIONAL
# }
#
2022-04-15 00:15:48 +02:00
if key_params is None:
algorithm = DerSequence([DerObjectId(key_oid)])
else:
algorithm = DerSequence([DerObjectId(key_oid), key_params])
2016-01-16 23:34:52 +01:00
pk_info = DerSequence([
0,
2022-04-15 00:15:48 +02:00
algorithm,
DerOctetString(private_key)
2016-01-16 23:34:52 +01:00
])
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).
2017-08-18 17:26:20 +02:00
Args:
2024-01-07 11:15:44 +01:00
p8_private_key (bytes):
The private key wrapped into a PKCS#8 container, DER encoded.
Keyword Args:
2017-08-18 17:26:20 +02:00
passphrase (byte string or string):
The passphrase to use to decrypt the blob (if it is encrypted).
2017-08-18 17:26:20 +02:00
Return:
A tuple containing
#. the algorithm identifier of the wrapped key (OID, dotted string)
2024-01-07 11:15:44 +01:00
#. the private key (bytes, DER encoded)
#. the associated parameters (bytes, DER encoded) or ``None``
2017-08-18 17:26:20 +02:00
Raises:
ValueError : if decoding fails
"""
if passphrase is not None:
passphrase = tobytes(passphrase)
found = False
try:
p8_private_key = PBES1.decrypt(p8_private_key, passphrase)
found = True
2018-06-12 14:15:39 +02:00
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
2018-06-12 14:15:39 +02:00
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)
2022-04-15 00:15:48 +02:00
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?)")
2016-01-24 15:13:22 +01:00
2022-04-15 00:15:48 +02:00
# RFC5208, PKCS#8, version is v1(0)
2016-01-24 15:13:22 +01:00
#
# PrivateKeyInfo ::= SEQUENCE {
# version Version,
# privateKeyAlgorithm PrivateKeyAlgorithmIdentifier,
# privateKey PrivateKey,
# attributes [0] IMPLICIT Attributes OPTIONAL
# }
#
2022-04-15 00:15:48 +02:00
# 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 ]],
# ...
2016-01-24 15:13:22 +01:00
# }
2022-04-15 00:15:48 +02:00
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")
2016-01-24 15:13:22 +01:00
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:
2016-01-24 15:13:22 +01:00
try:
DerNull().decode(algo[1])
algo_params = None
except:
algo_params = algo[1]
2022-04-15 00:15:48 +02:00
# PrivateKey ::= OCTET STRING
2016-01-24 15:13:22 +01:00
private_key = DerOctetString().decode(pk_info[2]).payload
2022-04-15 00:15:48 +02:00
# We ignore attributes and (for v2 only) publickey
2016-01-24 15:13:22 +01:00
return (algo_oid, private_key, algo_params)