diff options
author | Eric Brown <browne@vmware.com> | 2015-07-03 00:20:05 -0700 |
---|---|---|
committer | Eric Brown <browne@vmware.com> | 2015-07-27 14:41:44 -0700 |
commit | 452fe92787ff871417846748fc13e2a6a2899325 (patch) | |
tree | dc2a30518aeff5cc5df26b30fd392f31ebf42c33 /nova/crypto.py | |
parent | 3ee98a619f64a7bbbbc4f58b4f5d44b759958f18 (diff) | |
download | nova-452fe92787ff871417846748fc13e2a6a2899325.tar.gz |
Replace openssl calls with cryptography lib
This patch is a code reduction cleanup fix. Exec calls to openssl
can mostly be replaced with a proper python library called
cryptography.
Changed the following:
* Removed convert_from_sshrsa_to_pkcs8 and _to_sequence since
cryptography already has a method to do this conversion.
* Replaced openssl rsautl call in ssh_encrypt_text and decrypt_text
with cryptography's public key encrypt and decrypt methods.
* Replaced call to openssl x509 with cryptography's
load_pem_x509_certificate which also includes a way to retrieve
the fingerprint.
* generate_fingerprint's calls to paramiko were replaced with
cryptography's serialization module. Cryptography supports all
key sizes of elliptic curve keys whereas paramiko did not.
* Removed the requirement on pyasn1
Change-Id: I750b49e6a73cd18af8199c1c859c9f6047252387
Diffstat (limited to 'nova/crypto.py')
-rw-r--r-- | nova/crypto.py | 156 |
1 files changed, 41 insertions, 115 deletions
diff --git a/nova/crypto.py b/nova/crypto.py index 1b050788f3..dfaafcfdad 100644 --- a/nova/crypto.py +++ b/nova/crypto.py @@ -25,9 +25,13 @@ from __future__ import absolute_import import base64 import binascii import os -import re -import struct +from cryptography import exceptions +from cryptography.hazmat import backends +from cryptography.hazmat.primitives.asymmetric import padding +from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.primitives import serialization +from cryptography import x509 from oslo_concurrency import processutils from oslo_config import cfg from oslo_log import log as logging @@ -35,8 +39,6 @@ from oslo_utils import excutils from oslo_utils import fileutils from oslo_utils import timeutils import paramiko -from pyasn1.codec.der import encoder as der_encoder -from pyasn1.type import univ import six from nova import context @@ -130,24 +132,21 @@ def ensure_ca_filesystem(): def generate_fingerprint(public_key): try: - parts = public_key.split(' ') - ssh_alg = parts[0] - pub_data = base64.b64decode(parts[1]) - if ssh_alg == 'ssh-rsa': - pkey = paramiko.RSAKey(data=pub_data) - elif ssh_alg == 'ssh-dss': - pkey = paramiko.DSSKey(data=pub_data) - elif ssh_alg == 'ecdsa-sha2-nistp256': - pkey = paramiko.ECDSAKey(data=pub_data, validate_point=False) - else: - raise exception.InvalidKeypair( - reason=_('Unknown ssh key type %s') % ssh_alg) - raw_fp = binascii.hexlify(pkey.get_fingerprint()) + pub_bytes = public_key.encode('utf-8') + # Test that the given public_key string is a proper ssh key. The + # returned object is unused since pyca/cryptography does not have a + # fingerprint method. + serialization.load_ssh_public_key( + pub_bytes, backends.default_backend()) + pub_data = base64.b64decode(public_key.split(' ')[1]) + digest = hashes.Hash(hashes.MD5(), backends.default_backend()) + digest.update(pub_data) + md5hash = digest.finalize() + raw_fp = binascii.hexlify(md5hash) if six.PY3: raw_fp = raw_fp.decode('ascii') return ':'.join(a + b for a, b in zip(raw_fp[::2], raw_fp[1::2])) - except (TypeError, IndexError, UnicodeDecodeError, binascii.Error, - paramiko.ssh_exception.SSHException): + except Exception: raise exception.InvalidKeypair( reason=_('failed to generate fingerprint')) @@ -156,12 +155,13 @@ def generate_x509_fingerprint(pem_key): try: if isinstance(pem_key, six.text_type): pem_key = pem_key.encode('utf-8') - (out, _err) = utils.execute('openssl', 'x509', '-inform', 'PEM', - '-fingerprint', '-noout', - process_input=pem_key) - fingerprint = out.rpartition('=')[2].strip() - return fingerprint.lower() - except processutils.ProcessExecutionError as ex: + cert = x509.load_pem_x509_certificate( + pem_key, backends.default_backend()) + raw_fp = binascii.hexlify(cert.fingerprint(hashes.SHA1())) + if six.PY3: + raw_fp = raw_fp.decode('ascii') + return ':'.join(a + b for a, b in zip(raw_fp[::2], raw_fp[1::2])) + except (ValueError, TypeError, binascii.Error) as ex: raise exception.InvalidKeypair( reason=_('failed to generate X509 fingerprint. ' 'Error message: %s') % ex) @@ -189,81 +189,17 @@ def fetch_crl(project_id): def decrypt_text(project_id, text): - private_key = key_path(project_id) - if not os.path.exists(private_key): + private_key_file = key_path(project_id) + if not os.path.exists(private_key_file): raise exception.ProjectNotFound(project_id=project_id) + with open(private_key_file, 'rb') as f: + data = f.read() try: - dec, _err = utils.execute('openssl', - 'rsautl', - '-decrypt', - '-inkey', '%s' % private_key, - process_input=text, - binary=True) - return dec - except processutils.ProcessExecutionError as exc: - raise exception.DecryptionFailure(reason=exc.stderr) - - -_RSA_OID = univ.ObjectIdentifier('1.2.840.113549.1.1.1') - - -def _to_sequence(*vals): - seq = univ.Sequence() - for i in range(len(vals)): - seq.setComponentByPosition(i, vals[i]) - return seq - - -def convert_from_sshrsa_to_pkcs8(pubkey): - """Convert a ssh public key to openssl format - Equivalent to the ssh-keygen's -m option - """ - # get the second field from the public key file. - try: - keydata = base64.b64decode(pubkey.split(None)[1]) - except IndexError: - msg = _("Unable to find the key") - raise exception.EncryptionFailure(reason=msg) - - # decode the parts of the key - parts = [] - while keydata: - dlen = struct.unpack('>I', keydata[:4])[0] - data = keydata[4:dlen + 4] - keydata = keydata[4 + dlen:] - parts.append(data) - - # Use asn to build the openssl key structure - # - # SEQUENCE(2 elem) - # +- SEQUENCE(2 elem) - # | +- OBJECT IDENTIFIER (1.2.840.113549.1.1.1) - # | +- NULL - # +- BIT STRING(1 elem) - # +- SEQUENCE(2 elem) - # +- INTEGER(2048 bit) - # +- INTEGER 65537 - - # Build the sequence for the bit string - n_val = int(binascii.hexlify(parts[2]), 16) - e_val = int(binascii.hexlify(parts[1]), 16) - pkinfo = _to_sequence(univ.Integer(n_val), univ.Integer(e_val)) - - # Convert the sequence into a bit string - pklong = int(binascii.hexlify(der_encoder.encode(pkinfo)), 16) - pkbitstring = univ.BitString("'00%s'B" % bin(pklong)[2:]) - - # Build the key data structure - oid = _to_sequence(_RSA_OID, univ.Null()) - pkcs1_seq = _to_sequence(oid, pkbitstring) - pkcs8 = base64.b64encode(der_encoder.encode(pkcs1_seq)) - if six.PY3: - pkcs8 = pkcs8.decode('ascii') - - # Remove the embedded new line and format the key, each line - # should be 64 characters long - return ('-----BEGIN PUBLIC KEY-----\n%s\n-----END PUBLIC KEY-----\n' % - re.sub("(.{64})", "\\1\n", pkcs8.replace('\n', ''), re.DOTALL)) + priv_key = serialization.load_pem_private_key( + data, None, backends.default_backend()) + return priv_key.decrypt(text, padding.PKCS1v15()) + except (ValueError, TypeError, exceptions.UnsupportedAlgorithm) as exc: + raise exception.DecryptionFailure(reason=six.text_type(exc)) def ssh_encrypt_text(ssh_public_key, text): @@ -273,23 +209,13 @@ def ssh_encrypt_text(ssh_public_key, text): """ if isinstance(text, six.text_type): text = text.encode('utf-8') - with utils.tempdir() as tmpdir: - sslkey = os.path.abspath(os.path.join(tmpdir, 'ssl.key')) - try: - out = convert_from_sshrsa_to_pkcs8(ssh_public_key) - with open(sslkey, 'w') as f: - f.write(out) - enc, _err = utils.execute('openssl', - 'rsautl', - '-encrypt', - '-pubin', - '-inkey', sslkey, - '-keyform', 'PEM', - process_input=text, - binary=True) - return enc - except processutils.ProcessExecutionError as exc: - raise exception.EncryptionFailure(reason=exc.stderr) + try: + pub_bytes = ssh_public_key.encode('utf-8') + pub_key = serialization.load_ssh_public_key( + pub_bytes, backends.default_backend()) + return pub_key.encrypt(text, padding.PKCS1v15()) + except Exception as exc: + raise exception.EncryptionFailure(reason=six.text_type(exc)) def revoke_cert(project_id, file_name): |