def exportKey(self, format='PEM', passphrase=None, pkcs=1, protection=None): if passphrase is not None: passphrase = tobytes(passphrase) if format == 'OpenSSH': eb = long_to_bytes(self.e) nb = long_to_bytes(self.n) if bord(eb[0]) & 128: eb = bchr(0) + eb if bord(nb[0]) & 128: nb = bchr(0) + nb keyparts = [b('ssh-rsa'), eb, nb] keystring = b('').join( [struct.pack('>I', len(kp)) + kp for kp in keyparts]) return b('ssh-rsa ') + binascii.b2a_base64(keystring)[:-1] else: if self.has_private(): binary_key = newDerSequence(0, self.n, self.e, self.d, self.p, self.q, self.d % (self.p - 1), self.d % (self.q - 1), inverse(self.q, self.p)).encode() if pkcs == 1: keyType = 'RSA PRIVATE' if format == 'DER' and passphrase: raise ValueError( 'PKCS#1 private key cannot be encrypted') elif format == 'PEM' and protection is None: keyType = 'PRIVATE' binary_key = PKCS8.wrap(binary_key, oid, None) else: keyType = 'ENCRYPTED PRIVATE' if not protection: protection = 'PBKDF2WithHMAC-SHA1AndDES-EDE3-CBC' binary_key = PKCS8.wrap(binary_key, oid, passphrase, protection) passphrase = None else: keyType = 'RSA PUBLIC' binary_key = newDerSequence( algorithmIdentifier, newDerBitString(newDerSequence(self.n, self.e))).encode() if format == 'DER': return binary_key if format == 'PEM': pem_str = PEM.encode(binary_key, keyType + ' KEY', passphrase, self._randfunc) return tobytes(pem_str) raise ValueError( "Unknown key format '%s'. Cannot export the RSA key." % format) return
def _importKeyDER(self, extern_key, passphrase=None): try: der = decode_der(DerSequence, extern_key) if len(der) == 9 and der.hasOnlyInts() and der[0] == 0: del der[6:] der.append(inverse(der[4], der[5])) del der[0] return self.construct(der[:]) if len(der) == 2: try: if der.hasOnlyInts(): return self.construct(der[:]) if der[0] == algorithmIdentifier: bitmap = decode_der(DerBitString, der[1]) rsaPub = decode_der(DerSequence, bitmap.value) if len(rsaPub) == 2 and rsaPub.hasOnlyInts(): return self.construct(rsaPub[:]) except (ValueError, EOFError): pass k = PKCS8.unwrap(extern_key, passphrase) if k[0] == oid: return self._importKeyDER(k[1], passphrase) except (ValueError, EOFError): pass raise ValueError('RSA key format is not supported')
def test3(self): """Verify unwrapping with encryption""" for t in self.wrapped_enc_keys: res1, res2, res3 = PKCS8.unwrap(t[4], b("TestTest")) self.assertEqual(res1, self.oid_key) self.assertEqual(res2, self.clear_key)
def _importKeyDER(self, key_data, passphrase=None, params=None): try: if params: x = decode_der(DerInteger, key_data).value params = decode_der(DerSequence, params) p, q, g = list(params) y = pow(g, x, p) tup = (y, g, p, q, x) return self.construct(tup) der = decode_der(DerSequence, key_data) if len(der) == 6 and der.hasOnlyInts() and der[0] == 0: tup = [der[comp] for comp in (4, 3, 1, 2, 5)] return self.construct(tup) if len(der) == 2: try: algo = decode_der(DerSequence, der[0]) algo_oid = decode_der(DerObjectId, algo[0]).value params = decode_der(DerSequence, algo[1]) if algo_oid == oid and len( params) == 3 and params.hasOnlyInts(): bitmap = decode_der(DerBitString, der[1]) pub_key = decode_der(DerInteger, bitmap.value) tup = [pub_key.value] tup += [params[comp] for comp in (2, 0, 1)] return self.construct(tup) except (ValueError, EOFError): pass p8_pair = PKCS8.unwrap(key_data, passphrase) if p8_pair[0] == oid: return self._importKeyDER(p8_pair[1], passphrase, p8_pair[2]) except (ValueError, EOFError): pass raise ValueError('DSA key format is not supported')
def _import_pkcs8(encoded, passphrase): from Crypto.IO import PKCS8 k = PKCS8.unwrap(encoded, passphrase) if k[0] != oid: raise ValueError("No PKCS#8 encoded RSA key") return _import_keyDER(k[1], passphrase)
def _import_pkcs8(encoded, passphrase): from Crypto.IO import PKCS8 # From RFC5915, Section 1: # # Distributing an EC private key with PKCS#8 [RFC5208] involves including: # a) id-ecPublicKey, id-ecDH, or id-ecMQV (from [RFC5480]) with the # namedCurve as the parameters in the privateKeyAlgorithm field; and # b) ECPrivateKey in the PrivateKey field, which is an OCTET STRING. algo_oid, private_key, params = PKCS8.unwrap(encoded, passphrase) # We accept id-ecPublicKey, id-ecDH, id-ecMQV without making any # distiction for now. unrestricted_oid = "1.2.840.10045.2.1" ecdh_oid = "1.3.132.1.12" ecmqv_oid = "1.3.132.1.13" if algo_oid not in (unrestricted_oid, ecdh_oid, ecmqv_oid): raise UnsupportedEccFeature("Unsupported ECC purpose (OID: %s)" % algo_oid) curve_oid = DerObjectId().decode(params).value return _import_private_der(private_key, passphrase, curve_oid)
def _importKeyDER(extern_key, passphrase, verify_x509_cert): """Import an RSA key (public or private half), encoded in DER form.""" try: der = DerSequence().decode(extern_key) # Try PKCS#1 first, for a private key if len(der) == 9 and der.hasOnlyInts() and der[0] == 0: # ASN.1 RSAPrivateKey element del der[6:] # Remove d mod (p-1), # d mod (q-1), and # q^{-1} mod p der.append(Integer(der[4]).inverse(der[5])) # Add p^{-1} mod q del der[0] # Remove version return construct(der[:]) # Keep on trying PKCS#1, but now for a public key if len(der) == 2: try: # The DER object is an RSAPublicKey SEQUENCE with # two elements if der.hasOnlyInts(): return construct(der[:]) # The DER object is a SubjectPublicKeyInfo SEQUENCE # with two elements: an 'algorithmIdentifier' and a # 'subjectPublicKey'BIT STRING. # 'algorithmIdentifier' takes the value given at the # module level. # 'subjectPublicKey' encapsulates the actual ASN.1 # RSAPublicKey element. if der[0] == algorithmIdentifier: bitmap = DerBitString().decode(der[1]) rsaPub = DerSequence().decode(bitmap.value) if len(rsaPub) == 2 and rsaPub.hasOnlyInts(): return construct(rsaPub[:]) except (ValueError, EOFError): pass # Try to see if this is an X.509 DER certificate # (Certificate ASN.1 type) if len(der) == 3: from Crypto.PublicKey import _extract_sp_info try: sp_info = _extract_sp_info(der) if verify_x509_cert: raise NotImplementedError("X.509 certificate validation is not supported") return _importKeyDER(sp_info, passphrase, False) except ValueError: pass # Try PKCS#8 (possibly encrypted) k = PKCS8.unwrap(extern_key, passphrase) if k[0] == oid: return _importKeyDER(k[1], passphrase, False) except (ValueError, EOFError): pass raise ValueError("RSA key format is not supported")
def _export_pkcs8(self, **kwargs): if kwargs.get('passphrase', None) is not None and 'protection' not in kwargs: raise ValueError("At least the 'protection' parameter should be present") unrestricted_oid = "1.2.840.10045.2.1" private_key = self._export_private_der(include_ec_params=False) result = PKCS8.wrap(private_key, unrestricted_oid, key_params=DerObjectId(_curve.oid), **kwargs) return result
def _import_pkcs8(encoded, passphrase, params): if params: raise ValueError("PKCS#8 already includes parameters") k = PKCS8.unwrap(encoded, passphrase) if k[0] != oid: raise ValueError("No PKCS#8 encoded DSA key") x = DerInteger().decode(k[1]).value p, q, g = list(DerSequence().decode(k[2])) tup = (pow(g, x, p), g, p, q, x) return construct(tup)
def _importKeyDER(self, key_data, passphrase=None, params=None): """Import a DSA key (public or private half), encoded in DER form.""" try: # # Dss-Parms ::= SEQUENCE { # p OCTET STRING, # q OCTET STRING, # g OCTET STRING # } # # Try a simple private key first if params: x = decode_der(DerInteger, key_data).value params = decode_der(DerSequence, params) # Dss-Parms p, q, g = list(params) y = pow(g, x, p) tup = (y, g, p, q, x) return self.construct(tup) der = decode_der(DerSequence, key_data) # Try OpenSSL format for private keys if len(der) == 6 and der.hasOnlyInts() and der[0] == 0: tup = [der[comp] for comp in (4, 3, 1, 2, 5)] return self.construct(tup) # Try SubjectPublicKeyInfo if len(der) == 2: try: algo = decode_der(DerSequence, der[0]) algo_oid = decode_der(DerObjectId, algo[0]).value params = decode_der(DerSequence, algo[1]) # Dss-Parms if algo_oid == oid and len(params) == 3 and\ params.hasOnlyInts(): bitmap = decode_der(DerBitString, der[1]) pub_key = decode_der(DerInteger, bitmap.value) tup = [pub_key.value] tup += [params[comp] for comp in (2, 0, 1)] return self.construct(tup) except (ValueError, EOFError): pass # Try unencrypted PKCS#8 p8_pair = PKCS8.unwrap(key_data, passphrase) if p8_pair[0] == oid: return self._importKeyDER(p8_pair[1], passphrase, p8_pair[2]) except (ValueError, EOFError): pass raise KeyFormatError("DSA key format is not supported")
def _parse_key_pkcs8(self, data: bytes) -> bytes: parsed = pem.parse(data) if parsed and len(parsed) == 1: data = base64.b64decode(''.join( parsed[0].as_text().strip().split("\n")[1:-1])) try: (_, key, _) = pkcs8.unwrap(data, self.password.encode("utf-8")) except ValueError as error: raise X509AdapterError( "invalid password or PKCS#8 data") from error return key
def _load_private_key(self): """Load the private key used to sign HTTP requests. The private key is used to sign HTTP requests as defined in https://datatracker.ietf.org/doc/draft-cavage-http-signatures/. """ if self.private_key is not None: return with open(self.private_key_path, 'r') as f: pem_data = f.read() # Verify PEM Pre-Encapsulation Boundary r = re.compile(r"\s*-----BEGIN (.*)-----\s+") m = r.match(pem_data) if not m: raise ValueError("Not a valid PEM pre boundary") pem_header = m.group(1) if pem_header == 'RSA PRIVATE KEY': self.private_key = RSA.importKey(pem_data, self.private_key_passphrase) elif pem_header == 'EC PRIVATE KEY': self.private_key = ECC.import_key(pem_data, self.private_key_passphrase) elif pem_header in {'PRIVATE KEY', 'ENCRYPTED PRIVATE KEY'}: # Key is in PKCS8 format, which is capable of holding many different # types of private keys, not just EC keys. (key_binary, pem_header, is_encrypted) = \ PEM.decode(pem_data, self.private_key_passphrase) (oid, privkey, params) = \ PKCS8.unwrap(key_binary, passphrase=self.private_key_passphrase) if oid == '1.2.840.10045.2.1': self.private_key = ECC.import_key( pem_data, self.private_key_passphrase) else: raise Exception("Unsupported key: {0}. OID: {1}".format( pem_header, oid)) else: raise Exception("Unsupported key: {0}".format(pem_header)) # Validate the specified signature algorithm is compatible with the private key. if self.signing_algorithm is not None: supported_algs = None if isinstance(self.private_key, RSA.RsaKey): supported_algs = { ALGORITHM_RSASSA_PSS, ALGORITHM_RSASSA_PKCS1v15 } elif isinstance(self.private_key, ECC.EccKey): supported_algs = ALGORITHM_ECDSA_KEY_SIGNING_ALGORITHMS if supported_algs is not None and self.signing_algorithm not in supported_algs: raise Exception( "Signing algorithm {0} is not compatible with private key" .format(self.signing_algorithm))
def _importKeyDER(self, extern_key, passphrase=None): """Import an RSA key (public or private half), encoded in DER form.""" try: der = decode_der(DerSequence, extern_key) # Try PKCS#1 first, for a private key if len(der) == 9 and der.hasOnlyInts() and der[0] == 0: # ASN.1 RSAPrivateKey element del der[6:] # Remove d mod (p-1), # d mod (q-1), and # q^{-1} mod p der.append(inverse(der[4], der[5])) # Add p^{-1} mod q del der[0] # Remove version return self.construct(der[:]) # Keep on trying PKCS#1, but now for a public key if len(der) == 2: try: # The DER object is an RSAPublicKey SEQUENCE with # two elements if der.hasOnlyInts(): return self.construct(der[:]) # The DER object is a SubjectPublicKeyInfo SEQUENCE # with two elements: an 'algorithmIdentifier' and a # 'subjectPublicKey'BIT STRING. # 'algorithmIdentifier' takes the value given at the # module level. # 'subjectPublicKey' encapsulates the actual ASN.1 # RSAPublicKey element. if der[0] == algorithmIdentifier: bitmap = decode_der(DerBitString, der[1]) rsaPub = decode_der(DerSequence, bitmap.value) if len(rsaPub) == 2 and rsaPub.hasOnlyInts(): return self.construct(rsaPub[:]) except (ValueError, EOFError): pass # Try PKCS#8 (possibly encrypted) k = PKCS8.unwrap(extern_key, passphrase) if k[0] == oid: return self._importKeyDER(k[1], passphrase) except (ValueError, EOFError): pass raise ValueError("RSA key format is not supported")
def test4(self): """Verify wrapping with encryption""" for t in self.wrapped_enc_keys: if t[0] == -1: 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=None, randfunc=rng) self.assertEqual(wrapped, t[4])
def test4(self): """Verify wrapping with encryption""" for t in self.wrapped_enc_keys: if t[0]==-1: 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=None, randfunc=rng) self.assertEqual(wrapped, t[4])
def _import_pkcs8(encoded, passphrase): # From RFC5915, Section 1: # # Distributing an EC private key with PKCS#8 [RFC5208] involves including: # a) id-ecPublicKey, id-ecDH, or id-ecMQV (from [RFC5480]) with the # namedCurve as the parameters in the privateKeyAlgorithm field; and # b) ECPrivateKey in the PrivateKey field, which is an OCTET STRING. algo_oid, private_key, params = PKCS8.unwrap(encoded, passphrase) # We accept id-ecPublicKey, id-ecDH, id-ecMQV without making any # distiction for now. unrestricted_oid = "1.2.840.10045.2.1" ecdh_oid = "1.3.132.1.12" ecmqv_oid = "1.3.132.1.13" if algo_oid not in (unrestricted_oid, ecdh_oid, ecmqv_oid): raise ValueError("No PKCS#8 encoded ECC key") curve_name = DerObjectId().decode(params).value return _import_private_der(private_key, passphrase, curve_name)
def exportKey(self, format='PEM', pkcs8=None, passphrase=None, protection=None, randfunc=None): """Export this DSA key. :Parameters: format : string The format to use for wrapping the key: - *'DER'*. Binary encoding. - *'PEM'*. Textual encoding, done according to `RFC1421`_/ `RFC1423`_ (default). - *'OpenSSH'*. Textual encoding, one line of text, see `RFC4253`_. Only suitable for public keys, not private keys. passphrase : string For private keys only. The pass phrase to use for deriving the encryption key. pkcs8 : boolean For private keys only. If ``True`` (default), the key is arranged according to `PKCS#8`_ and if `False`, according to the custom OpenSSL/OpenSSH encoding. protection : string The encryption scheme to use for protecting the private key. It is only meaningful when a pass phrase is present too. If ``pkcs8`` takes value ``True``, ``protection`` is the PKCS#8 algorithm to use for deriving the secret and encrypting the private DSA key. For a complete list of algorithms, see `Crypto.IO.PKCS8`. The default is *PBKDF2WithHMAC-SHA1AndDES-EDE3-CBC*. If ``pkcs8`` is ``False``, the obsolete PEM encryption scheme is used. It is based on MD5 for key derivation, and Triple DES for encryption. Parameter ``protection`` is ignored. The combination ``format='DER'`` and ``pkcs8=False`` is not allowed if a passphrase is present. randfunc : callable A function that returns random bytes. By default it is `Crypto.Random.get_random_bytes`. :Return: A byte string with the encoded public or private half of the key. :Raise ValueError: When the format is unknown or when you try to encrypt a private key with *DER* format and OpenSSL/OpenSSH. :attention: 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 .. _RFC4253: http://www.ietf.org/rfc/rfc4253.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': tup1 = [self._key[x].to_bytes() for x in 'p', 'q', 'g', 'y'] def func(x): if (bord(x[0]) & 0x80): return bchr(0) + x else: return x tup2 = map(func, tup1) keyparts = [b('ssh-dss')] + tup2 keystring = b('').join( [struct.pack(">I", len(kp)) + kp for kp in keyparts] ) return b('ssh-dss ') + binascii.b2a_base64(keystring)[:-1] # DER format is always used, even in case of PEM, which simply # encodes it into BASE64. params = newDerSequence(self.p, self.q, self.g) if self.has_private(): if pkcs8 is None: pkcs8 = True if pkcs8: if not protection: protection = 'PBKDF2WithHMAC-SHA1AndDES-EDE3-CBC' private_key = DerInteger(self.x).encode() binary_key = PKCS8.wrap( private_key, oid, passphrase, protection, key_params=params, randfunc=randfunc ) if passphrase: key_type = 'ENCRYPTED PRIVATE' else: key_type = 'PRIVATE' passphrase = None else: if format != 'PEM' and passphrase: raise ValueError("DSA private key cannot be encrypted") ints = [0, self.p, self.q, self.g, self.y, self.x] binary_key = newDerSequence(*ints).encode() key_type = "DSA PRIVATE" else: if pkcs8: raise ValueError("PKCS#8 is only meaningful for private keys") binary_key = newDerSequence( newDerSequence(DerObjectId(oid), params), newDerBitString(DerInteger(self.y)) ).encode() key_type = "DSA PUBLIC" if format == 'DER': return binary_key if format == 'PEM': pem_str = PEM.encode( binary_key, key_type + " KEY", passphrase, randfunc ) return tobytes(pem_str) raise ValueError("Unknown key format '%s'. Cannot export the DSA key." % format)
def exportKey(self, format='PEM', pkcs8=None, passphrase=None, protection=None, randfunc=None): """Export this DSA key. Args: format (string): The encoding for the output: - *'PEM'* (default). ASCII as per `RFC1421`_/ `RFC1423`_. - *'DER'*. Binary ASN.1 encoding. - *'OpenSSH'*. ASCII one-liner as per `RFC4253`_. Only suitable for public keys, not for private keys. passphrase (string): *Private keys only*. The pass phrase to protect the output. pkcs8 (boolean): *Private keys only*. If ``True`` (default), the key is encoded with `PKCS#8`_. If ``False``, it is encoded in the custom OpenSSL/OpenSSH container. protection (string): *Only in combination with a pass phrase*. The encryption scheme to use to protect the output. If :data:`pkcs8` takes value ``True``, this is the PKCS#8 algorithm to use for deriving the secret and encrypting the private DSA key. For a complete list of algorithms, see :mod:`Crypto.IO.PKCS8`. The default is *PBKDF2WithHMAC-SHA1AndDES-EDE3-CBC*. If :data:`pkcs8` is ``False``, the obsolete PEM encryption scheme is used. It is based on MD5 for key derivation, and Triple DES for encryption. Parameter :data:`protection` is then ignored. The combination ``format='DER'`` and ``pkcs8=False`` is not allowed if a passphrase is present. randfunc (callable): A function that returns random bytes. By default it 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 OpenSSL/OpenSSH. .. 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 .. _RFC4253: http://www.ietf.org/rfc/rfc4253.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': tup1 = [self._key[x].to_bytes() for x in 'p', 'q', 'g', 'y'] def func(x): if (bord(x[0]) & 0x80): return bchr(0) + x else: return x tup2 = map(func, tup1) keyparts = [b('ssh-dss')] + tup2 keystring = b('').join( [struct.pack(">I", len(kp)) + kp for kp in keyparts] ) return b('ssh-dss ') + binascii.b2a_base64(keystring)[:-1] # DER format is always used, even in case of PEM, which simply # encodes it into BASE64. params = DerSequence([self.p, self.q, self.g]) if self.has_private(): if pkcs8 is None: pkcs8 = True if pkcs8: if not protection: protection = 'PBKDF2WithHMAC-SHA1AndDES-EDE3-CBC' private_key = DerInteger(self.x).encode() binary_key = PKCS8.wrap( private_key, oid, passphrase, protection, key_params=params, randfunc=randfunc ) if passphrase: key_type = 'ENCRYPTED PRIVATE' else: key_type = 'PRIVATE' passphrase = None else: if format != 'PEM' and passphrase: raise ValueError("DSA private key cannot be encrypted") ints = [0, self.p, self.q, self.g, self.y, self.x] binary_key = DerSequence(ints).encode() key_type = "DSA PRIVATE" else: if pkcs8: raise ValueError("PKCS#8 is only meaningful for private keys") binary_key = _create_subject_public_key_info(oid, DerInteger(self.y), params) key_type = "PUBLIC" if format == 'DER': return binary_key if format == 'PEM': pem_str = PEM.encode( binary_key, key_type + " KEY", passphrase, randfunc ) return tobytes(pem_str) raise ValueError("Unknown key format '%s'. Cannot export the DSA key." % format)
def test2(self): """Verify wrapping w/o encryption""" wrapped = PKCS8.wrap(self.clear_key, self.oid_key) res1, res2, res3 = PKCS8.unwrap(wrapped) self.assertEqual(res1, self.oid_key) self.assertEqual(res2, self.clear_key)
def test1(self): """Verify unwrapping w/o encryption""" res1, res2, res3 = PKCS8.unwrap(self.wrapped_clear_key) self.assertEqual(res1, self.oid_key) self.assertEqual(res2, self.clear_key)
def exportKey(self, format='PEM', pkcs8=None, passphrase=None, protection=None): if passphrase is not None: passphrase = tobytes(passphrase) if format == 'OpenSSH': tup1 = [long_to_bytes(x) for x in (self.p, self.q, self.g, self.y)] def func(x): if bord(x[0]) & 128: return bchr(0) + x else: return x tup2 = map(func, tup1) keyparts = [b('ssh-dss')] + tup2 keystring = b('').join( [struct.pack('>I', len(kp)) + kp for kp in keyparts]) return b('ssh-dss ') + binascii.b2a_base64(keystring)[:-1] else: params = newDerSequence(self.p, self.q, self.g) if self.has_private(): if pkcs8 is None: pkcs8 = True if pkcs8: if not protection: protection = 'PBKDF2WithHMAC-SHA1AndDES-EDE3-CBC' private_key = DerInteger(self.x).encode() binary_key = PKCS8.wrap(private_key, oid, passphrase, protection, key_params=params, randfunc=self._randfunc) if passphrase: key_type = 'ENCRYPTED PRIVATE' else: key_type = 'PRIVATE' passphrase = None else: if format != 'PEM' and passphrase: raise ValueError('DSA private key cannot be encrypted') ints = [0, self.p, self.q, self.g, self.y, self.x] binary_key = newDerSequence(*ints).encode() key_type = 'DSA PRIVATE' else: if pkcs8: raise ValueError( 'PKCS#8 is only meaningful for private keys') binary_key = newDerSequence( newDerSequence(DerObjectId(oid), params), newDerBitString(DerInteger(self.y))).encode() key_type = 'DSA PUBLIC' if format == 'DER': return binary_key if format == 'PEM': pem_str = PEM.encode(binary_key, key_type + ' KEY', passphrase, self._randfunc) return tobytes(pem_str) raise ValueError( "Unknown key format '%s'. Cannot export the DSA key." % format) return
def exportKey(self, format='PEM', pkcs8=None, passphrase=None, protection=None, randfunc=None): """Export this DSA key. Args: format (string): The encoding for the output: - *'PEM'* (default). ASCII as per `RFC1421`_/ `RFC1423`_. - *'DER'*. Binary ASN.1 encoding. - *'OpenSSH'*. ASCII one-liner as per `RFC4253`_. Only suitable for public keys, not for private keys. passphrase (string): *Private keys only*. The pass phrase to protect the output. pkcs8 (boolean): *Private keys only*. If ``True`` (default), the key is encoded with `PKCS#8`_. If ``False``, it is encoded in the custom OpenSSL/OpenSSH container. protection (string): *Only in combination with a pass phrase*. The encryption scheme to use to protect the output. If :data:`pkcs8` takes value ``True``, this is the PKCS#8 algorithm to use for deriving the secret and encrypting the private DSA key. For a complete list of algorithms, see :mod:`Crypto.IO.PKCS8`. The default is *PBKDF2WithHMAC-SHA1AndDES-EDE3-CBC*. If :data:`pkcs8` is ``False``, the obsolete PEM encryption scheme is used. It is based on MD5 for key derivation, and Triple DES for encryption. Parameter :data:`protection` is then ignored. The combination ``format='DER'`` and ``pkcs8=False`` is not allowed if a passphrase is present. randfunc (callable): A function that returns random bytes. By default it 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 OpenSSL/OpenSSH. .. 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 .. _RFC4253: http://www.ietf.org/rfc/rfc4253.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': tup1 = [self._key[x].to_bytes() for x in ('p', 'q', 'g', 'y')] def func(x): if (bord(x[0]) & 0x80): return bchr(0) + x else: return x tup2 = list(map(func, tup1)) keyparts = [b('ssh-dss')] + tup2 keystring = b('').join( [struct.pack(">I", len(kp)) + kp for kp in keyparts]) return b('ssh-dss ') + binascii.b2a_base64(keystring)[:-1] # DER format is always used, even in case of PEM, which simply # encodes it into BASE64. params = DerSequence([self.p, self.q, self.g]) if self.has_private(): if pkcs8 is None: pkcs8 = True if pkcs8: if not protection: protection = 'PBKDF2WithHMAC-SHA1AndDES-EDE3-CBC' private_key = DerInteger(self.x).encode() binary_key = PKCS8.wrap(private_key, oid, passphrase, protection, key_params=params, randfunc=randfunc) if passphrase: key_type = 'ENCRYPTED PRIVATE' else: key_type = 'PRIVATE' passphrase = None else: if format != 'PEM' and passphrase: raise ValueError("DSA private key cannot be encrypted") ints = [0, self.p, self.q, self.g, self.y, self.x] binary_key = DerSequence(ints).encode() key_type = "DSA PRIVATE" else: if pkcs8: raise ValueError("PKCS#8 is only meaningful for private keys") binary_key = _create_subject_public_key_info( oid, DerInteger(self.y), params) key_type = "PUBLIC" if format == 'DER': return binary_key if format == 'PEM': pem_str = PEM.encode(binary_key, key_type + " KEY", passphrase, randfunc) return tobytes(pem_str) raise ValueError( "Unknown key format '%s'. Cannot export the DSA key." % format)
def exportKey(self, format='PEM', passphrase=None, pkcs=1, protection=None, randfunc=None): """Export this RSA key. :Parameters: format : string The format to use for wrapping the key: - *'DER'*. Binary encoding. - *'PEM'*. Textual encoding, done according to `RFC1421`_/`RFC1423`_. - *'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 deriving the encryption key. pkcs : integer For *DER* and *PEM* format only. The PKCS standard to follow for assembling the components of the key. You have two choices: - **1** (default): the public key is embedded into an X.509 ``SubjectPublicKeyInfo`` DER SEQUENCE. The private key is embedded into a `PKCS#1`_ ``RSAPrivateKey`` DER SEQUENCE. - **8**: the private key is embedded into a `PKCS#8`_ ``PrivateKeyInfo`` DER SEQUENCE. This value cannot be used for public keys. protection : string The encryption scheme to use for protecting the private key. If ``None`` (default), the behavior depends on ``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 `Crypto.Protocol.KDF.PBKDF2` with 8 bytes salt, and 1 000 iterations of `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 ``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 `Crypto.IO.PKCS8` module (see ``wrap_algo`` parameter). randfunc : callable A function that provides random bytes. Only used for PEM encoding. The default is `Crypto.Random.get_random_bytes`. :Return: A byte string with the encoded public or private half of the key. :Raise ValueError: When the format is unknown or when you try to encrypt a private key with *DER* format and PKCS#1. :attention: 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': eb, nb = [self._key[comp].to_bytes() for comp in 'e', 'n'] if bord(eb[0]) & 0x80: eb = bchr(0x00) + eb if bord(nb[0]) & 0x80: nb = bchr(0x00) + nb keyparts = [b('ssh-rsa'), eb, nb] 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 = newDerSequence( 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: keyType = 'RSA PRIVATE' if format == 'DER' and passphrase: raise ValueError("PKCS#1 private key cannot be encrypted") else: # PKCS#8 if format == 'PEM' and protection is None: keyType = 'PRIVATE' binary_key = PKCS8.wrap(binary_key, oid, None) else: keyType = 'ENCRYPTED PRIVATE' if not protection: protection = 'PBKDF2WithHMAC-SHA1AndDES-EDE3-CBC' binary_key = PKCS8.wrap(binary_key, oid, passphrase, protection) passphrase = None else: keyType = "RSA PUBLIC" binary_key = newDerSequence( algorithmIdentifier, newDerBitString(newDerSequence(self.n, self.e))).encode() if format == 'DER': return binary_key if format == 'PEM': pem_str = PEM.encode(binary_key, keyType + " KEY", passphrase, randfunc) return tobytes(pem_str) raise ValueError( "Unknown key format '%s'. Cannot export the RSA key." % format)
def _importKeyDER(key_data, passphrase, params): """Import a DSA key (public or private half), encoded in DER form.""" try: # # Dss-Parms ::= SEQUENCE { # p OCTET STRING, # q OCTET STRING, # g OCTET STRING # } # # Try a simple private key first if params: x = DerInteger().decode(key_data).value p, q, g = list(DerSequence().decode(params)) # Dss-Parms tup = (pow(g, x, p), g, p, q, x) return construct(tup) der = DerSequence().decode(key_data) # Try OpenSSL format for private keys if len(der) == 6 and der.hasOnlyInts() and der[0] == 0: tup = [der[comp] for comp in (4, 3, 1, 2, 5)] return construct(tup) # Try SubjectPublicKeyInfo if len(der) == 2: try: algo = DerSequence().decode(der[0]) algo_oid = DerObjectId().decode(algo[0]).value params = DerSequence().decode(algo[1]) # Dss-Parms if algo_oid == oid and len(params) == 3 and\ params.hasOnlyInts(): bitmap = DerBitString().decode(der[1]) pub_key = DerInteger().decode(bitmap.value) tup = [pub_key.value] tup += [params[comp] for comp in (2, 0, 1)] return construct(tup) except (ValueError, EOFError): pass # Try to see if this is an X.509 DER certificate # (Certificate ASN.1 type) if len(der) == 3: from Crypto.PublicKey import _extract_sp_info try: sp_info = _extract_sp_info(der) return _importKeyDER(sp_info, passphrase, None) except ValueError: pass # Try unencrypted PKCS#8 p8_pair = PKCS8.unwrap(key_data, passphrase) if p8_pair[0] == oid: return _importKeyDER(p8_pair[1], passphrase, p8_pair[2]) except (ValueError, EOFError): pass raise ValueError("DSA key format is not supported")
def exportKey(self, format='PEM', passphrase=None, pkcs=1, protection=None, randfunc=None): """Export this RSA key. :Parameters: format : string The format to use for wrapping the key: - *'DER'*. Binary encoding. - *'PEM'*. Textual encoding, done according to `RFC1421`_/`RFC1423`_. - *'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 deriving the encryption key. pkcs : integer For *DER* and *PEM* format only. The PKCS standard to follow for assembling the components of the key. You have two choices: - **1** (default): the public key is embedded into an X.509 ``SubjectPublicKeyInfo`` DER SEQUENCE. The private key is embedded into a `PKCS#1`_ ``RSAPrivateKey`` DER SEQUENCE. - **8**: the private key is embedded into a `PKCS#8`_ ``PrivateKeyInfo`` DER SEQUENCE. This value cannot be used for public keys. protection : string The encryption scheme to use for protecting the private key. If ``None`` (default), the behavior depends on ``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 `Crypto.Protocol.KDF.PBKDF2` with 8 bytes salt, and 1 000 iterations of `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 ``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 `Crypto.IO.PKCS8` module (see ``wrap_algo`` parameter). randfunc : callable A function that provides random bytes. Only used for PEM encoding. The default is `Crypto.Random.get_random_bytes`. :Return: A byte string with the encoded public or private half of the key. :Raise ValueError: When the format is unknown or when you try to encrypt a private key with *DER* format and PKCS#1. :attention: 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': eb, nb = [self._key[comp].to_bytes() for comp in 'e', 'n'] if bord(eb[0]) & 0x80: eb=bchr(0x00)+eb if bord(nb[0]) & 0x80: nb=bchr(0x00)+nb keyparts = [ b('ssh-rsa'), eb, nb ] 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 = newDerSequence( 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: keyType = 'RSA PRIVATE' if format=='DER' and passphrase: raise ValueError("PKCS#1 private key cannot be encrypted") else: # PKCS#8 if format=='PEM' and protection is None: keyType = 'PRIVATE' binary_key = PKCS8.wrap(binary_key, oid, None) else: keyType = 'ENCRYPTED PRIVATE' if not protection: protection = 'PBKDF2WithHMAC-SHA1AndDES-EDE3-CBC' binary_key = PKCS8.wrap(binary_key, oid, passphrase, protection) passphrase = None else: keyType = "RSA PUBLIC" binary_key = newDerSequence( algorithmIdentifier, newDerBitString( newDerSequence( self.n, self.e ) ) ).encode() if format=='DER': return binary_key if format=='PEM': pem_str = PEM.encode(binary_key, keyType+" KEY", passphrase, randfunc) return tobytes(pem_str) raise ValueError("Unknown key format '%s'. Cannot export the RSA key." % format)
def exportKey(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 = bchr(0) + e_bytes if bord(n_bytes[0]) & 0x80: n_bytes = bchr(0) + 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 if format == 'PEM' and protection is None: key_type = 'PRIVATE KEY' binary_key = PKCS8.wrap(binary_key, oid, None) else: key_type = 'ENCRYPTED PRIVATE KEY' if not protection: protection = 'PBKDF2WithHMAC-SHA1AndDES-EDE3-CBC' binary_key = PKCS8.wrap(binary_key, oid, passphrase, protection) passphrase = None else: key_type = "PUBLIC KEY" binary_key = _create_subject_public_key_info(oid, DerSequence([self.n, self.e]) ) if format == 'DER': return binary_key if format == '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)
def exportKey(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 = bchr(0) + e_bytes if bord(n_bytes[0]) & 0x80: n_bytes = bchr(0) + 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 if format == 'PEM' and protection is None: key_type = 'PRIVATE KEY' binary_key = PKCS8.wrap(binary_key, oid, None) else: key_type = 'ENCRYPTED PRIVATE KEY' if not protection: protection = 'PBKDF2WithHMAC-SHA1AndDES-EDE3-CBC' binary_key = PKCS8.wrap(binary_key, oid, passphrase, protection) passphrase = None else: key_type = "PUBLIC KEY" binary_key = _create_subject_public_key_info( oid, DerSequence([self.n, self.e])) if format == 'DER': return binary_key if format == '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)
def _import_pkcs8(encoded, passphrase): k = PKCS8.unwrap(encoded, passphrase) if k[0] != oid: raise ValueError("No PKCS#8 encoded RSA key") return _import_keyDER(k[1], passphrase)