Add import and export functions for passphrase-protected pem files in keys.py

This commit is contained in:
vladdd 2013-10-22 14:01:06 -04:00
parent 45af91191a
commit 5eb0858e45

View file

@ -759,6 +759,185 @@ def verify_signature(key_dict, signature, data):
def import_rsakey_from_encrypted_pem(encrypted_pem, password):
"""
<Purpose>
Generate public and private RSA keys, with modulus length 'bits'. In
addition, a keyid identifier for the RSA key is generated. The object
returned conforms to 'tuf.formats.RSAKEY_SCHEMA' and has the
form:
{'keytype': 'rsa',
'keyid': keyid,
'keyval': {'public': '-----BEGIN RSA PUBLIC KEY----- ...',
'private': '-----BEGIN RSA PRIVATE KEY----- ...'}}
The public and private keys are strings in PEM format.
Although the PyCrypto crytography library called sets a 1024-bit minimum
key size, generate() enforces a minimum key size of 2048 bits. If 'bits' is
unspecified, a 3072-bit RSA key is generated, which is the key size
recommended by TUF.
>>> rsa_key = generate_rsa_key()
>>> private = rsa_key['keyval']['private']
>>> passphrase = 'secret'
>>> encrypted_pem = create_rsa_encrypted_pem(private, passphrase)
>>> rsa_key2 = import_rsakey_from_encrypted_pem(encrypted_pem, passphrase)
>>> rsa_key == rsa_key2
True
<Arguments>
encrypted_pem:
The key size, or key length, of the RSA key. 'bits' must be 2048, or
greater, and a multiple of 256.
password:
<Exceptions>
tuf.FormatError, if 'bits' is improperly or invalid (i.e., not an integer
and not at least 2048).
tuf.UnsupportedLibraryError, if any of the cryptography libraries specified
in 'tuf.conf.py' are unsupported or unavailable.
ValueError, if an exception occurs after calling the RSA key generation
routine. 'bits' must be a multiple of 256. The 'ValueError' exception is
raised by the key generation function of the cryptography library called.
<Side Effects>
The RSA keys are generated by calling PyCrypto's
Crypto.PublicKey.RSA.generate().
<Returns>
A dictionary containing the RSA keys and other identifying information.
Conforms to 'tuf.formats.RSAKEY_SCHEMA'.
"""
# Does 'encrypted_pem' have the correct format?
# This check will ensure 'encrypted_pem' conforms to
# 'tuf.formats.PEMRSA_SCHEMA'.
tuf.formats.PEMRSA_SCHEMA.check_match(encrypted_pem)
# Does 'password' have the correct format?
tuf.formats.PASSWORD_SCHEMA.check_match(password)
# Raise 'tuf.UnsupportedLibraryError' if the following libraries, specified in
# 'tuf.conf', are unsupported or unavailable:
# 'tuf.conf.RSA_CRYPTO_LIBRARY' and 'tuf.conf.ED25519_CRYPTO_LIBRARY'.
_check_crypto_libraries()
# Begin building the RSA key dictionary.
rsakey_dict = {}
keytype = 'rsa'
public = None
private = None
# Generate the public and private RSA keys. The PyCrypto module performs
# the actual key generation. Raise 'ValueError' if 'bits' is less than 1024
# or not a multiple of 256, although a 2048-bit minimum is enforced by
# tuf.formats.RSAKEYBITS_SCHEMA.check_match().
if _RSA_CRYPTO_LIBRARY == 'pycrypto':
public, private = \
tuf.pycrypto_keys.create_rsa_public_and_private_from_encrypted_pem(encrypted_pem,
password)
else:
message = 'Invalid crypto library: '+repr(_RSA_CRYPTO_LIBRARY)+'.'
raise tuf.UnsupportedLibraryError(message)
# Generate the keyid of the RSA key. 'key_value' corresponds to the
# 'keyval' entry of the 'RSAKEY_SCHEMA' dictionary. The private key
# information is not included in the generation of the 'keyid' identifier.
key_value = {'public': public,
'private': ''}
keyid = _get_keyid(keytype, key_value)
# Build the 'rsakey_dict' dictionary. Update 'key_value' with the RSA
# private key prior to adding 'key_value' to 'rsakey_dict'.
key_value['private'] = private
rsakey_dict['keytype'] = keytype
rsakey_dict['keyid'] = keyid
rsakey_dict['keyval'] = key_value
return rsakey_dict
def create_rsa_encrypted_pem(private_key, passphrase):
"""
<Purpose>
Return a string in PEM format, where the private part of the RSA key is
encrypted. The private part of the RSA key is encrypted by the Triple
Data Encryption Algorithm (3DES) and Cipher-block chaining (CBC) for the
mode of operation. Password-Based Key Derivation Function 1 (PBKF1) + MD5
is used to strengthen 'passphrase'.
https://en.wikipedia.org/wiki/Triple_DES
https://en.wikipedia.org/wiki/PBKDF2
>>> rsa_key = generate_rsa_key()
>>> private = rsa_key['keyval']['private']
>>> passphrase = 'secret'
>>> encrypted_pem = create_rsa_encrypted_pem(private, passphrase)
>>> tuf.formats.PEMRSA_SCHEMA.matches(encrypted_pem)
True
<Arguments>
private_key:
The private key string in PEM format.
passphrase:
The passphrase, or password, to encrypt the private part of the RSA
key. 'passphrase' is not used directly as the encryption key, a stronger
encryption key is derived from it.
<Exceptions>
tuf.FormatError, if the arguments are improperly formatted.
tuf.CryptoError, if an RSA key in encrypted PEM format cannot be created.
TypeError, 'private_key' is unset.
<Side Effects>
PyCrypto's Crypto.PublicKey.RSA.exportKey() called to perform the actual
generation of the PEM-formatted output.
<Returns>
A string in PEM format, where the private RSA key is encrypted.
Conforms to 'tuf.formats.PEMRSA_SCHEMA'.
"""
# Does 'private_key' have the correct format?
# This check will ensure 'private_key' has the appropriate number
# of objects and object types, and that all dict keys are properly named.
# Raise 'tuf.FormatError' if the check fails.
tuf.formats.PEMRSA_SCHEMA.check_match(private_key)
# Does 'passphrase' have the correct format?
tuf.formats.PASSWORD_SCHEMA.check_match(passphrase)
encrypted_pem = None
# Generate the public and private RSA keys. The PyCrypto module performs
# the actual key generation. Raise 'ValueError' if 'bits' is less than 1024
# or not a multiple of 256, although a 2048-bit minimum is enforced by
# tuf.formats.RSAKEYBITS_SCHEMA.check_match().
if _RSA_CRYPTO_LIBRARY == 'pycrypto':
encrypted_pem = \
tuf.pycrypto_keys.create_rsa_encrypted_pem(private_key, passphrase)
else:
message = 'Invalid crypto library: '+repr(_RSA_CRYPTO_LIBRARY)+'.'
raise tuf.UnsupportedLibraryError(message)
return encrypted_pem
if __name__ == '__main__':
# The interactive sessions of the documentation strings can
# be tested by running 'keys.py' as a standalone module.