def finalize_padded(self, pkey): if pkey.key_type not in (HAL_KEY_TYPE_RSA_PRIVATE, HAL_KEY_TYPE_RSA_PUBLIC): return self.finalize() # PKCS #1.5 requires the digest to be wrapped up in an ASN.1 DigestInfo object. from Crypto.Util.asn1 import DerSequence, DerNull, DerOctetString return DerSequence([DerSequence([self._context.oid, DerNull().encode()]).encode(), DerOctetString(self.finalize()).encode()]).encode()
def EMSA_PKCS1_V1_5_ENCODE(hash, emLen): """ Implement the ``EMSA-PKCS1-V1_5-ENCODE`` function, as defined in PKCS#1 v2.1 (RFC3447, 9.2). ``EMSA-PKCS1-V1_5-ENCODE`` actually accepts the message ``M`` as input, and hash it internally. Here, we expect that the message has already been hashed instead. :Parameters: hash : hash object The hash object that holds the digest of the message being signed. emLen : int The length the final encoding must have, in bytes. :attention: the early standard (RFC2313) stated that ``DigestInfo`` had to be BER-encoded. This means that old signatures might have length tags in indefinite form, which is not supported in DER. Such encoding cannot be reproduced by this function. :attention: the same standard defined ``DigestAlgorithm`` to be of ``AlgorithmIdentifier`` type, where the PARAMETERS item is optional. Encodings for ``MD2/4/5`` without ``PARAMETERS`` cannot be reproduced by this function. :Return: An ``emLen`` byte long string that encodes the hash. """ # First, build the ASN.1 DER object DigestInfo: # # DigestInfo ::= SEQUENCE { # digestAlgorithm AlgorithmIdentifier, # digest OCTET STRING # } # # where digestAlgorithm identifies the hash function and shall be an # algorithm ID with an OID in the set PKCS1-v1-5DigestAlgorithms. # # PKCS1-v1-5DigestAlgorithms ALGORITHM-IDENTIFIER ::= { # { OID id-md2 PARAMETERS NULL }| # { OID id-md5 PARAMETERS NULL }| # { OID id-sha1 PARAMETERS NULL }| # { OID id-sha256 PARAMETERS NULL }| # { OID id-sha384 PARAMETERS NULL }| # { OID id-sha512 PARAMETERS NULL } # } # digestAlgo = DerSequence([hash.oid, DerNull().encode()]) digest = DerOctetString(hash.digest()) digestInfo = DerSequence([digestAlgo.encode(), digest.encode()]).encode() # We need at least 11 bytes for the remaining data: 3 fixed bytes and # at least 8 bytes of padding). if emLen < len(digestInfo) + 11: raise ValueError( "Selected hash algorith has a too long digest (%d bytes)." % len(digest)) PS = bchr(0xFF) * (emLen - len(digestInfo) - 3) return b("\x00\x01") + PS + bchr(0x00) + digestInfo
def _create_subject_public_key_info(algo_oid, secret_key, params=None): if params is None: params = DerNull() spki = DerSequence([ DerSequence([DerObjectId(algo_oid), params]), DerBitString(secret_key) ]) return spki.encode()
def _encode(self, message): hash_oid = self.hash_function.get_OID() digest = self.hash_function.hash(message) der_digest = DerOctetString(digest).encode() der_null = DerNull().encode() der_sequence = DerSequence([hash_oid, der_null]).encode() hash_info = DerSequence([der_sequence, der_digest]).encode() hash_size = len(hash_info) padding = '\xff'*(self.n_size - hash_size - 3) return '\x00\x01' + padding + '\0' + hash_info
def test4(self): """Verify wrapping with encryption""" for t in self.wrapped_enc_keys: if t[0] == 'skip encryption': continue rng = Rng(t[2] + t[3]) params = {'iteration_count': t[1]} wrapped = PKCS8.wrap(self.clear_key, self.oid_key, b("TestTest"), protection=t[0], prot_params=params, key_params=DerNull(), randfunc=rng) self.assertEqual(wrapped, t[4])
def pkcs1_v1_5_encode(msg_hash: SHA256.SHA256Hash, emLen: int) -> bytes: # this code is copied from EMSA_PKCS1_V1_5_ENCODE # https://github.com/dlitz/pycrypto/blob/v2.7a1/lib/Crypto/Signature/PKCS1_v1_5.py#L173 digestAlgo = DerSequence([ DerObjectId(msg_hash.oid).encode() ]) #if with_hash_parameters: if True: digestAlgo.append(DerNull().encode()) digest = DerOctetString(msg_hash.digest()) digestInfo = DerSequence([ digestAlgo.encode(), digest.encode() ]).encode() # We need at least 11 bytes for the remaining data: 3 fixed bytes and # at least 8 bytes of padding). if emLen<len(digestInfo)+11: raise TypeError("Selected hash algorith has a too long digest (%d bytes)." % len(digest)) PS = b'\xFF' * (emLen - len(digestInfo) - 3) return b'\x00\x01' + PS + b'\x00' + digestInfo
def CRYPTO_EMSA_PKCS1_V1_5_ENCODE(M, emLen): msg_hash = SHA256.new(M) digestAlgo = DerSequence([DerObjectId(msg_hash.oid).encode()]) print(digestAlgo) digestAlgo.append(DerNull().encode()) digest = DerOctetString(msg_hash.digest()) digestInfo = DerSequence([digestAlgo.encode(), digest.encode()]).encode() print("%x" % int.from_bytes(digestInfo, byteorder='big')) print("%x" % int.from_bytes(digestAlgo.encode(), byteorder='big')) print("%x" % int.from_bytes(digest.encode(), byteorder='big')) print("%x" % int.from_bytes(msg_hash.digest(), byteorder='big')) #b91d0b7a09f57c109926a449647283adb5dc213c0fdb0cf2b219e064cc132273 2004 00 0501020403650148866009060d30 3130 # 501020403650148866009060d30 #b91d0b7a09f57c109926a449647283adb5dc213c0fdb0cf2b219e064cc132273 2004 #b91d0b7a09f57c109926a449647283adb5dc213c0fdb0cf2b219e064cc132273 #b91d0b7a09f57c109926a449647283adb5dc213c0fdb0cf2b219e064cc1322732004000501020403650148866009060d30313000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0100 #3031 300d06096086480165030402010500 0420 732213cc64e019b2f20cdb0f3c21dcb5ad83726449a42699107cf5097a0b1db9 # 300d06096086480165030402010500 # 0420 732213cc64e019b2f20cdb0f3c21dcb5ad83726449a42699107cf5097a0b1db9 # 732213cc64e019b2f20cdb0f3c21dcb5ad83726449a42699107cf5097a0b1db9 #0001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff003031300d060960864801650304020105000420732213cc64e019b2f20cdb0f3c21dcb5ad83726449a42699107cf5097a0b1db9 # We need at least 11 bytes for the remaining data: 3 fixed bytes and # at least 8 bytes of padding). if emLen < len(digestInfo) + 11: raise TypeError( "Selected hash algorith has a too long digest (%d bytes)." % len(digest)) PS = b'\xFF' * (emLen - len(digestInfo) - 3) out = b'\x00\x01' + PS + b'\x00' + digestInfo print("%x" % int.from_bytes(out, byteorder='big')) return out
def _expand_subject_public_key_info(encoded): """Parse a SubjectPublicKeyInfo structure. It returns a triple with: * OID (string) * encoded public key (bytes) * Algorithm parameters (bytes or None) """ # # SubjectPublicKeyInfo ::= SEQUENCE { # algorithm AlgorithmIdentifier, # subjectPublicKey BIT STRING # } # # AlgorithmIdentifier ::= SEQUENCE { # algorithm OBJECT IDENTIFIER, # parameters ANY DEFINED BY algorithm OPTIONAL # } # spki = DerSequence().decode(encoded, nr_elements=2) algo = DerSequence().decode(spki[0], nr_elements=(1, 2)) algo_oid = DerObjectId().decode(algo[0]) spk = DerBitString().decode(spki[1]).value if len(algo) == 1: algo_params = None else: try: DerNull().decode(algo[1]) algo_params = None except: algo_params = algo[1] return algo_oid.value, spk, algo_params
def testDecode1(self): # Empty sequence der = DerNull() self.assertEquals(der, der.decode(b('\x05\x00')))
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, DER encoded. 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 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)) if len(pk_info) == 2 and not passphrase: raise ValueError("Not a valid clear PKCS#8 structure " "(maybe it is encrypted?)") # # PrivateKeyInfo ::= SEQUENCE { # version Version, # privateKeyAlgorithm PrivateKeyAlgorithmIdentifier, # privateKey PrivateKey, # attributes [0] IMPLICIT Attributes OPTIONAL # } # Version ::= INTEGER if pk_info[0] != 0: raise ValueError("Not a valid PrivateKeyInfo SEQUENCE") # PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier # # EncryptedPrivateKeyInfo ::= SEQUENCE { # encryptionAlgorithm EncryptionAlgorithmIdentifier, # encryptedData EncryptedData # } # EncryptionAlgorithmIdentifier ::= AlgorithmIdentifier # AlgorithmIdentifier ::= SEQUENCE { # algorithm OBJECT IDENTIFIER, # parameters ANY DEFINED BY algorithm OPTIONAL # } 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] # EncryptedData ::= OCTET STRING private_key = DerOctetString().decode(pk_info[2]).payload return (algo_oid, private_key, algo_params)
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 | | | (called *N* or CPU/memory cost in scrypt). | | | | | | The default value for PBKDF2 is 1 000. | | | The default value for scrypt is 16 384. | +------------------+-----------------------------------------------+ | 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 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 = DerSequence([ 0, DerSequence([DerObjectId(key_oid), key_params]), 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 testEncode1(self): der = DerNull() self.assertEquals(der.encode(), b('\x05\x00'))
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)
raise ValueError("Not a valid PrivateKeyInfo SEQUENCE") # PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier # # EncryptedPrivateKeyInfo ::= SEQUENCE { # encryptionAlgorithm EncryptionAlgorithmIdentifier, # encryptedData EncryptedData # } # EncryptionAlgorithmIdentifier ::= AlgorithmIdentifier # AlgorithmIdentifier ::= SEQUENCE { # algorithm OBJECT IDENTIFIER, # parameters ANY DEFINED BY algorithm OPTIONAL # } 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] # EncryptedData ::= OCTET STRING private_key = DerOctetString().decode(pk_info[2]).payload return (algo_oid, private_key, algo_params)
def testDecode1(self): # Empty sequence der = DerNull() self.assertEqual(der, der.decode(b('\x05\x00')))
def _EMSA_PKCS1_V1_5_ENCODE(msg_hash, emLen, with_hash_parameters=True): """ Implement the ``EMSA-PKCS1-V1_5-ENCODE`` function, as defined in PKCS#1 v2.1 (RFC3447, 9.2). ``_EMSA-PKCS1-V1_5-ENCODE`` actually accepts the message ``M`` as input, and hash it internally. Here, we expect that the message has already been hashed instead. :Parameters: msg_hash : hash object The hash object that holds the digest of the message being signed. emLen : int The length the final encoding must have, in bytes. with_hash_parameters : bool If True (default), include NULL parameters for the hash algorithm in the ``digestAlgorithm`` SEQUENCE. :attention: the early standard (RFC2313) stated that ``DigestInfo`` had to be BER-encoded. This means that old signatures might have length tags in indefinite form, which is not supported in DER. Such encoding cannot be reproduced by this function. :Return: An ``emLen`` byte long string that encodes the hash. """ # First, build the ASN.1 DER object DigestInfo: # # DigestInfo ::= SEQUENCE { # digestAlgorithm AlgorithmIdentifier, # digest OCTET STRING # } # # where digestAlgorithm identifies the hash function and shall be an # algorithm ID with an OID in the set PKCS1-v1-5DigestAlgorithms. # # PKCS1-v1-5DigestAlgorithms ALGORITHM-IDENTIFIER ::= { # { OID id-md2 PARAMETERS NULL }| # { OID id-md5 PARAMETERS NULL }| # { OID id-sha1 PARAMETERS NULL }| # { OID id-sha256 PARAMETERS NULL }| # { OID id-sha384 PARAMETERS NULL }| # { OID id-sha512 PARAMETERS NULL } # } # # Appendix B.1 also says that for SHA-1/-2 algorithms, the parameters # should be omitted. They may be present, but when they are, they shall # have NULL value. digestAlgo = DerSequence([ DerObjectId(msg_hash.oid).encode() ]) if with_hash_parameters: digestAlgo.append(DerNull().encode()) digest = DerOctetString(msg_hash.digest()) digestInfo = DerSequence([ digestAlgo.encode(), digest.encode() ]).encode() # We need at least 11 bytes for the remaining data: 3 fixed bytes and # at least 8 bytes of padding). if emLen<len(digestInfo)+11: raise TypeError("Selected hash algorith has a too long digest (%d bytes)." % len(digest)) PS = b'\xFF' * (emLen - len(digestInfo) - 3) return b'\x00\x01' + PS + b'\x00' + digestInfo
def testEncode1(self): der = DerNull() self.assertEqual(der.encode(), b('\x05\x00'))
def testDecode1(self): # Empty sequence der = DerNull() der.decode(b('\x05\x00'))
# This is probably an OpenSSH key keystring = binascii.a2b_base64(extern_key.split(b(' '))[1]) keyparts = [] while len(keystring) > 4: l = struct.unpack(">I", keystring[:4])[0] keyparts.append(keystring[4:4 + l]) keystring = keystring[4 + l:] e = Integer.from_bytes(keyparts[1]) n = Integer.from_bytes(keyparts[2]) return construct([n, e]) if bord(extern_key[0]) == 0x30: # This is probably a DER encoded key return _importKeyDER(extern_key, passphrase) raise ValueError("RSA key format is not supported") #: `Object ID`_ for the RSA encryption algorithm. This OID often indicates #: a generic RSA key, even when such key will be actually used for digital #: signatures. #: #: .. _`Object ID`: http://www.alvestrand.no/objectid/1.2.840.113549.1.1.1.html oid = "1.2.840.113549.1.1.1" #: This is the standard DER object that qualifies a cryptographic algorithm #: in ASN.1-based data structures (e.g. X.509 certificates). algorithmIdentifier = DerSequence( [DerObjectId(oid).encode(), # algorithm field DerNull().encode()] # parameters field ).encode()
def pkcs1_hash_and_pad(text): return DerSequence([ DerSequence([SHA256.oid, DerNull().encode()]).encode(), DerOctetString(SHA256(text).digest()).encode() ]).encode()
# This is probably a DER encoded key return self._importKeyDER(externKey) raise ValueError("RSA key format is not supported") #: This is the ASN.1 DER object that qualifies an algorithm as #: compliant to PKCS#1 (that is, the standard RSA). # It is found in all 'algorithm' fields (also called 'algorithmIdentifier'). # It is a SEQUENCE with the oid assigned to RSA and with its parameters (none). # 0x06 0x09 OBJECT IDENTIFIER, 9 bytes of payload # 0x2A 0x86 0x48 0x86 0xF7 0x0D 0x01 0x01 0x01 # rsaEncryption (1 2 840 113549 1 1 1) (PKCS #1) # 0x05 0x00 NULL algorithmIdentifier = DerSequence( [ b('\x06\x09\x2A\x86\x48\x86\xF7\x0D\x01\x01\x01'), DerNull().encode() ] ).encode() _impl = RSAImplementation() #: #: Randomly generate a fresh, new RSA key object. #: #: See `RSAImplementation.generate`. #: generate = _impl.generate #: #: Construct an RSA key object from a tuple of valid RSA components. #: #: See `RSAImplementation.construct`. #: construct = _impl.construct
def export_key(self, format='PEM', passphrase=None, pkcs=1, protection=None, randfunc=None): """Export this RSA key. Args: format (string): The format to use for wrapping the key: - *'PEM'*. (*Default*) Text encoding, done according to `RFC1421`_/`RFC1423`_. - *'DER'*. Binary encoding. - *'OpenSSH'*. Textual encoding, done according to OpenSSH specification. Only suitable for public keys (not private keys). passphrase (string): (*For private keys only*) The pass phrase used for protecting the output. pkcs (integer): (*For private keys only*) The ASN.1 structure to use for serializing the key. Note that even in case of PEM encoding, there is an inner ASN.1 DER structure. With ``pkcs=1`` (*default*), the private key is encoded in a simple `PKCS#1`_ structure (``RSAPrivateKey``). With ``pkcs=8``, the private key is encoded in a `PKCS#8`_ structure (``PrivateKeyInfo``). .. note:: This parameter is ignored for a public key. For DER and PEM, an ASN.1 DER ``SubjectPublicKeyInfo`` structure is always used. protection (string): (*For private keys only*) The encryption scheme to use for protecting the private key. If ``None`` (default), the behavior depends on :attr:`format`: - For *'DER'*, the *PBKDF2WithHMAC-SHA1AndDES-EDE3-CBC* scheme is used. The following operations are performed: 1. A 16 byte Triple DES key is derived from the passphrase using :func:`Crypto.Protocol.KDF.PBKDF2` with 8 bytes salt, and 1 000 iterations of :mod:`Crypto.Hash.HMAC`. 2. The private key is encrypted using CBC. 3. The encrypted key is encoded according to PKCS#8. - For *'PEM'*, the obsolete PEM encryption scheme is used. It is based on MD5 for key derivation, and Triple DES for encryption. Specifying a value for :attr:`protection` is only meaningful for PKCS#8 (that is, ``pkcs=8``) and only if a pass phrase is present too. The supported schemes for PKCS#8 are listed in the :mod:`Crypto.IO.PKCS8` module (see :attr:`wrap_algo` parameter). randfunc (callable): A function that provides random bytes. Only used for PEM encoding. The default is :func:`Crypto.Random.get_random_bytes`. Returns: byte string: the encoded key Raises: ValueError:when the format is unknown or when you try to encrypt a private key with *DER* format and PKCS#1. .. warning:: If you don't provide a pass phrase, the private key will be exported in the clear! .. _RFC1421: http://www.ietf.org/rfc/rfc1421.txt .. _RFC1423: http://www.ietf.org/rfc/rfc1423.txt .. _`PKCS#1`: http://www.ietf.org/rfc/rfc3447.txt .. _`PKCS#8`: http://www.ietf.org/rfc/rfc5208.txt """ if passphrase is not None: passphrase = tobytes(passphrase) if randfunc is None: randfunc = Random.get_random_bytes if format == 'OpenSSH': e_bytes, n_bytes = [x.to_bytes() for x in (self._e, self._n)] if bord(e_bytes[0]) & 0x80: e_bytes = b'\x00' + e_bytes if bord(n_bytes[0]) & 0x80: n_bytes = b'\x00' + n_bytes keyparts = [b'ssh-rsa', e_bytes, n_bytes] keystring = b''.join( [struct.pack(">I", len(kp)) + kp for kp in keyparts]) return b'ssh-rsa ' + binascii.b2a_base64(keystring)[:-1] # DER format is always used, even in case of PEM, which simply # encodes it into BASE64. if self.has_private(): binary_key = DerSequence([ 0, self.n, self.e, self.d, self.p, self.q, self.d % (self.p - 1), self.d % (self.q - 1), Integer(self.q).inverse(self.p) ]).encode() if pkcs == 1: key_type = 'RSA PRIVATE KEY' if format == 'DER' and passphrase: raise ValueError("PKCS#1 private key cannot be encrypted") else: # PKCS#8 from Crypto.IO import PKCS8 if format == 'PEM' and protection is None: key_type = 'PRIVATE KEY' binary_key = PKCS8.wrap(binary_key, oid, None, key_params=DerNull()) else: key_type = 'ENCRYPTED PRIVATE KEY' if not protection: protection = 'PBKDF2WithHMAC-SHA1AndDES-EDE3-CBC' binary_key = PKCS8.wrap(binary_key, oid, passphrase, protection, key_params=DerNull()) passphrase = None else: key_type = "PUBLIC KEY" binary_key = _create_subject_public_key_info( oid, DerSequence([self.n, self.e]), DerNull()) if format == 'DER': return binary_key if format == 'PEM': from Crypto.IO import PEM pem_str = PEM.encode(binary_key, key_type, passphrase, randfunc) return tobytes(pem_str) raise ValueError( "Unknown key format '%s'. Cannot export the RSA key." % format)