def _check_nameConstraints(extensions): current_nc_ext = _find_extension(extensions, cryptography.x509.NameConstraints) current_nc_perm = [ str(altname) for altname in current_nc_ext.value.permitted_subtrees ] if current_nc_ext else [] current_nc_excl = [ str(altname) for altname in current_nc_ext.value.excluded_subtrees ] if current_nc_ext else [] nc_perm = [ str( cryptography_get_name(altname, 'name constraints permitted')) for altname in self.name_constraints_permitted ] nc_excl = [ str(cryptography_get_name(altname, 'name constraints excluded')) for altname in self.name_constraints_excluded ] if set(nc_perm) != set(current_nc_perm) or set(nc_excl) != set( current_nc_excl): return False if nc_perm or nc_excl: if current_nc_ext.critical != self.name_constraints_critical: return False return True
def parse_crl_distribution_points(module, crl_distribution_points): result = [] for index, parse_crl_distribution_point in enumerate( crl_distribution_points): try: params = dict( full_name=None, relative_name=None, crl_issuer=None, reasons=None, ) if parse_crl_distribution_point['full_name'] is not None: params['full_name'] = [ cryptography_get_name(name, 'full name') for name in parse_crl_distribution_point['full_name'] ] if parse_crl_distribution_point['relative_name'] is not None: try: params[ 'relative_name'] = cryptography_parse_relative_distinguished_name( parse_crl_distribution_point['relative_name']) except Exception: # If cryptography's version is < 1.6, the error is probably caused by that if CRYPTOGRAPHY_VERSION < LooseVersion('1.6'): raise OpenSSLObjectError( 'Cannot specify relative_name for cryptography < 1.6' ) raise if parse_crl_distribution_point['crl_issuer'] is not None: params['crl_issuer'] = [ cryptography_get_name(name, 'CRL issuer') for name in parse_crl_distribution_point['crl_issuer'] ] if parse_crl_distribution_point['reasons'] is not None: reasons = [] for reason in parse_crl_distribution_point['reasons']: reasons.append(REVOCATION_REASON_MAP[reason]) params['reasons'] = frozenset(reasons) result.append(cryptography.x509.DistributionPoint(**params)) except OpenSSLObjectError as e: raise OpenSSLObjectError( 'Error while parsing CRL distribution point #{index}: {error}'. format(index=index, error=e)) return result
def _check_subjectAltName(extensions): current_altnames_ext = _find_extension(extensions, cryptography.x509.SubjectAlternativeName) current_altnames = [str(altname) for altname in current_altnames_ext.value] if current_altnames_ext else [] altnames = [str(cryptography_get_name(altname)) for altname in self.subjectAltName] if self.subjectAltName else [] if set(altnames) != set(current_altnames): return False if altnames: if current_altnames_ext.critical != self.subjectAltName_critical: return False return True
def _generate_crl(self): backend = default_backend() crl = CertificateRevocationListBuilder() try: crl = crl.issuer_name( Name([ NameAttribute(cryptography_name_to_oid(entry[0]), to_text(entry[1])) for entry in self.issuer ])) except ValueError as e: raise CRLError(e) crl = crl.last_update(self.last_update) crl = crl.next_update(self.next_update) if self.update and self.crl: new_entries = set([ self._compress_entry(entry) for entry in self.revoked_certificates ]) for entry in self.crl: decoded_entry = self._compress_entry( cryptography_decode_revoked_certificate(entry)) if decoded_entry not in new_entries: crl = crl.add_revoked_certificate(entry) for entry in self.revoked_certificates: revoked_cert = RevokedCertificateBuilder() revoked_cert = revoked_cert.serial_number(entry['serial_number']) revoked_cert = revoked_cert.revocation_date( entry['revocation_date']) if entry['issuer'] is not None: revoked_cert = revoked_cert.add_extension( x509.CertificateIssuer([ cryptography_get_name(name, 'issuer') for name in entry['issuer'] ]), entry['issuer_critical']) if entry['reason'] is not None: revoked_cert = revoked_cert.add_extension( x509.CRLReason(entry['reason']), entry['reason_critical']) if entry['invalidity_date'] is not None: revoked_cert = revoked_cert.add_extension( x509.InvalidityDate(entry['invalidity_date']), entry['invalidity_date_critical']) crl = crl.add_revoked_certificate(revoked_cert.build(backend)) self.crl = crl.sign(self.privatekey, self.digest, backend=backend) if self.format == 'pem': return self.crl.public_bytes(Encoding.PEM) else: return self.crl.public_bytes(Encoding.DER)
def _validate_subject_alt_name(self): try: current_san = self.existing_certificate.extensions.get_extension_for_class( x509.SubjectAlternativeName).value expected_san = [ cryptography_get_name(san) for san in self.subject_alt_name ] if not compare_sets(expected_san, current_san, self.subject_alt_name_strict): return self.subject_alt_name, current_san except cryptography.x509.ExtensionNotFound: # This is only bad if the user specified a non-empty list if self.subject_alt_name: return NO_EXTENSION
def _check_authority_key_identifier(extensions): ext = _find_extension(extensions, cryptography.x509.AuthorityKeyIdentifier) if self.authority_key_identifier is not None or self.authority_cert_issuer is not None or self.authority_cert_serial_number is not None: if not ext or ext.critical: return False aci = None csr_aci = None if self.authority_cert_issuer is not None: aci = [str(cryptography_get_name(n)) for n in self.authority_cert_issuer] if ext.value.authority_cert_issuer is not None: csr_aci = [str(n) for n in ext.value.authority_cert_issuer] return (ext.value.key_identifier == self.authority_key_identifier and csr_aci == aci and ext.value.authority_cert_serial_number == self.authority_cert_serial_number) else: return ext is None
def __init__(self, module): super(CRL, self).__init__( module.params['path'], module.params['state'], module.params['force'], module.check_mode ) self.format = module.params['format'] self.update = module.params['mode'] == 'update' self.ignore_timestamps = module.params['ignore_timestamps'] self.return_content = module.params['return_content'] self.crl_content = None self.privatekey_path = module.params['privatekey_path'] self.privatekey_content = module.params['privatekey_content'] if self.privatekey_content is not None: self.privatekey_content = self.privatekey_content.encode('utf-8') self.privatekey_passphrase = module.params['privatekey_passphrase'] self.issuer = parse_name_field(module.params['issuer']) self.issuer = [(entry[0], entry[1]) for entry in self.issuer if entry[1]] self.last_update = get_relative_time_option(module.params['last_update'], 'last_update') self.next_update = get_relative_time_option(module.params['next_update'], 'next_update') self.digest = select_message_digest(module.params['digest']) if self.digest is None: raise CRLError('The digest "{0}" is not supported'.format(module.params['digest'])) self.revoked_certificates = [] for i, rc in enumerate(module.params['revoked_certificates']): result = { 'serial_number': None, 'revocation_date': None, 'issuer': None, 'issuer_critical': False, 'reason': None, 'reason_critical': False, 'invalidity_date': None, 'invalidity_date_critical': False, } path_prefix = 'revoked_certificates[{0}].'.format(i) if rc['path'] is not None or rc['content'] is not None: # Load certificate from file or content try: if rc['content'] is not None: rc['content'] = rc['content'].encode('utf-8') cert = load_certificate(rc['path'], content=rc['content'], backend='cryptography') result['serial_number'] = cryptography_serial_number_of_cert(cert) except OpenSSLObjectError as e: if rc['content'] is not None: module.fail_json( msg='Cannot parse certificate from {0}content: {1}'.format(path_prefix, to_native(e)) ) else: module.fail_json( msg='Cannot read certificate "{1}" from {0}path: {2}'.format(path_prefix, rc['path'], to_native(e)) ) else: # Specify serial_number (and potentially issuer) directly result['serial_number'] = rc['serial_number'] # All other options if rc['issuer']: result['issuer'] = [cryptography_get_name(issuer) for issuer in rc['issuer']] result['issuer_critical'] = rc['issuer_critical'] result['revocation_date'] = get_relative_time_option( rc['revocation_date'], path_prefix + 'revocation_date' ) if rc['reason']: result['reason'] = REVOCATION_REASON_MAP[rc['reason']] result['reason_critical'] = rc['reason_critical'] if rc['invalidity_date']: result['invalidity_date'] = get_relative_time_option( rc['invalidity_date'], path_prefix + 'invalidity_date' ) result['invalidity_date_critical'] = rc['invalidity_date_critical'] self.revoked_certificates.append(result) self.module = module self.backup = module.params['backup'] self.backup_file = None try: self.privatekey = load_privatekey( path=self.privatekey_path, content=self.privatekey_content, passphrase=self.privatekey_passphrase, backend='cryptography' ) except OpenSSLBadPassphraseError as exc: raise CRLError(exc) self.crl = None try: with open(self.path, 'rb') as f: data = f.read() self.actual_format = 'pem' if identify_pem_format(data) else 'der' if self.actual_format == 'pem': self.crl = x509.load_pem_x509_crl(data, default_backend()) if self.return_content: self.crl_content = data else: self.crl = x509.load_der_x509_crl(data, default_backend()) if self.return_content: self.crl_content = base64.b64encode(data) except Exception as dummy: self.crl_content = None self.actual_format = self.format
def generate_csr(self): """(Re-)Generate CSR.""" self._ensure_private_key_loaded() csr = cryptography.x509.CertificateSigningRequestBuilder() try: csr = csr.subject_name( cryptography.x509.Name([ cryptography.x509.NameAttribute( cryptography_name_to_oid(entry[0]), to_text(entry[1])) for entry in self.subject ])) except ValueError as e: raise CertificateSigningRequestError(e) if self.subjectAltName: csr = csr.add_extension(cryptography.x509.SubjectAlternativeName( [cryptography_get_name(name) for name in self.subjectAltName]), critical=self.subjectAltName_critical) if self.keyUsage: params = cryptography_parse_key_usage_params(self.keyUsage) csr = csr.add_extension(cryptography.x509.KeyUsage(**params), critical=self.keyUsage_critical) if self.extendedKeyUsage: usages = [ cryptography_name_to_oid(usage) for usage in self.extendedKeyUsage ] csr = csr.add_extension(cryptography.x509.ExtendedKeyUsage(usages), critical=self.extendedKeyUsage_critical) if self.basicConstraints: params = {} ca, path_length = cryptography_get_basic_constraints( self.basicConstraints) csr = csr.add_extension(cryptography.x509.BasicConstraints( ca, path_length), critical=self.basicConstraints_critical) if self.ocspMustStaple: try: # This only works with cryptography >= 2.1 csr = csr.add_extension(cryptography.x509.TLSFeature( [cryptography.x509.TLSFeatureType.status_request]), critical=self.ocspMustStaple_critical) except AttributeError as dummy: csr = csr.add_extension( cryptography.x509.UnrecognizedExtension( CRYPTOGRAPHY_MUST_STAPLE_NAME, CRYPTOGRAPHY_MUST_STAPLE_VALUE), critical=self.ocspMustStaple_critical) if self.name_constraints_permitted or self.name_constraints_excluded: try: csr = csr.add_extension( cryptography.x509.NameConstraints( [ cryptography_get_name( name, 'name constraints permitted') for name in self.name_constraints_permitted ], [ cryptography_get_name(name, 'name constraints excluded') for name in self.name_constraints_excluded ], ), critical=self.name_constraints_critical) except TypeError as e: raise OpenSSLObjectError( 'Error while parsing name constraint: {0}'.format(e)) if self.create_subject_key_identifier: csr = csr.add_extension( cryptography.x509.SubjectKeyIdentifier.from_public_key( self.privatekey.public_key()), critical=False) elif self.subject_key_identifier is not None: csr = csr.add_extension(cryptography.x509.SubjectKeyIdentifier( self.subject_key_identifier), critical=False) if self.authority_key_identifier is not None or self.authority_cert_issuer is not None or self.authority_cert_serial_number is not None: issuers = None if self.authority_cert_issuer is not None: issuers = [ cryptography_get_name(n, 'authority cert issuer') for n in self.authority_cert_issuer ] csr = csr.add_extension(cryptography.x509.AuthorityKeyIdentifier( self.authority_key_identifier, issuers, self.authority_cert_serial_number), critical=False) if self.crl_distribution_points: csr = csr.add_extension(cryptography.x509.CRLDistributionPoints( self.crl_distribution_points), critical=False) digest = None if cryptography_key_needs_digest_for_signing(self.privatekey): digest = select_message_digest(self.digest) if digest is None: raise CertificateSigningRequestError( 'Unsupported digest "{0}"'.format(self.digest)) try: self.csr = csr.sign(self.privatekey, digest, self.cryptography_backend) except TypeError as e: if str( e ) == 'Algorithm must be a registered hash algorithm.' and digest is None: self.module.fail_json( msg= 'Signing with Ed25519 and Ed448 keys requires cryptography 2.8 or newer.' ) raise except UnicodeError as e: # This catches IDNAErrors, which happens when a bad name is passed as a SAN # (https://github.com/ansible-collections/community.crypto/issues/105). # For older cryptography versions, this is handled by idna, which raises # an idna.core.IDNAError. Later versions of cryptography deprecated and stopped # requiring idna, whence we cannot easily handle this error. Fortunately, in # most versions of idna, IDNAError extends UnicodeError. There is only version # 2.3 where it extends Exception instead (see # https://github.com/kjd/idna/commit/ebefacd3134d0f5da4745878620a6a1cba86d130 # and then # https://github.com/kjd/idna/commit/ea03c7b5db7d2a99af082e0239da2b68aeea702a). msg = 'Error while creating CSR: {0}\n'.format(e) if self.using_common_name_for_san: self.module.fail_json( msg=msg + 'This is probably caused because the Common Name is used as a SAN.' ' Specifying use_common_name_for_san=false might fix this.' ) self.module.fail_json( msg=msg + 'This is probably caused by an invalid Subject Alternative DNS Name.' )
def test_cryptography_get_name_other_name_utfstring(): actual = cryptography_get_name( 'otherName:1.3.6.1.4.1.311.20.2.3;UTF8:Hello World') assert actual.type_id.dotted_string == '1.3.6.1.4.1.311.20.2.3' assert actual.value == b'\x0c\x0bHello World'
def test_cryptography_get_name_other_name_no_oid(): with pytest.raises( OpenSSLObjectError, match="Cannot parse Subject Alternative Name otherName"): cryptography_get_name('otherName:value')
def test_cryptography_get_name_invalid_prefix(): with pytest.raises(OpenSSLObjectError, match="Cannot parse Subject Alternative Name"): cryptography_get_name('fake:value')