Source code for tlslite.utils.openssl_rsakey

# Author: Trevor Perrin
# See the LICENSE file for legal information regarding use of this file.

"""OpenSSL/M2Crypto RSA implementation."""

from .cryptomath import *

from .rsakey import *
from .python_rsakey import Python_RSAKey
from .compat import compatAscii2Bytes, compat_b2a

#copied from M2Crypto.util.py, so when we load the local copy of m2
#we can still use it
[docs] def password_callback(v, prompt1='Enter private key passphrase:', prompt2='Verify passphrase:'): from getpass import getpass while 1: try: p1=getpass(prompt1) if v: p2=getpass(prompt2) if p1==p2: break else: break except KeyboardInterrupt: return None return p1
if m2cryptoLoaded: # pylint: disable=import-error from M2Crypto.RSA import RSAError class OpenSSL_RSAKey(RSAKey): def __init__(self, n=0, e=0, key_type="rsa"): self.rsa = None self._hasPrivateKey = False if (n and not e) or (e and not n): raise AssertionError() if n and e: self.rsa = m2.rsa_new() m2.rsa_set_n(self.rsa, numberToMPI(n)) m2.rsa_set_e(self.rsa, numberToMPI(e)) self.key_type = key_type def __del__(self): if self.rsa: m2.rsa_free(self.rsa) def __getattr__(self, name): if name == 'e': if not self.rsa: return 0 return mpiToNumber(m2.rsa_get_e(self.rsa)) elif name == 'n': if not self.rsa: return 0 return mpiToNumber(m2.rsa_get_n(self.rsa)) else: raise AttributeError def hasPrivateKey(self): return self._hasPrivateKey def _rawPrivateKeyOp(self, message): data = numberToByteArray(message, numBytes(self.n)) string = m2.rsa_private_encrypt(self.rsa, bytes(data), m2.no_padding) ciphertext = bytesToNumber(bytearray(string)) return ciphertext def _raw_private_key_op_bytes(self, message): return self._call_m2crypto( m2.rsa_private_encrypt, message, "Bad parameters to private key operation") def _rawPublicKeyOp(self, ciphertext): data = numberToByteArray(ciphertext, numBytes(self.n)) string = m2.rsa_public_decrypt(self.rsa, bytes(data), m2.no_padding) message = bytesToNumber(bytearray(string)) return message def _call_m2crypto(self, method, param, err_msg): try: return bytearray(method(self.rsa, bytes(param), m2.no_padding)) except RSAError: raise ValueError(err_msg) def _raw_public_key_op_bytes(self, ciphertext): return self._call_m2crypto( m2.rsa_public_decrypt, ciphertext, "Bad parameters to public key operation") def acceptsPassword(self): return True def write(self, password=None): bio = m2.bio_new(m2.bio_s_mem()) if self._hasPrivateKey: if password: def f(v): return password m2.rsa_write_key(self.rsa, bio, m2.des_ede_cbc(), f) else: def f(): pass m2.rsa_write_key_no_cipher(self.rsa, bio, f) else: if password: raise AssertionError() m2.rsa_write_pub_key(self.rsa, bio) s = m2.bio_read(bio, m2.bio_ctrl_pending(bio)) m2.bio_free(bio) return s @staticmethod def generate(bits, key_type="rsa"): key = OpenSSL_RSAKey() def f():pass # pylint: disable=no-member key.rsa = m2.rsa_generate_key(bits, 65537, f) # pylint: enable=no-member key._hasPrivateKey = True key.key_type = key_type b64_key = compat_b2a(key.write()) py_key = Python_RSAKey.parsePEM(b64_key) key.d = py_key.d return key @staticmethod def parse(s, passwordCallback=None): # Skip forward to the first PEM header start = s.find("-----BEGIN ") if start == -1: raise SyntaxError() s = s[start:] if s.startswith("-----BEGIN "): if passwordCallback==None: callback = password_callback else: def f(v, prompt1=None, prompt2=None): return passwordCallback() callback = f bio = m2.bio_new(m2.bio_s_mem()) try: m2.bio_write(bio, compatAscii2Bytes(s)) key = OpenSSL_RSAKey() # parse SSLay format PEM file if s.startswith("-----BEGIN RSA PRIVATE KEY-----"): def f():pass key.rsa = m2.rsa_read_key(bio, callback) if key.rsa == None: raise SyntaxError() key._hasPrivateKey = True # parse a standard PKCS#8 PEM file elif s.startswith("-----BEGIN PRIVATE KEY-----"): def f():pass key.rsa = m2.pkey_read_pem(bio, callback) # the below code assumes RSA key while PKCS#8 files # (and by extension the EVP_PKEY structure) can be # also DSA or EC, thus the double check against None # (first if the file was properly loaded and second # if the file actually has a RSA key in it) # tlslite doesn't support DSA or EC so it's useless # to handle them in a different way if key.rsa == None: raise SyntaxError() key.rsa = m2.pkey_get1_rsa(key.rsa) if key.rsa == None: raise SyntaxError() key._hasPrivateKey = True elif s.startswith("-----BEGIN PUBLIC KEY-----"): key.rsa = m2.rsa_read_pub_key(bio) if key.rsa == None: raise SyntaxError() key._hasPrivateKey = False else: raise SyntaxError() if key._hasPrivateKey: b64_key = compat_b2a(key.write()) py_key = Python_RSAKey.parsePEM(b64_key) key.d = py_key.d return key finally: m2.bio_free(bio) else: raise SyntaxError()