def _export_private_encrypted_pkcs8_in_clear_pem(self, passphrase, **kwargs): from crypto.IO import PEM assert passphrase if 'protection' not in kwargs: raise ValueError( "At least the 'protection' parameter should be present") encoded_der = self._export_pkcs8(passphrase=passphrase, **kwargs) return PEM.encode(encoded_der, "ENCRYPTED PRIVATE KEY")
def import_key(extern_key, passphrase=None): """Import a DSA key. Args: extern_key (string or byte string): The DSA key to import. The following formats are supported for a DSA **public** key: - X.509 certificate (binary DER or PEM) - X.509 ``subjectPublicKeyInfo`` (binary DER or PEM) - OpenSSH (ASCII one-liner, see `RFC4253`_) The following formats are supported for a DSA **private** key: - `PKCS#8`_ ``PrivateKeyInfo`` or ``EncryptedPrivateKeyInfo`` DER SEQUENCE (binary or PEM) - OpenSSL/OpenSSH custom format (binary or PEM) For details about the PEM encoding, see `RFC1421`_/`RFC1423`_. passphrase (string): In case of an encrypted private key, this is the pass phrase from which the decryption key is derived. Encryption may be applied either at the `PKCS#8`_ or at the PEM level. Returns: :class:`DsaKey` : a DSA key object Raises: ValueError : when the given key cannot be parsed (possibly because the pass phrase is wrong). .. _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 """ extern_key = tobytes(extern_key) if passphrase is not None: passphrase = tobytes(passphrase) if extern_key.startswith(b'-----'): # This is probably a PEM encoded key (der, marker, enc_flag) = PEM.decode(tostr(extern_key), passphrase) if enc_flag: passphrase = None return _import_key_der(der, passphrase, None) if extern_key.startswith(b'ssh-dss '): # This is probably a public OpenSSH key keystring = binascii.a2b_base64(extern_key.split(b' ')[1]) keyparts = [] while len(keystring) > 4: length = struct.unpack(">I", keystring[:4])[0] keyparts.append(keystring[4:4 + length]) keystring = keystring[4 + length:] if keyparts[0] == b"ssh-dss": tup = [Integer.from_bytes(keyparts[x]) for x in (4, 3, 1, 2)] return construct(tup) if len(extern_key) > 0 and bord(extern_key[0]) == 0x30: # This is probably a DER encoded key return _import_key_der(extern_key, passphrase, None) raise ValueError("DSA key format is not supported")
def export_key(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 = [func(x) for x in 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 import_key(encoded, passphrase=None): """Import an ECC key (public or private). Args: encoded (bytes or multi-line string): The ECC key to import. An ECC **public** key can be: - An X.509 certificate, binary (DER) or ASCII (PEM) - An X.509 ``subjectPublicKeyInfo``, binary (DER) or ASCII (PEM) - An OpenSSH line (e.g. the content of ``~/.ssh/id_ecdsa``, ASCII) An ECC **private** key can be: - In binary format (DER, see section 3 of `RFC5915`_ or `PKCS#8`_) - In ASCII format (PEM or OpenSSH) Private keys can be in the clear or password-protected. For details about the PEM encoding, see `RFC1421`_/`RFC1423`_. passphrase (byte string): The passphrase to use for decrypting a private key. Encryption may be applied protected at the PEM level or at the PKCS#8 level. This parameter is ignored if the key in input is not encrypted. Returns: :class:`EccKey` : a new ECC key object Raises: ValueError: when the given key cannot be parsed (possibly because the pass phrase is wrong). .. _RFC1421: http://www.ietf.org/rfc/rfc1421.txt .. _RFC1423: http://www.ietf.org/rfc/rfc1423.txt .. _RFC5915: http://www.ietf.org/rfc/rfc5915.txt .. _`PKCS#8`: http://www.ietf.org/rfc/rfc5208.txt """ encoded = tobytes(encoded) if passphrase is not None: passphrase = tobytes(passphrase) # PEM if encoded.startswith(b('-----')): der_encoded, marker, enc_flag = PEM.decode(tostr(encoded), passphrase) if enc_flag: passphrase = None try: result = _import_der(der_encoded, passphrase) except UnsupportedEccFeature as uef: raise uef except ValueError: raise ValueError("Invalid DER encoding inside the PEM file") return result # OpenSSH if encoded.startswith(b('ecdsa-sha2-')): return _import_openssh(encoded) # DER if bord(encoded[0]) == 0x30: return _import_der(encoded, passphrase) raise ValueError("ECC key format is not supported")
def _export_private_clear_pkcs8_in_clear_pem(self): encoded_der = self._export_pkcs8() return PEM.encode(encoded_der, "PRIVATE KEY")
def _export_private_pem(self, passphrase, **kwargs): encoded_der = self._export_private_der() return PEM.encode(encoded_der, "EC PRIVATE KEY", passphrase, **kwargs)
def _export_public_pem(self, compress): encoded_der = self._export_subjectPublicKeyInfo(compress) return PEM.encode(encoded_der, "PUBLIC KEY")
def _export_private_clear_pkcs8_in_clear_pem(self): from crypto.IO import PEM encoded_der = self._export_pkcs8() return PEM.encode(encoded_der, "PRIVATE KEY")
def import_key(encoded, passphrase=None): """Import an ECC key (public or private). Args: encoded (bytes or multi-line string): The ECC key to import. An ECC **public** key can be: - An X.509 certificate, binary (DER) or ASCII (PEM) - An X.509 ``subjectPublicKeyInfo``, binary (DER) or ASCII (PEM) - An OpenSSH line (e.g. the content of ``~/.ssh/id_ecdsa``, ASCII) An ECC **private** key can be: - In binary format (DER, see section 3 of `RFC5915`_ or `PKCS#8`_) - In ASCII format (PEM or `OpenSSH 6.5+`_) Private keys can be in the clear or password-protected. For details about the PEM encoding, see `RFC1421`_/`RFC1423`_. passphrase (byte string): The passphrase to use for decrypting a private key. Encryption may be applied protected at the PEM level or at the PKCS#8 level. This parameter is ignored if the key in input is not encrypted. Returns: :class:`EccKey` : a new ECC key object Raises: ValueError: when the given key cannot be parsed (possibly because the pass phrase is wrong). .. _RFC1421: http://www.ietf.org/rfc/rfc1421.txt .. _RFC1423: http://www.ietf.org/rfc/rfc1423.txt .. _RFC5915: http://www.ietf.org/rfc/rfc5915.txt .. _`PKCS#8`: http://www.ietf.org/rfc/rfc5208.txt .. _`OpenSSH 6.5+`: https://flak.tedunangst.com/post/new-openssh-key-format-and-bcrypt-pbkdf """ from crypto.IO import PEM encoded = tobytes(encoded) if passphrase is not None: passphrase = tobytes(passphrase) # PEM if encoded.startswith(b'-----BEGIN OPENSSH PRIVATE KEY'): text_encoded = tostr(encoded) openssh_encoded, marker, enc_flag = PEM.decode(text_encoded, passphrase) result = _import_openssh_private_ecc(openssh_encoded, passphrase) return result elif encoded.startswith(b'-----'): text_encoded = tostr(encoded) # Remove any EC PARAMETERS section # Ignore its content because the curve type must be already given in the key if sys.version_info[:2] != (2, 6): ecparams_start = "-----BEGIN EC PARAMETERS-----" ecparams_end = "-----END EC PARAMETERS-----" text_encoded = re.sub(ecparams_start + ".*?" + ecparams_end, "", text_encoded, flags=re.DOTALL) der_encoded, marker, enc_flag = PEM.decode(text_encoded, passphrase) if enc_flag: passphrase = None try: result = _import_der(der_encoded, passphrase) except UnsupportedEccFeature as uef: raise uef except ValueError: raise ValueError("Invalid DER encoding inside the PEM file") return result # OpenSSH if encoded.startswith(b'ecdsa-sha2-'): return _import_openssh_public(encoded) # DER if len(encoded) > 0 and bord(encoded[0]) == 0x30: return _import_der(encoded, passphrase) raise ValueError("ECC key format is not supported")
def import_key(extern_key, passphrase=None): """Import an RSA key (public or private half), encoded in standard form. Args: extern_key (string or byte string): The RSA key to import. The following formats are supported for an RSA **public key**: - X.509 certificate (binary or PEM format) - X.509 ``subjectPublicKeyInfo`` DER SEQUENCE (binary or PEM encoding) - `PKCS#1`_ ``RSAPublicKey`` DER SEQUENCE (binary or PEM encoding) - OpenSSH (textual public key only) The following formats are supported for an RSA **private key**: - PKCS#1 ``RSAPrivateKey`` DER SEQUENCE (binary or PEM encoding) - `PKCS#8`_ ``PrivateKeyInfo`` or ``EncryptedPrivateKeyInfo`` DER SEQUENCE (binary or PEM encoding) - OpenSSH (textual public key only) For details about the PEM encoding, see `RFC1421`_/`RFC1423`_. The private key may be encrypted by means of a certain pass phrase either at the PEM level or at the PKCS#8 level. passphrase (string): In case of an encrypted private key, this is the pass phrase from which the decryption key is derived. Returns: An RSA key object (:class:`RsaKey`). Raises: ValueError/IndexError/TypeError: When the given key cannot be parsed (possibly because the pass phrase is wrong). .. _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 """ extern_key = tobytes(extern_key) if passphrase is not None: passphrase = tobytes(passphrase) if extern_key.startswith(b('-----')): # This is probably a PEM encoded key. (der, marker, enc_flag) = PEM.decode(tostr(extern_key), passphrase) if enc_flag: passphrase = None return _import_keyDER(der, passphrase) if extern_key.startswith(b('ssh-rsa ')): # 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 _import_keyDER(extern_key, passphrase) raise ValueError("RSA key format is not supported")
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 = 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)