def __init__(self, module): super(OwnCACertificateBackendCryptography, self).__init__(module, 'cryptography') self.create_subject_key_identifier = module.params['ownca_create_subject_key_identifier'] self.create_authority_key_identifier = module.params['ownca_create_authority_key_identifier'] self.notBefore = get_relative_time_option(module.params['ownca_not_before'], 'ownca_not_before', backend=self.backend) self.notAfter = get_relative_time_option(module.params['ownca_not_after'], 'ownca_not_after', backend=self.backend) self.digest = select_message_digest(module.params['ownca_digest']) self.version = module.params['ownca_version'] self.serial_number = x509.random_serial_number() self.ca_cert_path = module.params['ownca_path'] self.ca_cert_content = module.params['ownca_content'] if self.ca_cert_content is not None: self.ca_cert_content = self.ca_cert_content.encode('utf-8') self.ca_privatekey_path = module.params['ownca_privatekey_path'] self.ca_privatekey_content = module.params['ownca_privatekey_content'] if self.ca_privatekey_content is not None: self.ca_privatekey_content = self.ca_privatekey_content.encode('utf-8') self.ca_privatekey_passphrase = module.params['ownca_privatekey_passphrase'] if self.csr_content is None and self.csr_path is None: raise CertificateError( 'csr_path or csr_content is required for ownca provider' ) if self.csr_content is None and not os.path.exists(self.csr_path): raise CertificateError( 'The certificate signing request file {0} does not exist'.format(self.csr_path) ) if self.ca_cert_content is None and not os.path.exists(self.ca_cert_path): raise CertificateError( 'The CA certificate file {0} does not exist'.format(self.ca_cert_path) ) if self.ca_privatekey_content is None and not os.path.exists(self.ca_privatekey_path): raise CertificateError( 'The CA private key file {0} does not exist'.format(self.ca_privatekey_path) ) self._ensure_csr_loaded() self.ca_cert = load_certificate( path=self.ca_cert_path, content=self.ca_cert_content, backend=self.backend ) try: self.ca_private_key = load_privatekey( path=self.ca_privatekey_path, content=self.ca_privatekey_content, passphrase=self.ca_privatekey_passphrase, backend=self.backend ) except OpenSSLBadPassphraseError as exc: module.fail_json(msg=str(exc)) if cryptography_key_needs_digest_for_signing(self.ca_private_key): if self.digest is None: raise CertificateError( 'The digest %s is not supported with the cryptography backend' % module.params['ownca_digest'] ) else: self.digest = None
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 __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 __init__(self, module): super(SelfSignedCertificateBackendCryptography, self).__init__(module, 'cryptography') self.create_subject_key_identifier = module.params[ 'selfsigned_create_subject_key_identifier'] self.notBefore = get_relative_time_option( module.params['selfsigned_not_before'], 'selfsigned_not_before', backend=self.backend) self.notAfter = get_relative_time_option( module.params['selfsigned_not_after'], 'selfsigned_not_after', backend=self.backend) self.digest = select_message_digest(module.params['selfsigned_digest']) self.version = module.params['selfsigned_version'] self.serial_number = x509.random_serial_number() if self.csr_path is not None and not os.path.exists(self.csr_path): raise CertificateError( 'The certificate signing request file {0} does not exist'. format(self.csr_path)) if self.privatekey_content is None and not os.path.exists( self.privatekey_path): raise CertificateError( 'The private key file {0} does not exist'.format( self.privatekey_path)) self._module = module self._ensure_private_key_loaded() self._ensure_csr_loaded() if self.csr is None: # Create empty CSR on the fly csr = cryptography.x509.CertificateSigningRequestBuilder() csr = csr.subject_name(cryptography.x509.Name([])) digest = None if cryptography_key_needs_digest_for_signing(self.privatekey): digest = self.digest if digest is None: self.module.fail_json( msg='Unsupported digest "{0}"'.format( module.params['selfsigned_digest'])) try: self.csr = csr.sign(self.privatekey, digest, default_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 if cryptography_key_needs_digest_for_signing(self.privatekey): if self.digest is None: raise CertificateError( 'The digest %s is not supported with the cryptography backend' % module.params['selfsigned_digest']) else: self.digest = None