mirror of
				https://github.com/Legrandin/pycryptodome.git
				synced 2025-10-31 13:41:27 +00:00 
			
		
		
		
	
		
			
	
	
		
			210 lines
		
	
	
	
		
			7.3 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
		
		
			
		
	
	
			210 lines
		
	
	
	
		
			7.3 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
|   | # -*- coding: utf-8 -*- | ||
|  | # | ||
|  | #  PublicKey/PKCS8.py : PKCS#8 functions | ||
|  | # | ||
|  | # =================================================================== | ||
|  | # The contents of this file are dedicated to the public domain.  To | ||
|  | # the extent that dedication to the public domain is not available, | ||
|  | # everyone is granted a worldwide, perpetual, royalty-free, | ||
|  | # non-exclusive license to exercise all rights associated with the | ||
|  | # contents of this file for any purpose whatsoever. | ||
|  | # No rights are reserved. | ||
|  | # | ||
|  | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
|  | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
|  | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | ||
|  | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | ||
|  | # BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | ||
|  | # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | ||
|  | # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
|  | # SOFTWARE. | ||
|  | # =================================================================== | ||
|  | """
 | ||
|  | Module for handling private keys wrapped according to `PKCS#8`_. | ||
|  | 
 | ||
|  | PKCS8 is a standard for storing and transferring private key information. | ||
|  | The wrapped key can either be clear or encrypted. | ||
|  | 
 | ||
|  | All encryption algorithms are based on passphrase-based key derivation. | ||
|  | The following mechanisms are fully supported: | ||
|  | 
 | ||
|  | * *PBKDF2WithHMAC-SHA1AndAES128-CBC* | ||
|  | * *PBKDF2WithHMAC-SHA1AndAES192-CBC* | ||
|  | * *PBKDF2WithHMAC-SHA1AndAES256-CBC* | ||
|  | * *PBKDF2WithHMAC-SHA1AndDES-EDE3-CBC* | ||
|  | 
 | ||
|  | The following 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* | ||
|  | 
 | ||
|  | .. _`PKCS#8`: http://www.ietf.org/rfc/rfc5208.txt | ||
|  | 
 | ||
|  | """
 | ||
|  | 
 | ||
|  | import sys | ||
|  | 
 | ||
|  | if sys.version_info[0] == 2 and sys.version_info[1] == 1: | ||
|  |     from Crypto.Util.py21compat import * | ||
|  | from Crypto.Util.py3compat import * | ||
|  | 
 | ||
|  | from Crypto.Util.asn1 import * | ||
|  | 
 | ||
|  | from Crypto.IO._PBES import PBES1, PBES2 | ||
|  | 
 | ||
|  | __all__ = ['wrap', 'unwrap'] | ||
|  | 
 | ||
|  | 
 | ||
|  | def decode_der(obj_class, binstr): | ||
|  |     """Instantiate a DER object class, decode a DER binary string in it, and
 | ||
|  |     return the object."""
 | ||
|  |     der = obj_class() | ||
|  |     der.decode(binstr) | ||
|  |     return der | ||
|  | 
 | ||
|  | 
 | ||
|  | def wrap(private_key, key_oid, passphrase=None, protection=None, | ||
|  |          prot_params=None, key_params=None, randfunc=None): | ||
|  |     """Wrap a private key into a PKCS#8 blob (clear or encrypted).
 | ||
|  | 
 | ||
|  |     :Parameters: | ||
|  | 
 | ||
|  |       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 : (binary) 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.   | | ||
|  |         |                  | The default value is 1 000.                   | | ||
|  |         +------------------+-----------------------------------------------+ | ||
|  |         | salt_size        | Salt is used to thwart dictionary and rainbow | | ||
|  |         |                  | attacks on passwords. The default value is 8  | | ||
|  |         |                  | bytes.                                        | | ||
|  |         +------------------+-----------------------------------------------+ | ||
|  | 
 | ||
|  |       key_params : DER object | ||
|  |         The algorithm parameters associated to the private key. | ||
|  |         It is required for algorithms like DSA, but not for others like RSA. | ||
|  | 
 | ||
|  |       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``. | ||
|  | 
 | ||
|  |     :Return: | ||
|  |       The PKCS#8-wrapped private key (possibly encrypted), | ||
|  |       as a binary string. | ||
|  |     """
 | ||
|  | 
 | ||
|  |     if key_params is None: | ||
|  |         key_params = DerNull() | ||
|  | 
 | ||
|  |     # | ||
|  |     #   PrivateKeyInfo ::= SEQUENCE { | ||
|  |     #       version                 Version, | ||
|  |     #       privateKeyAlgorithm     PrivateKeyAlgorithmIdentifier, | ||
|  |     #       privateKey              PrivateKey, | ||
|  |     #       attributes              [0]  IMPLICIT Attributes OPTIONAL | ||
|  |     #   } | ||
|  |     # | ||
|  |     pk_info = newDerSequence( | ||
|  |                 0, | ||
|  |                 newDerSequence( | ||
|  |                     DerObjectId(key_oid), | ||
|  |                     key_params | ||
|  |                 ), | ||
|  |                 newDerOctetString(private_key) | ||
|  |             ) | ||
|  |     pk_info_der = pk_info.encode() | ||
|  | 
 | ||
|  |     if not passphrase: | ||
|  |         return pk_info_der | ||
|  | 
 | ||
|  |     # 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).
 | ||
|  | 
 | ||
|  |     :Parameters: | ||
|  |       p8_private_key : byte string | ||
|  |         The private key wrapped into a PKCS#8 blob | ||
|  |       passphrase : (byte) 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 | ||
|  |         for pbes in PBES1, PBES2: | ||
|  |             try: | ||
|  |                 p8_private_key = pbes.decrypt(p8_private_key, passphrase) | ||
|  |             except ValueError: | ||
|  |                 pass | ||
|  |             else: | ||
|  |                 found = True | ||
|  |                 break | ||
|  |         if not found: | ||
|  |             raise ValueError("Unsupported PKCS#5 Object ID ") | ||
|  | 
 | ||
|  |     pk_info = decode_der(DerSequence, p8_private_key) | ||
|  |     if len(pk_info) == 2 and not passphrase: | ||
|  |         raise ValueError("Not a valid clear PKCS#8 structure " | ||
|  |                          "(maybe it is encrypted?)") | ||
|  |     if not 3 <= len(pk_info) <= 4 or pk_info[0] != 0: | ||
|  |         raise ValueError("Not a valid PrivateKeyInfo SEQUENCE") | ||
|  | 
 | ||
|  |     # | ||
|  |     #   AlgorithmIdentifier  ::=  SEQUENCE  { | ||
|  |     #       algorithm               OBJECT IDENTIFIER, | ||
|  |     #       parameters              ANY DEFINED BY algorithm OPTIONAL | ||
|  |     #   } | ||
|  |     # | ||
|  |     algo_id = decode_der(DerSequence, pk_info[1]) | ||
|  |     if not 1 <= len(algo_id) <= 2: | ||
|  |         raise ValueError("Not a valid AlgorithmIdentifier SEQUENCE") | ||
|  |     algo = decode_der(DerObjectId, algo_id[0]).value | ||
|  |     private_key = decode_der(DerOctetString, pk_info[2]).payload | ||
|  |     if len(algo_id) == 2 and algo_id[1] != b('\x05\x00'): | ||
|  |         params = algo_id[1] | ||
|  |     else: | ||
|  |         params = None | ||
|  |     return (algo, private_key, params) |