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 __init__(self, module): super(OwnCACertificateBackendPyOpenSSL, self).__init__(module, 'pyopenssl') self.notBefore = get_relative_time_option(self.module.params['ownca_not_before'], 'ownca_not_before', backend=self.backend) self.notAfter = get_relative_time_option(self.module.params['ownca_not_after'], 'ownca_not_after', backend=self.backend) self.digest = self.module.params['ownca_digest'] self.version = self.module.params['ownca_version'] self.serial_number = generate_serial_number() if self.module.params['ownca_create_subject_key_identifier'] != 'create_if_not_provided': self.module.fail_json(msg='ownca_create_subject_key_identifier cannot be used with the pyOpenSSL backend!') if self.module.params['ownca_create_authority_key_identifier']: self.module.warn('ownca_create_authority_key_identifier is ignored by the pyOpenSSL backend!') self.ca_cert_path = self.module.params['ownca_path'] self.ca_cert_content = self.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 = self.module.params['ownca_privatekey_path'] self.ca_privatekey_content = self.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 = self.module.params['ownca_privatekey_passphrase'] 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, ) try: self.ca_privatekey = load_privatekey( path=self.ca_privatekey_path, content=self.ca_privatekey_content, passphrase=self.ca_privatekey_passphrase ) except OpenSSLBadPassphraseError as exc: self.module.fail_json(msg=str(exc))
def _validate_valid_in(self): valid_in_asn1 = get_relative_time_option(self.valid_in, "valid_in", backend=self.backend) valid_in_date = to_bytes(valid_in_asn1, errors='surrogate_or_strict') return self.existing_certificate.get_notBefore( ), valid_in_date, self.existing_certificate.get_notAfter()
def _validate_invalid_at(self): rt = get_relative_time_option(self.invalid_at, "invalid_at", backend=self.backend) rt = to_bytes(rt, errors='surrogate_or_strict') return self.existing_certificate.get_notBefore( ), rt, self.existing_certificate.get_notAfter()
def main(): module = AnsibleModule( argument_spec=dict( path=dict(type='path'), content=dict(type='str'), valid_at=dict(type='dict'), select_crypto_backend=dict( type='str', default='auto', choices=['auto', 'cryptography', 'pyopenssl']), ), required_one_of=(['path', 'content'], ), mutually_exclusive=(['path', 'content'], ), supports_check_mode=True, ) if module._name == 'community.crypto.openssl_certificate_info': module.deprecate( "The 'community.crypto.openssl_certificate_info' module has been renamed to 'community.crypto.x509_certificate_info'", version='2.0.0', collection_name='community.crypto') if module.params['content'] is not None: data = module.params['content'].encode('utf-8') else: try: with open(module.params['path'], 'rb') as f: data = f.read() except (IOError, OSError) as e: module.fail_json( msg='Error while reading certificate file from disk: {0}'. format(e)) backend, module_backend = select_backend( module, module.params['select_crypto_backend'], data) valid_at = module.params['valid_at'] if valid_at: for k, v in valid_at.items(): if not isinstance(v, string_types): module.fail_json( msg= 'The value for valid_at.{0} must be of type string (got {1})' .format(k, type(v))) valid_at[k] = get_relative_time_option(v, 'valid_at.{0}'.format(k)) try: result = module_backend.get_info() not_before = module_backend.get_not_before() not_after = module_backend.get_not_after() result['valid_at'] = dict() if valid_at: for k, v in valid_at.items(): result['valid_at'][k] = not_before <= v <= not_after module.exit_json(**result) except OpenSSLObjectError as exc: module.fail_json(msg=to_native(exc))
def __init__(self, module, backend): super(EntrustCertificateBackend, self).__init__(module, backend) self.trackingId = None self.notAfter = get_relative_time_option(module.params['entrust_not_after'], 'entrust_not_after', backend=self.backend) if self.csr_content is None and self.csr_path is None: raise CertificateError( 'csr_path or csr_content is required for entrust 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) ) self._ensure_csr_loaded() # ECS API defaults to using the validated organization tied to the account. # We want to always force behavior of trying to use the organization provided in the CSR. # To that end we need to parse out the organization from the CSR. self.csr_org = None if self.backend == 'pyopenssl': csr_subject = self.csr.get_subject() csr_subject_components = csr_subject.get_components() for k, v in csr_subject_components: if k.upper() == 'O': # Entrust does not support multiple validated organizations in a single certificate if self.csr_org is not None: self.module.fail_json(msg=("Entrust provider does not currently support multiple validated organizations. Multiple organizations " "found in Subject DN: '{0}'. ".format(csr_subject))) else: self.csr_org = v elif self.backend == 'cryptography': csr_subject_orgs = self.csr.subject.get_attributes_for_oid(NameOID.ORGANIZATION_NAME) if len(csr_subject_orgs) == 1: self.csr_org = csr_subject_orgs[0].value elif len(csr_subject_orgs) > 1: self.module.fail_json(msg=("Entrust provider does not currently support multiple validated organizations. Multiple organizations found in " "Subject DN: '{0}'. ".format(self.csr.subject))) # If no organization in the CSR, explicitly tell ECS that it should be blank in issued cert, not defaulted to # organization tied to the account. if self.csr_org is None: self.csr_org = '' try: self.ecs_client = ECSClient( entrust_api_user=self.module.params['entrust_api_user'], entrust_api_key=self.module.params['entrust_api_key'], entrust_api_cert=self.module.params['entrust_api_client_cert_path'], entrust_api_cert_key=self.module.params['entrust_api_client_cert_key_path'], entrust_api_specification_path=self.module.params['entrust_api_specification_path'] ) except SessionConfigurationException as e: module.fail_json(msg='Failed to initialize Entrust Provider: {0}'.format(to_native(e.message)))
def __init__(self, module): super(SelfSignedCertificateBackendPyOpenSSL, self).__init__(module, 'pyopenssl') if module.params[ 'selfsigned_create_subject_key_identifier'] != 'create_if_not_provided': module.fail_json( msg= 'selfsigned_create_subject_key_identifier cannot be used with the pyOpenSSL backend!' ) 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 = module.params['selfsigned_digest'] self.version = module.params['selfsigned_version'] self.serial_number = generate_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._ensure_private_key_loaded() self._ensure_csr_loaded() if self.csr is None: # Create empty CSR on the fly self.csr = crypto.X509Req() self.csr.set_pubkey(self.privatekey) self.csr.sign(self.privatekey, self.digest)
def __init__(self, module, backend): super(CertificateInfo, self).__init__( module.params['path'] or '', 'present', False, module.check_mode, ) self.backend = backend self.module = module self.content = module.params['content'] if self.content is not None: self.content = self.content.encode('utf-8') self.valid_at = module.params['valid_at'] if self.valid_at: for k, v in self.valid_at.items(): if not isinstance(v, string_types): self.module.fail_json( msg='The value for valid_at.{0} must be of type string (got {1})'.format(k, type(v)) ) self.valid_at[k] = get_relative_time_option(v, 'valid_at.{0}'.format(k))
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
def _validate_valid_in(self): valid_in_date = get_relative_time_option(self.valid_in, "valid_in", backend=self.backend) return self.existing_certificate.not_valid_before, valid_in_date, self.existing_certificate.not_valid_after
def _validate_invalid_at(self): rt = get_relative_time_option(self.invalid_at, 'invalid_at', backend=self.backend) return self.existing_certificate.not_valid_before, rt, self.existing_certificate.not_valid_after
def assertonly(self): messages = [] if self.privatekey_path is not None or self.privatekey_content is not None: if not self._validate_privatekey(): messages.append( 'Certificate %s and private key %s do not match' % (self.path, self.privatekey_path or '(provided in module options)')) if self.csr_path is not None or self.csr_content is not None: if not self._validate_csr_signature(): messages.append( 'Certificate %s and CSR %s do not match: private key mismatch' % (self.path, self.csr_path or '(provided in module options)')) if not self._validate_csr_subject(): messages.append( 'Certificate %s and CSR %s do not match: subject mismatch' % (self.path, self.csr_path or '(provided in module options)')) if not self._validate_csr_extensions(): messages.append( 'Certificate %s and CSR %s do not match: extensions mismatch' % (self.path, self.csr_path or '(provided in module options)')) if self.signature_algorithms is not None: wrong_alg = self._validate_signature_algorithms() if wrong_alg: messages.append( 'Invalid signature algorithm (got %s, expected one of %s)' % (wrong_alg, self.signature_algorithms)) if self.subject is not None: failure = self._validate_subject() if failure: dummy, cert_subject = failure messages.append( 'Invalid subject component (got %s, expected all of %s to be present)' % (cert_subject, self.subject)) if self.issuer is not None: failure = self._validate_issuer() if failure: dummy, cert_issuer = failure messages.append( 'Invalid issuer component (got %s, expected all of %s to be present)' % (cert_issuer, self.issuer)) if self.has_expired is not None: cert_expired = self._validate_has_expired() if cert_expired != self.has_expired: messages.append( 'Certificate expiration check failed (certificate expiration is %s, expected %s)' % (cert_expired, self.has_expired)) if self.version is not None: cert_version = self._validate_version() if cert_version != self.version: messages.append( 'Invalid certificate version number (got %s, expected %s)' % (cert_version, self.version)) if self.key_usage is not None: failure = self._validate_key_usage() if failure == NO_EXTENSION: messages.append('Found no keyUsage extension') elif failure: dummy, cert_key_usage = failure messages.append( 'Invalid keyUsage components (got %s, expected all of %s to be present)' % (cert_key_usage, self.key_usage)) if self.extended_key_usage is not None: failure = self._validate_extended_key_usage() if failure == NO_EXTENSION: messages.append('Found no extendedKeyUsage extension') elif failure: dummy, ext_cert_key_usage = failure messages.append( 'Invalid extendedKeyUsage component (got %s, expected all of %s to be present)' % (ext_cert_key_usage, self.extended_key_usage)) if self.subject_alt_name is not None: failure = self._validate_subject_alt_name() if failure == NO_EXTENSION: messages.append('Found no subjectAltName extension') elif failure: dummy, cert_san = failure messages.append( 'Invalid subjectAltName component (got %s, expected all of %s to be present)' % (cert_san, self.subject_alt_name)) if self.not_before is not None: cert_not_valid_before = self._validate_not_before() if cert_not_valid_before != get_relative_time_option( self.not_before, 'not_before', backend=self.backend): messages.append( 'Invalid not_before component (got %s, expected %s to be present)' % (cert_not_valid_before, self.not_before)) if self.not_after is not None: cert_not_valid_after = self._validate_not_after() if cert_not_valid_after != get_relative_time_option( self.not_after, 'not_after', backend=self.backend): messages.append( 'Invalid not_after component (got %s, expected %s to be present)' % (cert_not_valid_after, self.not_after)) if self.valid_at is not None: not_before, valid_at, not_after = self._validate_valid_at() if not (not_before <= valid_at <= not_after): messages.append( 'Certificate is not valid for the specified date (%s) - not_before: %s - not_after: %s' % (self.valid_at, not_before, not_after)) if self.invalid_at is not None: not_before, invalid_at, not_after = self._validate_invalid_at() if not_before <= invalid_at <= not_after: messages.append( 'Certificate is not invalid for the specified date (%s) - not_before: %s - not_after: %s' % (self.invalid_at, not_before, not_after)) if self.valid_in is not None: not_before, valid_in, not_after = self._validate_valid_in() if not not_before <= valid_in <= not_after: messages.append( 'Certificate is not valid in %s from now (that would be %s) - not_before: %s - not_after: %s' % (self.valid_in, valid_in, not_before, not_after)) return messages