def load_dh_params_from_string(ctx, dh_params_string): bio = _new_mem_buf() _lib.BIO_write(bio, dh_params_string.encode('ascii'), len(dh_params_string.encode('ascii'))) # pylint: disable=no-member dh = _lib.PEM_read_bio_DHparams(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL) # pylint: disable=no-member dh = _ffi.gc(dh, _lib.DH_free) # pylint: disable=no-member _lib.SSL_CTX_set_tmp_dh(ctx._context, dh) # pylint: disable=no-member
def __init__(self, key, certificate, intermediate): """ :param key: String representation of the private key :param certificate: String representation of the certificate :param intermediate: String representation of the intermediate file :param dh: String representation of the DH parameters """ self.ctx = new_tls_server_context() x509 = load_certificate(FILETYPE_PEM, certificate) self.ctx.use_certificate(x509) if intermediate: x509 = load_certificate(FILETYPE_PEM, intermediate) self.ctx.add_extra_chain_cert(x509) key = load_privatekey(FILETYPE_PEM, key) self.ctx.use_privatekey(key) # If SSL_CTX_set_ecdh_auto is available then set it so the ECDH curve # will be auto-selected. This function was added in 1.0.2 and made a # noop in 1.1.0+ (where it is set automatically). try: _lib.SSL_CTX_set_ecdh_auto(self.ctx._context, 1) # pylint: disable=no-member except AttributeError: ecdh = _lib.EC_KEY_new_by_curve_name(_lib.NID_X9_62_prime256v1) # pylint: disable=no-member ecdh = _ffi.gc(ecdh, _lib.EC_KEY_free) # pylint: disable=no-member _lib.SSL_CTX_set_tmp_ecdh(self.ctx._context, ecdh) # pylint: disable=no-member
def load_publickey(type, buffer): """ Load a public key from a buffer. :param type: The file type (one of :data:`FILETYPE_PEM`,\ :data:`FILETYPE_ASN1`). :param buffer: The buffer the key is stored in. :type buffer: A Python string object, either unicode or bytestring. :return: The PKey object. :rtype: :class:`PKey` COPIED AS IS FROM PYOPENSSL (because <0.16 does not have the method) """ if isinstance(buffer, _text_type): buffer = buffer.encode("ascii") bio = _new_mem_buf(buffer) if type == FILETYPE_PEM: evp_pkey = _lib.PEM_read_bio_PUBKEY(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL) elif type == FILETYPE_ASN1: evp_pkey = _lib.d2i_PUBKEY_bio(bio, _ffi.NULL) else: raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1") if evp_pkey == _ffi.NULL: _raise_current_error() pkey = PKey.__new__(PKey) pkey._pkey = _ffi.gc(evp_pkey, _lib.EVP_PKEY_free) return pkey
def get_pkcs7_certificates(bundle): """ Extracts X.509 certificates from an OpenSSL PKCS7 object. Args: bundle (OpenSSL PKCS7 object) : PKCS7 object to extract the certificates from. Returns: A tuple containing the extracted certificates (cryptography X.509 certificates, not OpenSSL X.509 certificates!) """ from OpenSSL._util import (ffi as _ffi, lib as _lib) from OpenSSL.crypto import X509 pkcs7_certs = _ffi.NULL if bundle.type_is_signed(): pkcs7_certs = bundle._pkcs7.d.sign.cert elif bundle.type_is_signedAndEnveloped(): pkcs7_certs = bundle._pkcs7.d.signed_and_enveloped.cert certificates = [] for i in range(_lib.sk_X509_num(pkcs7_certs)): certificate = X509.__new__(X509) certificate._x509 = _ffi.gc( _lib.X509_dup(_lib.sk_X509_value(pkcs7_certs, i)), _lib.X509_free) certificates.append(certificate.to_cryptography()) if not certificates: return tuple() return tuple(certificates)
def get_client_ca_list(self): """ Get CAs whose certificates are suggested for client authentication. :return: If this is a server connection, a list of X509Names representing the acceptable CAs as set by :py:meth:`OpenSSL.SSL.Context.set_client_ca_list` or :py:meth:`OpenSSL.SSL.Context.add_client_ca`. If this is a client connection, the list of such X509Names sent by the server, or an empty list if that has not yet happened. """ ca_names = _lib.SSL_get_client_CA_list(self._ssl) if ca_names == _ffi.NULL: # TODO: This is untested. return [] result = [] for i in range(_lib.sk_X509_NAME_num(ca_names)): name = _lib.sk_X509_NAME_value(ca_names, i) copy = _lib.X509_NAME_dup(name) if copy == _ffi.NULL: # TODO: This is untested. _raise_current_error() pyname = X509Name.__new__(X509Name) pyname._name = _ffi.gc(copy, _lib.X509_NAME_free) result.append(pyname) return result
def load_dh_params_from_string(ctx, dh_params_string): bio = _new_mem_buf() _lib.BIO_write(bio, str(dh_params_string), len(str(dh_params_string))) dh = _lib.PEM_read_bio_DHparams(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL) dh = _ffi.gc(dh, _lib.DH_free) _lib.SSL_CTX_set_tmp_dh(ctx._context, dh)
def load_tmp_dh(self, dhfile): """ Load parameters for Ephemeral Diffie-Hellman :param dhfile: The file to load EDH parameters from :return: None """ if not isinstance(dhfile, bytes): raise TypeError("dhfile must be a byte string") bio = _lib.BIO_new_file(dhfile, b"r") if bio == _ffi.NULL: _raise_current_error() bio = _ffi.gc(bio, _lib.BIO_free) dh = _lib.PEM_read_bio_DHparams(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL) dh = _ffi.gc(dh, _lib.DH_free) _lib.SSL_CTX_set_tmp_dh(self._context, dh)
def wrapper(b64, lib=lib): if isinstance(b64, str): b64 = b64.encode('ascii') b64_ptr = _sslffi.new('char[]', b64) spki_obj = lib.NETSCAPE_SPKI_b64_decode(b64_ptr, len(b64)) if spki_obj == cffi.NULL: raise ValueError("Invalid SPKI base64") def free(spki_obj, ref=b64_ptr): _ssllib.NETSCAPE_SPKI_free(spki_obj) return _sslffi.gc(spki_obj, free)
def get_peer_certificate(self): """ Retrieve the other side's certificate (if any) :return: The peer's certificate """ cert = _lib.SSL_get_peer_certificate(self._ssl) if cert != _ffi.NULL: pycert = X509.__new__(X509) pycert._x509 = _ffi.gc(cert, _lib.X509_free) return pycert return None
def load_tmp_dh(self, dhfile): """ Function overridden in order to enforce ECDH/PFS """ from OpenSSL._util import (ffi as _ffi, lib as _lib) if not isinstance(dhfile, bytes): raise TypeError("dhfile must be a byte string") bio = _lib.BIO_new_file(dhfile, b"r") if bio == _ffi.NULL: _raise_current_error() bio = _ffi.gc(bio, _lib.BIO_free) dh = _lib.PEM_read_bio_DHparams(bio, _ffi.NULL, _ffi.NULL, _ffi.NULL) dh = _ffi.gc(dh, _lib.DH_free) _lib.SSL_CTX_set_tmp_dh(self._context, dh) ecdh = _lib.EC_KEY_new_by_curve_name(_lib.NID_X9_62_prime256v1) ecdh = _ffi.gc(ecdh, _lib.EC_KEY_free) _lib.SSL_CTX_set_tmp_ecdh(self._context, ecdh)
def get_csr(client, key_id, key_version_id, subject_name, domain, kms_algorithm, signature_algorithm): clear_text_public_key_pem = get_public_key(client, key_id, key_version_id) # kms get_public_key pkey = load_publickey(FILETYPE_PEM, clear_text_public_key_pem) req = X509Req() req.set_pubkey(pkey) req.get_subject().CN = subject_name.get('CN') req.get_subject().C = subject_name.get('C') req.get_subject().O = subject_name.get('O') req.set_version(0) # addExtensions req.add_extensions([ X509Extension(b'subjectAltName', False, ','.join(domain).encode('ascii')) ]) result_buffer = _ffi.new('unsigned char**') encode_result = _lib.i2d_re_X509_REQ_tbs(req._req, result_buffer) md_length = _ffi.new("unsigned int *") md = _ffi.new("unsigned char[]", 32) evp_md = _lib.EVP_get_digestbyname(b"sha256") md_ctx = _lib.Cryptography_EVP_MD_CTX_new() md_ctx = _ffi.gc(md_ctx, _lib.Cryptography_EVP_MD_CTX_free) _lib.EVP_DigestInit_ex(md_ctx, evp_md, _ffi.NULL) _lib.EVP_DigestUpdate(md_ctx, result_buffer[0], encode_result) _lib.EVP_DigestFinal_ex(md_ctx, md, md_length) psig = _ffi.new("ASN1_BIT_STRING **") palg = _ffi.new("X509_ALGOR **") _lib.X509_REQ_get0_signature(req._req, psig, palg) # kms_sign sign_data = kms_sign(client, key_id, key_version_id, kms_algorithm, bytes(md)) _lib.ASN1_STRING_set(psig[0], sign_data, len(sign_data)) psig[0].flags &= ~(0x08 | 0x07) psig[0].flags |= 0x08 _lib.OPENSSL_free(result_buffer[0]) palg[0].algorithm = _lib.OBJ_nid2obj(_lib.OBJ_sn2nid(signature_algorithm)) csr_pem_str = dump_certificate_request(FILETYPE_PEM, req) return csr_pem_str
def get_session(self): """ Returns the Session currently used. @return: An instance of :py:class:`OpenSSL.SSL.Session` or :py:obj:`None` if no session exists. """ session = _lib.SSL_get1_session(self._ssl) if session == _ffi.NULL: return None pysession = Session.__new__(Session) pysession._session = _ffi.gc(session, _lib.SSL_SESSION_free) return pysession
def b64_decode(cls, spkac_str): """ Construct a NetscapeSPKI from spkac base64 string :param spkac_str: base64 encoded string """ new = cls() arg = _ffi.new('char[]', spkac_str) spki = _lib.NETSCAPE_SPKI_b64_decode(arg, -1) if spki == _ffi.NULL: raise ValueError('Invalid SPKAC string') new._spki = _ffi.gc(spki, _lib.NETSCAPE_SPKI_free) return new
def __init__(self, method): """ :param method: One of SSLv2_METHOD, SSLv3_METHOD, SSLv23_METHOD, or TLSv1_METHOD. """ if not isinstance(method, integer_types): raise TypeError("method must be an integer") try: method_func = self._methods[method] except KeyError: raise ValueError("No such protocol") method_obj = method_func() if method_obj == _ffi.NULL: # TODO: This is untested. _raise_current_error() context = _lib.SSL_CTX_new(method_obj) if context == _ffi.NULL: # TODO: This is untested. _raise_current_error() context = _ffi.gc(context, _lib.SSL_CTX_free) self._context = context self._passphrase_helper = None self._passphrase_callback = None self._passphrase_userdata = None self._verify_helper = None self._verify_callback = None self._info_callback = None self._tlsext_servername_callback = None self._app_data = None self._npn_advertise_callback = None self._npn_advertise_callback_args = None self._npn_select_callback = None self._npn_select_callback_args = None # SSL_CTX_set_app_data(self->ctx, self); # SSL_CTX_set_mode(self->ctx, SSL_MODE_ENABLE_PARTIAL_WRITE | # SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER | # SSL_MODE_AUTO_RETRY); self.set_mode(_lib.SSL_MODE_ENABLE_PARTIAL_WRITE)
def get_peer_cert_chain(self): """ Retrieve the other side's certificate (if any) :return: A list of X509 instances giving the peer's certificate chain, or None if it does not have one. """ cert_stack = _lib.SSL_get_peer_cert_chain(self._ssl) if cert_stack == _ffi.NULL: return None result = [] for i in range(_lib.sk_X509_num(cert_stack)): # TODO could incref instead of dup here cert = _lib.X509_dup(_lib.sk_X509_value(cert_stack, i)) pycert = X509.__new__(X509) pycert._x509 = _ffi.gc(cert, _lib.X509_free) result.append(pycert) return result
def cacheContext(self): if self._context is None: ctx = SSL.Context(SSL.SSLv23_METHOD) ctx.set_options(SSL.OP_CIPHER_SERVER_PREFERENCE | SSL.OP_NO_SSLv2 | SSL.OP_NO_SSLv3 | SSL.OP_SINGLE_DH_USE | SSL.OP_NO_COMPRESSION | SSL.OP_NO_TICKET) ctx.set_mode(SSL.MODE_RELEASE_BUFFERS) first = True if os.path.isfile(self.certificateFilePath): with open(self.certificateFilePath, 'r') as f: first = False x509 = load_certificate(FILETYPE_PEM, f.read()) ctx.use_certificate(x509) if self.intermediateFilePath != self.certificateFilePath and \ os.path.isfile(self.intermediateFilePath): if first: ctx.use_certificate_chain_file(self.intermediateFilePath) else: with open(self.intermediateFilePath, 'r') as f: x509 = load_certificate(FILETYPE_PEM, f.read()) ctx.add_extra_chain_cert(x509) ctx.use_privatekey_file(self.privateKeyFilePath) ctx.set_cipher_list(self.cipherList) # If SSL_CTX_set_ecdh_auto is available then set it so the ECDH curve # will be auto-selected. This function was added in 1.0.2 and made a # noop in 1.1.0+ (where it is set automatically). try: _lib.SSL_CTX_set_ecdh_auto(ctx._context, 1) # pylint: disable=no-member except AttributeError: ecdh = _lib.EC_KEY_new_by_curve_name(_lib.NID_X9_62_prime256v1) # pylint: disable=no-member ecdh = _ffi.gc(ecdh, _lib.EC_KEY_free) # pylint: disable=no-member _lib.SSL_CTX_set_tmp_ecdh(ctx._context, ecdh) # pylint: disable=no-member self._context = ctx
def cacheContext(self): if self._context is None: ctx = SSL.Context(self.sslmethod) ctx.set_options(SSL.OP_CIPHER_SERVER_PREFERENCE | SSL.OP_NO_SSLv2 | SSL.OP_NO_SSLv3 | SSL.OP_SINGLE_DH_USE | SSL.OP_NO_COMPRESSION | SSL.OP_NO_TICKET) ctx.set_mode(SSL.MODE_RELEASE_BUFFERS) first = True if os.path.isfile(self.certificateFilePath): with open(self.certificateFilePath, 'r') as f: first = False x509 = load_certificate(FILETYPE_PEM, f.read()) ctx.use_certificate(x509) if self.intermediateFilePath != self.certificateFilePath and \ os.path.isfile(self.intermediateFilePath): if first: ctx.use_certificate_chain_file(self.intermediateFilePath) else: with open(self.intermediateFilePath, 'r') as f: x509 = load_certificate(FILETYPE_PEM, f.read()) ctx.add_extra_chain_cert(x509) ctx.use_privatekey_file(self.privateKeyFilePath) ctx.set_cipher_list(self.cipherList) ctx.load_tmp_dh(self.dhFilePath) ecdh = _lib.EC_KEY_new_by_curve_name(_lib.NID_X9_62_prime256v1) ecdh = _ffi.gc(ecdh, _lib.EC_KEY_free) _lib.SSL_CTX_set_tmp_ecdh(ctx._context, ecdh) self._context = ctx
def check(self): """ Check the consistency of an RSA private key. This is the Python equivalent of OpenSSL's ``RSA_check_key``. :return: True if key is consistent. :raise Error: if the key is inconsistent. :raise TypeError: if the key is of a type which cannot be checked.\ Only RSA keys can currently be checked. """ if self._only_public: raise TypeError("public key only") if _lib.EVP_PKEY_type(self.type()) != _lib.EVP_PKEY_RSA: raise TypeError("key type unsupported") rsa = _lib.EVP_PKEY_get1_RSA(self._pkey) rsa = _ffi.gc(rsa, _lib.RSA_free) result = _lib.RSA_check_key(rsa) if result: return True _raise_current_error()
def __init__(self, method): """ :param method: One of SSLv2_METHOD, SSLv3_METHOD, SSLv23_METHOD, or TLSv1_METHOD. """ if not isinstance(method, integer_types): raise TypeError("method must be an integer") try: method_func = self._methods[method] except KeyError: raise ValueError("No such protocol") method_obj = method_func() if method_obj == _ffi.NULL: # TODO: This is untested. _raise_current_error() context = _lib.SSL_CTX_new(method_obj) if context == _ffi.NULL: # TODO: This is untested. _raise_current_error() context = _ffi.gc(context, _lib.SSL_CTX_free) self._context = context self._passphrase_helper = None self._passphrase_callback = None self._passphrase_userdata = None self._verify_helper = None self._verify_callback = None self._info_callback = None self._tlsext_servername_callback = None self._app_data = None # SSL_CTX_set_app_data(self->ctx, self); # SSL_CTX_set_mode(self->ctx, SSL_MODE_ENABLE_PARTIAL_WRITE | # SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER | # SSL_MODE_AUTO_RETRY); self.set_mode(_lib.SSL_MODE_ENABLE_PARTIAL_WRITE)
def __init__(self, context, socket=None): """ Create a new Connection object, using the given OpenSSL.SSL.Context instance and socket. :param context: An SSL Context to use for this connection :param socket: The socket to use for transport layer """ if not isinstance(context, Context): raise TypeError("context must be a Context instance") ssl = _lib.SSL_new(context._context) self._ssl = _ffi.gc(ssl, _lib.SSL_free) self._context = context self._reverse_mapping[self._ssl] = self if socket is None: self._socket = None # Don't set up any gc for these, SSL_free will take care of them. self._into_ssl = _lib.BIO_new(_lib.BIO_s_mem()) self._from_ssl = _lib.BIO_new(_lib.BIO_s_mem()) if self._into_ssl == _ffi.NULL or self._from_ssl == _ffi.NULL: # TODO: This is untested. _raise_current_error() _lib.SSL_set_bio(self._ssl, self._into_ssl, self._from_ssl) else: self._into_ssl = None self._from_ssl = None self._socket = socket set_result = _lib.SSL_set_fd(self._ssl, _asFileDescriptor(self._socket)) if not set_result: # TODO: This is untested. _raise_current_error()
def __init__(self, priv_key, certificate, intermediate, dh): """ @param priv_key: String representation of the private key @param certificate: String representation of the certificate @param intermediate: String representation of the intermediate file @param dh: String representation of the DH parameters """ self.ctx = new_tls_server_context() x509 = load_certificate(FILETYPE_PEM, certificate) self.ctx.use_certificate(x509) if intermediate: x509 = load_certificate(FILETYPE_PEM, intermediate) self.ctx.add_extra_chain_cert(x509) priv_key = load_privatekey(FILETYPE_PEM, priv_key) self.ctx.use_privatekey(priv_key) load_dh_params_from_string(self.ctx, dh) ecdh = _lib.EC_KEY_new_by_curve_name(_lib.NID_X9_62_prime256v1) # pylint: disable=no-member ecdh = _ffi.gc(ecdh, _lib.EC_KEY_free) # pylint: disable=no-member _lib.SSL_CTX_set_tmp_ecdh(self.ctx._context, ecdh) # pylint: disable=no-member
def _new_mem_buf(buffer=None): """ Allocate a new OpenSSL memory BIO. Arrange for the garbage collector to clean it up automatically. :param buffer: None or some bytes to use to put into the BIO so that they can be read out. """ if buffer is None: bio = _lib.BIO_new(_lib.BIO_s_mem()) free = _lib.BIO_free else: data = _ffi.new("char[]", buffer) bio = _lib.BIO_new_mem_buf(data, len(buffer)) # Keep the memory alive as long as the bio is alive! def free(bio, ref=data): return _lib.BIO_free(bio) if bio == _ffi.NULL: # TODO: This is untested. _raise_current_error() bio = _ffi.gc(bio, free) return bio
def __init__(self, spkac_str=None): if spkac_str is not None: self._spki = NetscapeSPKINew.b64_decode(spkac_str)._spki else: spki = _lib.NETSCAPE_SPKI_new() self._spki = _ffi.gc(spki, _lib.NETSCAPE_SPKI_free)
def generate_key(self, type, bits): """ Generate a key pair of the given type, with the given number of bits. This generates a key "into" the this object. :param type: The key type. :type type: :py:data:`TYPE_RSA` or :py:data:`TYPE_DSA` :param bits: The number of bits. :type bits: :py:data:`int` ``>= 0`` :raises TypeError: If :py:data:`type` or :py:data:`bits` isn't\ of the appropriate type. :raises ValueError: If the number of bits isn't an integer of\ the appropriate size. :return: :py:const:`None` """ if not isinstance(type, int): raise TypeError("type must be an integer") if not isinstance(bits, int): raise TypeError("bits must be an integer") # TODO Check error return exponent = _lib.BN_new() exponent = _ffi.gc(exponent, _lib.BN_free) _lib.BN_set_word(exponent, _lib.RSA_F4) if type == TYPE_RSA: if bits <= 0: raise ValueError("Invalid number of bits") rsa = _lib.RSA_new() result = _lib.RSA_generate_key_ex(rsa, bits, exponent, _ffi.NULL) if result == 0: # TODO: The test for this case is commented out. Different # builds of OpenSSL appear to have different failure modes that # make it hard to test. Visual inspection of the OpenSSL # source reveals that a return value of 0 signals an error. # Manual testing on a particular build of OpenSSL suggests that # this is probably the appropriate way to handle those errors. _raise_current_error() result = _lib.EVP_PKEY_assign_RSA(self._pkey, rsa) if not result: # TODO: It appears as though this can fail if an engine is in # use which does not support RSA. _raise_current_error() elif type == TYPE_DSA: dsa = _lib.DSA_new() if dsa == _ffi.NULL: # TODO: This is untested. _raise_current_error() dsa = _ffi.gc(dsa, _lib.DSA_free) res = _lib.DSA_generate_parameters_ex(dsa, bits, _ffi.NULL, 0, _ffi.NULL, _ffi.NULL, _ffi.NULL) if not res == 1: # TODO: This is untested. _raise_current_error() if not _lib.DSA_generate_key(dsa): # TODO: This is untested. _raise_current_error() if not _lib.EVP_PKEY_set1_DSA(self._pkey, dsa): # TODO: This is untested. _raise_current_error() else: raise Error("No such key type") self._initialized = True
def __init__(self): pkey = _lib.EVP_PKEY_new() self._pkey = _ffi.gc(pkey, _lib.EVP_PKEY_free) self._initialized = False