def get_common_extensions(self, issuer_url=None, crl_url=None, ocsp_url=None): extensions = [] if crl_url: if isinstance(crl_url, six.string_types): crl_url = [url.strip() for url in crl_url.split()] urls = [ x509.UniformResourceIdentifier(force_text(c)) for c in crl_url ] dps = [ x509.DistributionPoint(full_name=[c], relative_name=None, crl_issuer=None, reasons=None) for c in urls ] extensions.append((False, x509.CRLDistributionPoints(dps))) auth_info_access = [] if ocsp_url: uri = x509.UniformResourceIdentifier(force_text(ocsp_url)) auth_info_access.append( x509.AccessDescription( access_method=AuthorityInformationAccessOID.OCSP, access_location=uri)) if issuer_url: uri = x509.UniformResourceIdentifier(force_text(issuer_url)) auth_info_access.append( x509.AccessDescription( access_method=AuthorityInformationAccessOID.CA_ISSUERS, access_location=uri)) if auth_info_access: extensions.append( (False, x509.AuthorityInformationAccess(auth_info_access))) return extensions
def get_common_extensions( self, issuer_url: Optional[str] = None, crl_url: Optional[Iterable[str]] = None, ocsp_url: Optional[str] = None, ) -> List[Tuple[bool, Union[x509.CRLDistributionPoints, x509.AuthorityInformationAccess]]]: """Add extensions potentially common to both CAs and certs.""" extensions: List[Tuple[bool, Union[x509.CRLDistributionPoints, x509.AuthorityInformationAccess]]] = [] if crl_url: urls = [x509.UniformResourceIdentifier(c) for c in crl_url] dps = [ x509.DistributionPoint(full_name=[c], relative_name=None, crl_issuer=None, reasons=None) for c in urls ] extensions.append((False, x509.CRLDistributionPoints(dps))) auth_info_access = [] if ocsp_url: uri = x509.UniformResourceIdentifier(ocsp_url) auth_info_access.append( x509.AccessDescription(access_method=AuthorityInformationAccessOID.OCSP, access_location=uri) ) if issuer_url: uri = x509.UniformResourceIdentifier(issuer_url) auth_info_access.append( x509.AccessDescription( access_method=AuthorityInformationAccessOID.CA_ISSUERS, access_location=uri ) ) if auth_info_access: extensions.append((False, x509.AuthorityInformationAccess(auth_info_access))) return extensions
def extension_type(self) -> x509.AuthorityInformationAccess: # pylint: disable=not-an-iterable; pylint does not detect GeneralNameList as iterable descs = [ x509.AccessDescription(AuthorityInformationAccessOID.CA_ISSUERS, v) for v in self._issuers ] descs += [ x509.AccessDescription(AuthorityInformationAccessOID.OCSP, v) for v in self._ocsp ] return x509.AuthorityInformationAccess(descriptions=descs)
def assert_oscp(self, crt, certificate): ext = crt.extensions.get_extension_for_oid(ExtensionOID.AUTHORITY_INFORMATION_ACCESS) self.assertFalse(ext.critical) self.assertEqual( ext.value[0], x509.AccessDescription( AuthorityInformationAccessOID.OCSP, x509.UniformResourceIdentifier(certificate.ocsp_distribution_host) ), )
def _set_ocsp_distribution_url(self, cert: Certificate_model) -> None: if cert.type == CertificateTypes.SERVER_CERT or cert.type == CertificateTypes.CLIENT_CERT: cert = cert.parent if cert.ocsp_distribution_host: self._builder = self._builder.add_extension( x509.AuthorityInformationAccess([ x509.AccessDescription( AuthorityInformationAccessOID.OCSP, x509.UniformResourceIdentifier( cert.ocsp_distribution_host)) ]), critical=True)
def _decode_authority_information_access(backend, aia): aia = backend._ffi.cast("Cryptography_STACK_OF_ACCESS_DESCRIPTION *", aia) aia = backend._ffi.gc(aia, backend._lib.sk_ACCESS_DESCRIPTION_free) num = backend._lib.sk_ACCESS_DESCRIPTION_num(aia) access_descriptions = [] for i in range(num): ad = backend._lib.sk_ACCESS_DESCRIPTION_value(aia, i) backend.openssl_assert(ad.method != backend._ffi.NULL) oid = x509.ObjectIdentifier(_obj2txt(backend, ad.method)) backend.openssl_assert(ad.location != backend._ffi.NULL) gn = _decode_general_name(backend, ad.location) access_descriptions.append(x509.AccessDescription(oid, gn)) return x509.AuthorityInformationAccess(access_descriptions)
def _set_ocsp_distribution_url(self, cert: CertificateType) -> None: if cert.type is not CertificateTypes.ROOT: cert = cert.parent if cert.ocsp_distribution_host: self._builder = self._builder.add_extension( x509.AuthorityInformationAccess([ x509.AccessDescription( AuthorityInformationAccessOID.OCSP, x509.UniformResourceIdentifier( cert.ocsp_distribution_host), ) ]), critical=False, )
def test_multiple_aia(ca_workspace, server): root = ca_workspace.issue_new_trusted_root() intermediate = ca_workspace.issue_new_ca(root) intermediate_url = server.create_aia_url(intermediate) cert = ca_workspace.issue_new_leaf( intermediate, ca_issuers=[ x509.AccessDescription( x509.AuthorityInformationAccessOID.OCSP, x509.UniformResourceIdentifier("http://example.com") ), intermediate_url ] ) ca_workspace.assert_validates(cert, [cert, intermediate, root])
def _decode_information_access(backend, ia): ia = backend._ffi.cast("Cryptography_STACK_OF_ACCESS_DESCRIPTION *", ia) ia = backend._ffi.gc( ia, lambda x: backend._lib.sk_ACCESS_DESCRIPTION_pop_free( x, backend._ffi.addressof(backend._lib._original_lib, "ACCESS_DESCRIPTION_free"))) num = backend._lib.sk_ACCESS_DESCRIPTION_num(ia) access_descriptions = [] for i in range(num): ad = backend._lib.sk_ACCESS_DESCRIPTION_value(ia, i) backend.openssl_assert(ad.method != backend._ffi.NULL) oid = x509.ObjectIdentifier(_obj2txt(backend, ad.method)) backend.openssl_assert(ad.location != backend._ffi.NULL) gn = _decode_general_name(backend, ad.location) access_descriptions.append(x509.AccessDescription(oid, gn)) return access_descriptions
def _build_authority_information_access(self, ext): aia = self._backend._lib.X509V3_EXT_d2i(ext) assert aia != self._backend._ffi.NULL aia = self._backend._ffi.cast( "Cryptography_STACK_OF_ACCESS_DESCRIPTION *", aia) aia = self._backend._ffi.gc( aia, self._backend._lib.sk_ACCESS_DESCRIPTION_free) num = self._backend._lib.sk_ACCESS_DESCRIPTION_num(aia) access_descriptions = [] for i in range(num): ad = self._backend._lib.sk_ACCESS_DESCRIPTION_value(aia, i) assert ad.method != self._backend._ffi.NULL oid = x509.ObjectIdentifier(_obj2txt(self._backend, ad.method)) assert ad.location != self._backend._ffi.NULL gn = _build_general_name(self._backend, ad.location) access_descriptions.append(x509.AccessDescription(oid, gn)) return x509.AuthorityInformationAccess(access_descriptions)
def parse_access_desc(desc): if 'method' not in desc: raise ValueError('Access description must contain \'method\' field.') method = desc['method'] if method != 'OCSP' and method != 'CA_ISSUERS': raise ValueError( 'Available methods for access description are OCSP and CA_ISSUERS') if method == 'OCSP': method = x509.AuthorityInformationAccessOID.OCSP else: method = x509.AuthorityInformationAccessOID.CA_ISSUERS if 'value' not in desc: raise ValueError('Access description must contain \'value\' field.') return x509.AccessDescription(method, parse_general_name(desc['value']))
def create(): """ Create :return: """ parent_id = request.args.get("parent") parent_cert = Certificate.objects( id=parent_id).first() if parent_id else None form_default_data = {} # @todo how to list meta fields # @todo rewrite with a intersect field_names = [ f.name for f in CreateCertificateForm() if hasattr(x509.NameOID, f.name) ] for field_name in field_names: # get default value from environment variables form_default_data[field_name] = os.environ.get(f"DEFAULT_{field_name}") # get default value from parent certificate if any if parent_cert: for item in parent_cert.cert.subject: if item.oid == getattr(x509.NameOID, field_name): form_default_data[field_name] = item.value if parent_cert: form = CreateCertificateForm( data={ **form_default_data, "duration": int(current_app.config.get("DEFAULT_DURATION")), "parent": parent_cert.cert.serial_number, "mode": { "is_server_auth": True, "is_client_auth": True, }, "policy": { "oid": current_app.config.get('DEFAULT_POLICY_OID'), "url": current_app.config.get('DEFAULT_POLICY_URL'), }, "crl": { "url": current_app.config.get("DEFAULT_CA_ISSUER_URL") + url_for("repository.download", id=parent_cert.id, file_format="crl"), }, "aia": { "enabled": True, 'ca_issuers': current_app.config.get("DEFAULT_CA_ISSUER_URL") + url_for( "repository.download", id=parent_id, file_format="crt"), 'ocsp': current_app.config.get("DEFAULT_OCSP_URL") } }) else: form = CreateCertificateForm( data={ **form_default_data, "duration": 10 * int(current_app.config.get("DEFAULT_DURATION")), "parent": 0, "mode": { "is_ca": True, }, "policy": { "oid": current_app.config.get('DEFAULT_POLICY_OID'), "url": current_app.config.get('DEFAULT_POLICY_URL'), }, "aia": { "enabled": False, 'ca_issuers': current_app.config.get( "DEFAULT_CA_ISSUER_URL"), 'ocsp': current_app.config.get("DEFAULT_OCSP_URL") } }) if form.validate_on_submit(): # key key = rsa.generate_private_key(public_exponent=65537, key_size=2048, backend=default_backend()) # serial number serial_number = x509.random_serial_number() # subject names = [] for field_name in field_names: if getattr(form, field_name).data: names.append( x509.NameAttribute(getattr(x509.NameOID, field_name), getattr(form, field_name).data)) subject = x509.Name(names) # issuer and signing key issuer = parent_cert.cert.subject if parent_cert else subject signing_key = parent_cert.key if parent_cert else key cert = x509.CertificateBuilder( ).subject_name(subject).issuer_name(issuer).public_key( key.public_key()).serial_number(serial_number).not_valid_before( datetime.utcnow()).not_valid_after( datetime.utcnow() + timedelta(days=int(form.duration.data))).add_extension( x509.SubjectKeyIdentifier.from_public_key( key.public_key()), critical=False).add_extension( x509.AuthorityKeyIdentifier.from_issuer_public_key( signing_key.public_key()), critical=False) # basic constraints cert = cert.add_extension(x509.BasicConstraints( form.mode.data.get('is_ca'), None), critical=False) # ocsp if form.aia.data.get("enabled"): cert = cert.add_extension( x509.AuthorityInformationAccess([ # openssl x509 -in 95285781730451486911577519787958288522332983584.crt -ocsp_uri -noout x509.AccessDescription( x509.OID_OCSP, x509.UniformResourceIdentifier( form.aia.data.get('ocsp'))), # @todo validate issuers x509.AccessDescription( x509.OID_CA_ISSUERS, x509.UniformResourceIdentifier( form.aia.data.get('ca_issuers'))) ]), critical=False) # key_usage if form.mode.data.get('is_ca'): cert = cert.add_extension( x509.KeyUsage( # 数字签名 digital_signature=True, # 认可签名 content_commitment=False, # 秘钥加密 key_encipherment=False, # 数据加密 data_encipherment=False, # 秘钥协商 key_agreement=False, # 证书签名 key_cert_sign=True, # CRL 签名 crl_sign=True, # 仅加密 encipher_only=False, # 仅解密 decipher_only=False, ), critical=True) else: cert = cert.add_extension(x509.KeyUsage(digital_signature=True, content_commitment=False, key_encipherment=True, data_encipherment=True, key_agreement=False, key_cert_sign=False, crl_sign=False, encipher_only=False, decipher_only=False), critical=True) # extended_key_usage # @todo render all extended key usage extended_key_usage = [] if form.mode.data.get('is_server_auth'): extended_key_usage.append(x509.oid.ExtendedKeyUsageOID.SERVER_AUTH) if form.mode.data.get('is_client_auth'): extended_key_usage.append(x509.oid.ExtendedKeyUsageOID.CLIENT_AUTH) if len(extended_key_usage): cert = cert.add_extension( x509.ExtendedKeyUsage(extended_key_usage), critical=False) # subject alternative name san = [] for item in form.san.data.split("\n"): if item: try: ipaddress = IPv4Address(item) san.append(x509.IPAddress(ipaddress)) except Exception as e: logger.info(e) san.append(x509.DNSName(item)) if len(san): cert = cert.add_extension(x509.SubjectAlternativeName(san), critical=False) # certificate policies if form.policy.data.get('url') and form.policy.data.get('oid'): cert = cert.add_extension(x509.CertificatePolicies([ x509.PolicyInformation( x509.ObjectIdentifier(form.policy.data.get('oid')), [form.policy.data.get('url')], ) ]), critical=False) # crl distribution points if form.crl.data.get('url'): cert = cert.add_extension(x509.CRLDistributionPoints([ x509.DistributionPoint( full_name=[ x509.UniformResourceIdentifier( form.crl.data.get('url')) ], relative_name=None, reasons=None, crl_issuer=None, ) ]), critical=False) # sign cert = cert.sign(signing_key, hashes.SHA256(), default_backend()) # save c = Certificate(key=key, cert=cert, serial_number=str(serial_number), pid=parent_cert.id if parent_cert else None) c.save() flash(f"Certificate create successful") return redirect(url_for(".home")) return render_template("create.html", form=form)
def test_generate_user_certificate(self): root_key = Key().create_key(2048) root_certificate = CertificateFactory(expires_at=arrow.get( timezone.now()).replace(days=+6).date(), key=root_key.serialize()) root_certhandler = Certificate() root_certhandler.create_certificate(root_certificate) subject = DistinguishedNameFactory( countryName=root_certificate.dn.countryName, stateOrProvinceName=root_certificate.dn.stateOrProvinceName, organizationName=root_certificate.dn.organizationName) int_key = Key().create_key(2048) int_certificate = CertificateFactory( expires_at=arrow.get(timezone.now()).replace(days=+5).date(), type=CertificateTypes.INTERMEDIATE, parent=root_certificate, dn=subject, key=int_key.serialize()) int_certhandler = Certificate() int_certhandler.create_certificate(int_certificate) key = Key().create_key(2048) server_subject = DistinguishedNameFactory( subjectAltNames=["jeroen", "*****@*****.**"]) certificate = CertificateFactory(type=CertificateTypes.CLIENT_CERT, parent=int_certificate, dn=server_subject, key=key.serialize()) certhandler = Certificate() certhandler.create_certificate(certificate) crt = certhandler.certificate self.assertEqual(crt.serial_number, int(certificate.serial)) self.assertEqual(crt.public_key().public_numbers(), key.key.public_key().public_numbers()) self.assertEqual( crt.not_valid_before, datetime.datetime(year=certificate.created_at.year, month=certificate.created_at.month, day=certificate.created_at.day)) self.assertEqual( crt.not_valid_after, datetime.datetime(year=certificate.expires_at.year, month=certificate.expires_at.month, day=certificate.expires_at.day)) # basicConstraints = CA:FALSE ext = crt.extensions.get_extension_for_oid( ExtensionOID.BASIC_CONSTRAINTS) self.assertFalse(ext.critical) self.assertEqual(ext.value, x509.BasicConstraints(ca=False, path_length=None)) # authorityKeyIdentifier = keyid:always, issuer ext = crt.extensions.get_extension_for_oid( ExtensionOID.AUTHORITY_KEY_IDENTIFIER) self.assertTrue(ext.critical) self.assertEqual( ext.value.key_identifier, _key_identifier_from_public_key(int_key.key.public_key())) # subjectKeyIdentifier = hash ext = crt.extensions.get_extension_for_oid( ExtensionOID.SUBJECT_KEY_IDENTIFIER) self.assertTrue(ext.critical) self.assertEqual(ext.value.digest, _key_identifier_from_public_key(key.key.public_key())) # keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment ext = crt.extensions.get_extension_for_oid(ExtensionOID.KEY_USAGE) self.assertTrue(ext.critical) self.assertEqual( ext.value, x509.KeyUsage(digital_signature=True, content_commitment=True, key_encipherment=True, data_encipherment=False, key_agreement=False, key_cert_sign=False, crl_sign=False, encipher_only=False, decipher_only=False)) # extendedKeyUsage = clientAuth, emailProtection ext = crt.extensions.get_extension_for_oid( ExtensionOID.EXTENDED_KEY_USAGE) self.assertFalse(ext.critical) self.assertEqual([x for x in ext.value], [ ExtendedKeyUsageOID.CLIENT_AUTH, ExtendedKeyUsageOID.EMAIL_PROTECTION ]) # { % if cert.dn.subjectAltNames %} # subjectAltName = @alt_names ext = crt.extensions.get_extension_for_oid( ExtensionOID.SUBJECT_ALTERNATIVE_NAME) self.assertFalse(ext.critical) self.assertEqual([x.value for x in ext.value], ['jeroen', '*****@*****.**']) # crlDistributionPoints ext = crt.extensions.get_extension_for_oid( ExtensionOID.CRL_DISTRIBUTION_POINTS) self.assertTrue(ext.critical) crl_dp = ext.value self.assertEqual( crl_dp[0].full_name[0].value, 'URI:{}{}.crl'.format(int_certificate.crl_distribution_url, int_certificate.shortname)) self.assertEqual( crl_dp[0].reasons, frozenset([ x509.ReasonFlags.key_compromise, x509.ReasonFlags.ca_compromise, x509.ReasonFlags.affiliation_changed, x509.ReasonFlags.superseded, x509.ReasonFlags.privilege_withdrawn, x509.ReasonFlags.cessation_of_operation, x509.ReasonFlags.aa_compromise, x509.ReasonFlags.certificate_hold, ])) # OCSP # authorityInfoAccess = OCSP;URI:{{cert.ocsp_distribution_host}} ext = crt.extensions.get_extension_for_oid( ExtensionOID.AUTHORITY_INFORMATION_ACCESS) self.assertTrue(ext.critical) self.assertEqual( ext.value[0], x509.AccessDescription( AuthorityInformationAccessOID.OCSP, x509.UniformResourceIdentifier( int_certificate.ocsp_distribution_host))) # subject subject = crt.subject self.assertIsInstance(subject, x509.Name) self.assertListEqual(list(subject), [ x509.NameAttribute(NameOID.COMMON_NAME, certificate.dn.commonName), x509.NameAttribute(NameOID.ORGANIZATION_NAME, certificate.dn.organizationName), x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME, certificate.dn.organizationalUnitName), x509.NameAttribute(NameOID.LOCALITY_NAME, certificate.dn.localityName), x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, certificate.dn.stateOrProvinceName), x509.NameAttribute(NameOID.EMAIL_ADDRESS, certificate.dn.emailAddress), x509.NameAttribute(NameOID.COUNTRY_NAME, str(certificate.dn.countryName)), ]) # issuer issuer = crt.issuer self.assertIsInstance(issuer, x509.Name) self.assertListEqual(list(issuer), [ x509.NameAttribute(NameOID.COMMON_NAME, int_certificate.dn.commonName), x509.NameAttribute(NameOID.ORGANIZATION_NAME, int_certificate.dn.organizationName), x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME, int_certificate.dn.organizationalUnitName), x509.NameAttribute(NameOID.LOCALITY_NAME, int_certificate.dn.localityName), x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, int_certificate.dn.stateOrProvinceName), x509.NameAttribute(NameOID.EMAIL_ADDRESS, int_certificate.dn.emailAddress), x509.NameAttribute(NameOID.COUNTRY_NAME, str(int_certificate.dn.countryName)), ])
def generate(self, csr, issuer_crt, issuer_key, profile, ca=False, selfSigned=False, start=None, duration=None, digest=None, sans=[]): """Generate a certificate using: - Certificate request (csr) - Issuer certificate (issuer_crt) - Issuer key (issuer_key) - profile object (profile) Optional parameters set: - a CA certificate role (ca) - a self-signed certificate (selfSigned) - a specific start timestamp (start) """ # Retrieve subject from csr subject = csr.subject self.output('Subject found: {s}'.format(s=subject.rfc4514_string()), level="DEBUG") dn = self._get_dn(subject) self.output('DN found is {d}'.format(d=dn), level="DEBUG") try: alt_names = None alt_names = csr.extensions.get_extension_for_oid( ExtensionOID.SUBJECT_ALTERNATIVE_NAME) self.output('Subject alternate found: {s}'.format(s=alt_names), level="DEBUG") except x509.ExtensionNotFound as err: pass # Force default if necessary now = datetime.datetime.utcnow( ) if start is None else datetime.fromtimestamp(start) duration = profile['duration'] if duration is None else duration # Generate serial number try: serial_number = self._generate_serial() except Exception as err: raise Exception( 'Error during serial number generation: {e}'.format(e=err)) # For self-signed certificate issuer is certificate itself issuer_name = subject if selfSigned else issuer_crt.issuer issuer_serial = serial_number if selfSigned else issuer_crt.serial_number try: # Define basic constraints if ca: basic_contraints = x509.BasicConstraints(ca=True, path_length=0) else: basic_contraints = x509.BasicConstraints(ca=False, path_length=None) builder = (x509.CertificateBuilder().subject_name( subject).issuer_name(issuer_name).public_key( csr.public_key()).serial_number( serial_number).not_valid_before(now).not_valid_after( now + datetime.timedelta(days=duration)).add_extension( basic_contraints, critical=True)) except Exception as err: raise Exception('Unable to build structure: {e}'.format(e=err)) # We never trust CSR extensions # they may have been alterated by the user try: # Due to uPKI design (TLS for renew), digital_signature MUST be setup digital_signature = True # Initialize key usage content_commitment = False key_encipherment = False data_encipherment = False key_agreement = False key_cert_sign = False crl_sign = False encipher_only = False decipher_only = False # Build Key Usages from profile for usage in profile['keyUsage']: if usage == 'digitalSignature': digital_signature = True elif usage == 'nonRepudiation': content_commitment = True elif usage == 'keyEncipherment': key_encipherment = True elif usage == 'dataEncipherment': data_encipherment = True elif usage == 'keyAgreement': key_agreement = True elif usage == 'keyCertSign': key_cert_sign = True elif usage == 'cRLSign': crl_sign = True elif usage == 'encipherOnly': encipher_only = True elif usage == 'decipherOnly': decipher_only = True # Setup X509 Key Usages key_usages = x509.KeyUsage(digital_signature=digital_signature, content_commitment=content_commitment, key_encipherment=key_encipherment, data_encipherment=data_encipherment, key_agreement=key_agreement, key_cert_sign=key_cert_sign, crl_sign=crl_sign, encipher_only=encipher_only, decipher_only=decipher_only) builder = builder.add_extension(key_usages, critical=True) except KeyError: # If no Key Usages are set, thats strange raise Exception('No Key Usages set.') except Exception as err: raise Exception('Unable to set Key Usages: {e}'.format(e=err)) try: # Build Key Usages extended based on profile key_usages_extended = list() for eusage in profile['extendedKeyUsage']: if eusage == 'serverAuth': key_usages_extended.append(ExtendedKeyUsageOID.SERVER_AUTH) elif eusage == 'clientAuth': key_usages_extended.append(ExtendedKeyUsageOID.CLIENT_AUTH) elif eusage == 'codeSigning': key_usages_extended.append( ExtendedKeyUsageOID.CODE_SIGNING) elif eusage == 'emailProtection': key_usages_extended.append( ExtendedKeyUsageOID.EMAIL_PROTECTION) elif eusage == 'timeStamping': key_usages_extended.append( ExtendedKeyUsageOID.TIME_STAMPING) elif eusage == 'OCSPSigning': key_usages_extended.append( ExtendedKeyUsageOID.OCSP_SIGNING) #### CHECK TROUBLES ASSOCIATED WITH THIS CHOICE ##### # Always add 'clientAuth' for automatic renewal if ExtendedKeyUsageOID.CLIENT_AUTH not in key_usages_extended: key_usages_extended.append(ExtendedKeyUsageOID.CLIENT_AUTH) ##################################################### # Add Deprecated nsCertType (still required by some software) # nsCertType_oid = x509.ObjectIdentifier('2.16.840.1.113730.1.1') # for c_type in profile['certType']: # if c_type.lower() in ['client', 'server', 'email', 'objsign']: # builder.add_extension(nsCertType_oid, c_type.lower()) # Set Key Usages if needed if len(key_usages_extended): builder = builder.add_extension( x509.ExtendedKeyUsage(key_usages_extended), critical=False) except KeyError: # If no extended key usages are set, do nothing pass except Exception as err: raise Exception( 'Unable to set Extended Key Usages: {e}'.format(e=err)) # Add alternate names if found in CSR if alt_names is not None: # Verify each time that SANS entry was registered # We can NOT trust CSR data (client manipulation) subject_alt = list([]) for entry in alt_names.value.get_values_for_type(x509.IPAddress): if entry not in sans: continue subject_alt.append(x509.IPAddress(ipaddress.ip_address(entry))) for entry in alt_names.value.get_values_for_type(x509.DNSName): if entry not in sans: continue subject_alt.append(x509.DNSName(entry)) for entry in alt_names.value.get_values_for_type(x509.RFC822Name): if entry not in sans: continue subject_alt.append(x509.RFC822Name(entry)) for entry in alt_names.value.get_values_for_type( x509.UniformResourceIdentifier): if entry not in sans: continue subject_alt.append(x509.UniformResourceIdentifier(entry)) try: # Add all alternates to certificate builder = builder.add_extension( x509.SubjectAlternativeName(subject_alt), critical=False) except Exception as err: raise Exception( 'Unable to set alternatives name: {e}'.format(e=err)) try: # Register signing authority issuer_key_id = x509.SubjectKeyIdentifier.from_public_key( issuer_key.public_key()) builder = builder.add_extension(x509.AuthorityKeyIdentifier( issuer_key_id.digest, [x509.DirectoryName(issuer_name)], issuer_serial), critical=False) except Exception as err: raise Exception( 'Unable to setup Authority Identifier: {e}'.format(e=err)) ca_endpoints = list() try: # Default value if not set in profile ca_url = profile['ca'] if profile[ 'ca'] else "https://certificates.{d}/certs/ca.crt".format( d=profile['domain']) except KeyError: ca_url = None try: # Default value if not set in profile ocsp_url = profile['ocsp'] if profile[ 'ocsp'] else "https://certificates.{d}/ocsp".format( d=profile['domain']) except KeyError: ocsp_url = None try: # Add CA certificate distribution point and OCSP validation url if ca_url: ca_endpoints.append( x509.AccessDescription( x509.oid.AuthorityInformationAccessOID.OCSP, x509.UniformResourceIdentifier(ca_url))) if ocsp_url: ca_endpoints.append( x509.AccessDescription( x509.oid.AuthorityInformationAccessOID.OCSP, x509.UniformResourceIdentifier(ocsp_url))) builder = builder.add_extension( x509.AuthorityInformationAccess(ca_endpoints), critical=False) except Exception as err: raise Exception( 'Unable to setup OCSP/CA endpoint: {e}'.format(e=err)) try: # Add CRL distribution point crl_endpoints = list() # Default value if not set in profile url = "https://certificates.{d}/certs/crl.pem".format( d=profile['domain']) try: if profile['csr']: url = profile['csr'] except KeyError: pass crl_endpoints.append( x509.DistributionPoint( [x509.UniformResourceIdentifier(url)], None, None, [x509.DNSName(issuer_name.rfc4514_string())])) builder = builder.add_extension( x509.CRLDistributionPoints(crl_endpoints), critical=False) except Exception as err: raise Exception('Unable to setup CRL endpoints: {e}'.format(e=err)) if digest is None: digest = profile['digest'] if digest == 'md5': digest = hashes.MD5() elif digest == 'sha1': digest = hashes.SHA1() elif digest == 'sha256': digest = hashes.SHA256() elif digest == 'sha512': digest = hashed.SHA512() else: raise NotImplementedError( 'Private key only support {s} digest signatures'.format( s=self._allowed.Digest)) try: pub_crt = builder.sign(private_key=issuer_key, algorithm=digest, backend=self.__backend) except Exception as err: raise Exception('Unable to sign certificate: {e}'.format(e=err)) return pub_crt
def makeCert(cert_data): #given parameters for cert, returns bytes for certificate (private key and public key) private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048, backend=default_backend()) public_key = private_key.public_key() builder = x509.CertificateBuilder() subject = x509.Name([ x509.NameAttribute(NameOID.COMMON_NAME, cert_data.common_name), x509.NameAttribute(NameOID.COUNTRY_NAME, cert_data.country_name), x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, cert_data.state_or_province_name), x509.NameAttribute(NameOID.LOCALITY_NAME, cert_data.locality_name), x509.NameAttribute(NameOID.ORGANIZATION_NAME, cert_data.organization_name), x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME, cert_data.organization_unit_name), x509.NameAttribute(NameOID.USER_ID, cert_data.user_id), x509.NameAttribute(NameOID.EMAIL_ADDRESS, cert_data.email_address), ]) builder = builder.subject_name(subject) builder = builder.not_valid_before(cert_data.not_valid_before) builder = builder.not_valid_after(cert_data.not_valid_after) builder = builder.serial_number(cert_data.serial_number) builder = builder.public_key(public_key) builder = builder.add_extension(x509.SubjectAlternativeName( [x509.DNSName(cert_data.dns_name)]), critical=False) #if CA if (cert_data.is_ca): builder = builder.issuer_name(subject) builder = builder.add_extension( x509.BasicConstraints(ca=True, path_length=2), critical=True, ) builder = builder.add_extension(x509.KeyUsage(digital_signature=True, content_commitment=True, key_encipherment=True, key_agreement=False, data_encipherment=False, crl_sign=True, encipher_only=False, decipher_only=False, key_cert_sign=True), critical=True) builder = builder.add_extension(x509.AuthorityInformationAccess([ (x509.AccessDescription( x509.oid.AuthorityInformationAccessOID.OCSP, x509.UniformResourceIdentifier('https:ocsp.zibawa.com'))) ]), critical=False) certificate = builder.sign(private_key=private_key, algorithm=hashes.SHA256(), backend=default_backend()) #write certificate to pem file with open(certStorePath(cert_data.serial_number), "wb") as f: f.write(certificate.public_bytes(serialization.Encoding.PEM)) #write private key to pem file with open(keyStorePath(cert_data.serial_number), "wb") as f: f.write( private_key.private_bytes( encoding=serialization.Encoding.PEM, format=serialization.PrivateFormat.TraditionalOpenSSL, encryption_algorithm=serialization.NoEncryption())) #returns only public cert as datastream dataStream = (certificate.public_bytes(serialization.Encoding.PEM)) else: #if NOT CA builder = builder.issuer_name( loadPEMCert(certStorePath(cert_data.issuer_serial_number)).subject) builder = builder.add_extension( x509.BasicConstraints(ca=False, path_length=None), critical=True, ) builder = builder.add_extension(x509.KeyUsage(digital_signature=True, content_commitment=True, key_encipherment=True, key_agreement=False, data_encipherment=False, crl_sign=False, encipher_only=False, decipher_only=False, key_cert_sign=False), critical=True) certificate = builder.sign(private_key=loadPEMKey( keyStorePath(cert_data.issuer_serial_number)), algorithm=hashes.SHA256(), backend=default_backend()) #if not CA return public AND private key dataStream = private_key.private_bytes( encoding=serialization.Encoding.PEM, format=serialization.PrivateFormat.TraditionalOpenSSL, encryption_algorithm=serialization.NoEncryption()) dataStream += (certificate.public_bytes(serialization.Encoding.PEM)) return dataStream
def sign_csr(key, csr, caprofile, valid=30, isca=False, cacert=None, aia_issuers=None, ocsp_responders=None): global SETTINGS one_day = datetime.timedelta(1, 0, 0) builder = x509.CertificateBuilder() builder = builder.subject_name(csr.subject) if not cacert: builder = builder.issuer_name(x509.Name(construct_sn(caprofile))) else: builder = builder.issuer_name(cacert.subject) builder = builder.not_valid_before(datetime.datetime.today() - one_day) builder = builder.not_valid_after(datetime.datetime.today() + (one_day * valid)) #builder = builder.serial_number(x509.random_serial_number()) # too new to some systems builder = builder.serial_number( int.from_bytes(os.urandom(10), byteorder="big")) builder = builder.public_key(csr.public_key()) builder = builder.add_extension(x509.SubjectKeyIdentifier.from_public_key( csr.public_key()), critical=False) # more info about issuer has_ski = False try: if cacert: ski = cacert.extensions.get_extension_for_class( x509.SubjectKeyIdentifier) builder = builder.add_extension( x509.AuthorityKeyIdentifier.from_issuer_subject_key_identifier( ski), critical=False) has_ski = True except AttributeError: # this is workaround for older versions of python cryptography, not having from_issuer_subject_key_identifier # -> which throws AttributeError has_ski = False except x509.extensions.ExtensionNotFound: has_ski = False if not has_ski: builder = builder.add_extension( x509.AuthorityKeyIdentifier.from_issuer_public_key( key.public_key()), critical=False) all_aias = [] if aia_issuers: for loc in aia_issuers: aia_uri = x509.AccessDescription( AuthorityInformationAccessOID.CA_ISSUERS, x509.UniformResourceIdentifier(loc)) all_aias.append(aia_uri) if ocsp_responders: for resp in ocsp_responders: aia_uri = x509.AccessDescription( AuthorityInformationAccessOID.OCSP, x509.UniformResourceIdentifier(resp)) all_aias.append(aia_uri) if all_aias: alist = x509.AuthorityInformationAccess(all_aias) builder = builder.add_extension(alist, critical=False) print("sign CSR: == extensions ==") for e in csr.extensions: if isinstance(e.value, x509.BasicConstraints): print("sign CSR: %s" % (e.oid, )) if e.value.ca: print(" CA=TRUE requested") if isca and not SETTINGS["ca"]["settings"]["grant_ca"]: print(" not allowed but overridden") elif not SETTINGS["ca"]["settings"]["grant_ca"]: print(" not allowed by rule") continue else: print(" allowed by rule") builder = builder.add_extension(e.value, e.critical) certificate = builder.sign(private_key=key, algorithm=hashes.SHA256(), backend=default_backend()) return certificate
def test_generate_root_ca(self): key = Key().create_key(2048) certificate = CertificateFactory(key=key.serialize()) certhandler = Certificate() certhandler.create_certificate(certificate) crt = certhandler.certificate self.assertEqual(crt.serial_number, int(certificate.serial)) self.assertEqual(crt.public_key().public_numbers(), key.key.public_key().public_numbers()) self.assertEqual( crt.not_valid_before, datetime.datetime(year=certificate.created_at.year, month=certificate.created_at.month, day=certificate.created_at.day)) self.assertEqual( crt.not_valid_after, datetime.datetime(year=certificate.expires_at.year, month=certificate.expires_at.month, day=certificate.expires_at.day)) # subject subject = crt.subject self.assertIsInstance(subject, x509.Name) self.assertListEqual(list(subject), [ x509.NameAttribute(NameOID.COMMON_NAME, certificate.dn.commonName), x509.NameAttribute(NameOID.ORGANIZATION_NAME, certificate.dn.organizationName), x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME, certificate.dn.organizationalUnitName), x509.NameAttribute(NameOID.LOCALITY_NAME, certificate.dn.localityName), x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, certificate.dn.stateOrProvinceName), x509.NameAttribute(NameOID.EMAIL_ADDRESS, certificate.dn.emailAddress), x509.NameAttribute(NameOID.COUNTRY_NAME, str(certificate.dn.countryName)), ]) # issuer issuer = crt.issuer self.assertIsInstance(issuer, x509.Name) self.assertListEqual(list(subject), [ x509.NameAttribute(NameOID.COMMON_NAME, certificate.dn.commonName), x509.NameAttribute(NameOID.ORGANIZATION_NAME, certificate.dn.organizationName), x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME, certificate.dn.organizationalUnitName), x509.NameAttribute(NameOID.LOCALITY_NAME, certificate.dn.localityName), x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, certificate.dn.stateOrProvinceName), x509.NameAttribute(NameOID.EMAIL_ADDRESS, certificate.dn.emailAddress), x509.NameAttribute(NameOID.COUNTRY_NAME, str(certificate.dn.countryName)), ]) # crlDistributionspoints ext = crt.extensions.get_extension_for_oid( ExtensionOID.CRL_DISTRIBUTION_POINTS) self.assertTrue(ext.critical) crl_dp = ext.value self.assertEqual( crl_dp[0].full_name[0].value, 'URI:{}{}.crl'.format(certificate.crl_distribution_url, certificate.shortname)) self.assertEqual( crl_dp[0].reasons, frozenset([ x509.ReasonFlags.key_compromise, x509.ReasonFlags.ca_compromise, x509.ReasonFlags.affiliation_changed, x509.ReasonFlags.superseded, x509.ReasonFlags.privilege_withdrawn, x509.ReasonFlags.cessation_of_operation, x509.ReasonFlags.aa_compromise, x509.ReasonFlags.certificate_hold, ])) # keyUsage = basicConstraints = critical, CA:true ext = crt.extensions.get_extension_for_oid( ExtensionOID.BASIC_CONSTRAINTS) self.assertTrue(ext.critical) self.assertEqual(ext.value, x509.BasicConstraints(ca=True, path_length=None)) # keyUsage = critical, digitalSignature, cRLSign, keyCertSign ext = crt.extensions.get_extension_for_oid(ExtensionOID.KEY_USAGE) self.assertTrue(ext.critical) self.assertEqual( ext.value, x509.KeyUsage(digital_signature=True, content_commitment=False, key_encipherment=False, data_encipherment=False, key_agreement=False, key_cert_sign=True, crl_sign=True, encipher_only=False, decipher_only=False)) # OCSP # authorityInfoAccess = OCSP;URI:{{cert.ocsp_distribution_host}} ext = crt.extensions.get_extension_for_oid( ExtensionOID.AUTHORITY_INFORMATION_ACCESS) self.assertTrue(ext.critical) self.assertEqual( ext.value[0], x509.AccessDescription( AuthorityInformationAccessOID.OCSP, x509.UniformResourceIdentifier( certificate.ocsp_distribution_host))) # authorityKeyIdentifier = keyid:always, issuer ext = crt.extensions.get_extension_for_oid( ExtensionOID.AUTHORITY_KEY_IDENTIFIER) self.assertTrue(ext.critical) self.assertEqual(ext.value.key_identifier, _key_identifier_from_public_key(key.key.public_key())) # subjectKeyIdentifier = hash ext = crt.extensions.get_extension_for_oid( ExtensionOID.SUBJECT_KEY_IDENTIFIER) self.assertTrue(ext.critical) self.assertEqual(ext.value.digest, _key_identifier_from_public_key(key.key.public_key()))
class TestCertificateRevocationListBuilder(object): def test_issuer_name_invalid(self): builder = x509.CertificateRevocationListBuilder() with pytest.raises(TypeError): builder.issuer_name("notanx509name") def test_set_issuer_name_twice(self): builder = x509.CertificateRevocationListBuilder().issuer_name( x509.Name([x509.NameAttribute(NameOID.COUNTRY_NAME, u'US')]) ) with pytest.raises(ValueError): builder.issuer_name( x509.Name([x509.NameAttribute(NameOID.COUNTRY_NAME, u'US')]) ) @pytest.mark.requires_backend_interface(interface=RSABackend) @pytest.mark.requires_backend_interface(interface=X509Backend) def test_aware_last_update(self, backend): last_time = datetime.datetime(2012, 1, 16, 22, 43) tz = pytz.timezone("US/Pacific") last_time = tz.localize(last_time) utc_last = datetime.datetime(2012, 1, 17, 6, 43) next_time = datetime.datetime(2022, 1, 17, 6, 43) private_key = RSA_KEY_2048.private_key(backend) builder = x509.CertificateRevocationListBuilder().issuer_name( x509.Name([ x509.NameAttribute(NameOID.COMMON_NAME, u"cryptography.io CA") ]) ).last_update(last_time).next_update(next_time) crl = builder.sign(private_key, hashes.SHA256(), backend) assert crl.last_update == utc_last def test_last_update_invalid(self): builder = x509.CertificateRevocationListBuilder() with pytest.raises(TypeError): builder.last_update("notadatetime") def test_last_update_before_unix_epoch(self): builder = x509.CertificateRevocationListBuilder() with pytest.raises(ValueError): builder.last_update(datetime.datetime(1960, 8, 10)) def test_set_last_update_twice(self): builder = x509.CertificateRevocationListBuilder().last_update( datetime.datetime(2002, 1, 1, 12, 1) ) with pytest.raises(ValueError): builder.last_update(datetime.datetime(2002, 1, 1, 12, 1)) @pytest.mark.requires_backend_interface(interface=RSABackend) @pytest.mark.requires_backend_interface(interface=X509Backend) def test_aware_next_update(self, backend): next_time = datetime.datetime(2022, 1, 16, 22, 43) tz = pytz.timezone("US/Pacific") next_time = tz.localize(next_time) utc_next = datetime.datetime(2022, 1, 17, 6, 43) last_time = datetime.datetime(2012, 1, 17, 6, 43) private_key = RSA_KEY_2048.private_key(backend) builder = x509.CertificateRevocationListBuilder().issuer_name( x509.Name([ x509.NameAttribute(NameOID.COMMON_NAME, u"cryptography.io CA") ]) ).last_update(last_time).next_update(next_time) crl = builder.sign(private_key, hashes.SHA256(), backend) assert crl.next_update == utc_next def test_next_update_invalid(self): builder = x509.CertificateRevocationListBuilder() with pytest.raises(TypeError): builder.next_update("notadatetime") def test_next_update_before_unix_epoch(self): builder = x509.CertificateRevocationListBuilder() with pytest.raises(ValueError): builder.next_update(datetime.datetime(1960, 8, 10)) def test_set_next_update_twice(self): builder = x509.CertificateRevocationListBuilder().next_update( datetime.datetime(2002, 1, 1, 12, 1) ) with pytest.raises(ValueError): builder.next_update(datetime.datetime(2002, 1, 1, 12, 1)) def test_last_update_after_next_update(self): builder = x509.CertificateRevocationListBuilder() builder = builder.next_update( datetime.datetime(2002, 1, 1, 12, 1) ) with pytest.raises(ValueError): builder.last_update(datetime.datetime(2003, 1, 1, 12, 1)) def test_next_update_after_last_update(self): builder = x509.CertificateRevocationListBuilder() builder = builder.last_update( datetime.datetime(2002, 1, 1, 12, 1) ) with pytest.raises(ValueError): builder.next_update(datetime.datetime(2001, 1, 1, 12, 1)) def test_add_extension_checks_for_duplicates(self): builder = x509.CertificateRevocationListBuilder().add_extension( x509.CRLNumber(1), False ) with pytest.raises(ValueError): builder.add_extension(x509.CRLNumber(2), False) def test_add_invalid_extension(self): builder = x509.CertificateRevocationListBuilder() with pytest.raises(TypeError): builder.add_extension( object(), False ) def test_add_invalid_revoked_certificate(self): builder = x509.CertificateRevocationListBuilder() with pytest.raises(TypeError): builder.add_revoked_certificate(object()) @pytest.mark.requires_backend_interface(interface=RSABackend) @pytest.mark.requires_backend_interface(interface=X509Backend) def test_no_issuer_name(self, backend): private_key = RSA_KEY_2048.private_key(backend) builder = x509.CertificateRevocationListBuilder().last_update( datetime.datetime(2002, 1, 1, 12, 1) ).next_update( datetime.datetime(2030, 1, 1, 12, 1) ) with pytest.raises(ValueError): builder.sign(private_key, hashes.SHA256(), backend) @pytest.mark.requires_backend_interface(interface=RSABackend) @pytest.mark.requires_backend_interface(interface=X509Backend) def test_no_last_update(self, backend): private_key = RSA_KEY_2048.private_key(backend) builder = x509.CertificateRevocationListBuilder().issuer_name( x509.Name([x509.NameAttribute(NameOID.COUNTRY_NAME, u'US')]) ).next_update( datetime.datetime(2030, 1, 1, 12, 1) ) with pytest.raises(ValueError): builder.sign(private_key, hashes.SHA256(), backend) @pytest.mark.requires_backend_interface(interface=RSABackend) @pytest.mark.requires_backend_interface(interface=X509Backend) def test_no_next_update(self, backend): private_key = RSA_KEY_2048.private_key(backend) builder = x509.CertificateRevocationListBuilder().issuer_name( x509.Name([x509.NameAttribute(NameOID.COUNTRY_NAME, u'US')]) ).last_update( datetime.datetime(2030, 1, 1, 12, 1) ) with pytest.raises(ValueError): builder.sign(private_key, hashes.SHA256(), backend) @pytest.mark.requires_backend_interface(interface=RSABackend) @pytest.mark.requires_backend_interface(interface=X509Backend) def test_sign_empty_list(self, backend): private_key = RSA_KEY_2048.private_key(backend) last_update = datetime.datetime(2002, 1, 1, 12, 1) next_update = datetime.datetime(2030, 1, 1, 12, 1) builder = x509.CertificateRevocationListBuilder().issuer_name( x509.Name([ x509.NameAttribute(NameOID.COMMON_NAME, u"cryptography.io CA") ]) ).last_update(last_update).next_update(next_update) crl = builder.sign(private_key, hashes.SHA256(), backend) assert len(crl) == 0 assert crl.last_update == last_update assert crl.next_update == next_update @pytest.mark.parametrize( "extension", [ x509.CRLNumber(13), x509.AuthorityKeyIdentifier( b"\xc3\x9c\xf3\xfc\xd3F\x084\xbb\xceF\x7f\xa0|[\xf3\xe2\x08" b"\xcbY", None, None ), x509.AuthorityInformationAccess([ x509.AccessDescription( AuthorityInformationAccessOID.CA_ISSUERS, x509.DNSName(u"cryptography.io") ) ]), x509.IssuerAlternativeName([ x509.UniformResourceIdentifier(u"https://cryptography.io"), ]) ] ) @pytest.mark.requires_backend_interface(interface=RSABackend) @pytest.mark.requires_backend_interface(interface=X509Backend) def test_sign_extensions(self, backend, extension): private_key = RSA_KEY_2048.private_key(backend) last_update = datetime.datetime(2002, 1, 1, 12, 1) next_update = datetime.datetime(2030, 1, 1, 12, 1) builder = x509.CertificateRevocationListBuilder().issuer_name( x509.Name([ x509.NameAttribute(NameOID.COMMON_NAME, u"cryptography.io CA") ]) ).last_update( last_update ).next_update( next_update ).add_extension( extension, False ) crl = builder.sign(private_key, hashes.SHA256(), backend) assert len(crl) == 0 assert len(crl.extensions) == 1 ext = crl.extensions.get_extension_for_class(type(extension)) assert ext.critical is False assert ext.value == extension @pytest.mark.requires_backend_interface(interface=RSABackend) @pytest.mark.requires_backend_interface(interface=X509Backend) def test_sign_multiple_extensions_critical(self, backend): private_key = RSA_KEY_2048.private_key(backend) last_update = datetime.datetime(2002, 1, 1, 12, 1) next_update = datetime.datetime(2030, 1, 1, 12, 1) ian = x509.IssuerAlternativeName([ x509.UniformResourceIdentifier(u"https://cryptography.io"), ]) crl_number = x509.CRLNumber(13) builder = x509.CertificateRevocationListBuilder().issuer_name( x509.Name([ x509.NameAttribute(NameOID.COMMON_NAME, u"cryptography.io CA") ]) ).last_update( last_update ).next_update( next_update ).add_extension( crl_number, False ).add_extension( ian, True ) crl = builder.sign(private_key, hashes.SHA256(), backend) assert len(crl) == 0 assert len(crl.extensions) == 2 ext1 = crl.extensions.get_extension_for_class(x509.CRLNumber) assert ext1.critical is False assert ext1.value == crl_number ext2 = crl.extensions.get_extension_for_class( x509.IssuerAlternativeName ) assert ext2.critical is True assert ext2.value == ian @pytest.mark.requires_backend_interface(interface=RSABackend) @pytest.mark.requires_backend_interface(interface=X509Backend) def test_add_unsupported_extension(self, backend): private_key = RSA_KEY_2048.private_key(backend) last_update = datetime.datetime(2002, 1, 1, 12, 1) next_update = datetime.datetime(2030, 1, 1, 12, 1) builder = x509.CertificateRevocationListBuilder().issuer_name( x509.Name([ x509.NameAttribute(NameOID.COMMON_NAME, u"cryptography.io CA") ]) ).last_update( last_update ).next_update( next_update ).add_extension( x509.OCSPNoCheck(), False ) with pytest.raises(NotImplementedError): builder.sign(private_key, hashes.SHA256(), backend) @pytest.mark.requires_backend_interface(interface=RSABackend) @pytest.mark.requires_backend_interface(interface=X509Backend) def test_sign_rsa_key_too_small(self, backend): private_key = RSA_KEY_512.private_key(backend) last_update = datetime.datetime(2002, 1, 1, 12, 1) next_update = datetime.datetime(2030, 1, 1, 12, 1) builder = x509.CertificateRevocationListBuilder().issuer_name( x509.Name([ x509.NameAttribute(NameOID.COMMON_NAME, u"cryptography.io CA") ]) ).last_update( last_update ).next_update( next_update ) with pytest.raises(ValueError): builder.sign(private_key, hashes.SHA512(), backend) @pytest.mark.requires_backend_interface(interface=RSABackend) @pytest.mark.requires_backend_interface(interface=X509Backend) def test_sign_with_invalid_hash(self, backend): private_key = RSA_KEY_2048.private_key(backend) last_update = datetime.datetime(2002, 1, 1, 12, 1) next_update = datetime.datetime(2030, 1, 1, 12, 1) builder = x509.CertificateRevocationListBuilder().issuer_name( x509.Name([ x509.NameAttribute(NameOID.COMMON_NAME, u"cryptography.io CA") ]) ).last_update( last_update ).next_update( next_update ) with pytest.raises(TypeError): builder.sign(private_key, object(), backend) @pytest.mark.requires_backend_interface(interface=DSABackend) @pytest.mark.requires_backend_interface(interface=X509Backend) def test_sign_dsa_key(self, backend): private_key = DSA_KEY_2048.private_key(backend) invalidity_date = x509.InvalidityDate( datetime.datetime(2002, 1, 1, 0, 0) ) ian = x509.IssuerAlternativeName([ x509.UniformResourceIdentifier(u"https://cryptography.io"), ]) revoked_cert0 = x509.RevokedCertificateBuilder().serial_number( 2 ).revocation_date( datetime.datetime(2012, 1, 1, 1, 1) ).add_extension( invalidity_date, False ).build(backend) last_update = datetime.datetime(2002, 1, 1, 12, 1) next_update = datetime.datetime(2030, 1, 1, 12, 1) builder = x509.CertificateRevocationListBuilder().issuer_name( x509.Name([ x509.NameAttribute(NameOID.COMMON_NAME, u"cryptography.io CA") ]) ).last_update( last_update ).next_update( next_update ).add_revoked_certificate( revoked_cert0 ).add_extension( ian, False ) crl = builder.sign(private_key, hashes.SHA256(), backend) assert crl.extensions.get_extension_for_class( x509.IssuerAlternativeName ).value == ian assert crl[0].serial_number == revoked_cert0.serial_number assert crl[0].revocation_date == revoked_cert0.revocation_date assert len(crl[0].extensions) == 1 ext = crl[0].extensions.get_extension_for_class(x509.InvalidityDate) assert ext.critical is False assert ext.value == invalidity_date @pytest.mark.requires_backend_interface(interface=EllipticCurveBackend) @pytest.mark.requires_backend_interface(interface=X509Backend) def test_sign_ec_key(self, backend): _skip_curve_unsupported(backend, ec.SECP256R1()) private_key = ec.generate_private_key(ec.SECP256R1(), backend) invalidity_date = x509.InvalidityDate( datetime.datetime(2002, 1, 1, 0, 0) ) ian = x509.IssuerAlternativeName([ x509.UniformResourceIdentifier(u"https://cryptography.io"), ]) revoked_cert0 = x509.RevokedCertificateBuilder().serial_number( 2 ).revocation_date( datetime.datetime(2012, 1, 1, 1, 1) ).add_extension( invalidity_date, False ).build(backend) last_update = datetime.datetime(2002, 1, 1, 12, 1) next_update = datetime.datetime(2030, 1, 1, 12, 1) builder = x509.CertificateRevocationListBuilder().issuer_name( x509.Name([ x509.NameAttribute(NameOID.COMMON_NAME, u"cryptography.io CA") ]) ).last_update( last_update ).next_update( next_update ).add_revoked_certificate( revoked_cert0 ).add_extension( ian, False ) crl = builder.sign(private_key, hashes.SHA256(), backend) assert crl.extensions.get_extension_for_class( x509.IssuerAlternativeName ).value == ian assert crl[0].serial_number == revoked_cert0.serial_number assert crl[0].revocation_date == revoked_cert0.revocation_date assert len(crl[0].extensions) == 1 ext = crl[0].extensions.get_extension_for_class(x509.InvalidityDate) assert ext.critical is False assert ext.value == invalidity_date @pytest.mark.requires_backend_interface(interface=DSABackend) @pytest.mark.requires_backend_interface(interface=X509Backend) def test_dsa_key_sign_md5(self, backend): private_key = DSA_KEY_2048.private_key(backend) last_time = datetime.datetime(2012, 1, 16, 22, 43) next_time = datetime.datetime(2022, 1, 17, 6, 43) builder = x509.CertificateRevocationListBuilder().issuer_name( x509.Name([ x509.NameAttribute(NameOID.COMMON_NAME, u"cryptography.io CA") ]) ).last_update(last_time).next_update(next_time) with pytest.raises(ValueError): builder.sign(private_key, hashes.MD5(), backend) @pytest.mark.requires_backend_interface(interface=EllipticCurveBackend) @pytest.mark.requires_backend_interface(interface=X509Backend) def test_ec_key_sign_md5(self, backend): _skip_curve_unsupported(backend, ec.SECP256R1()) private_key = EC_KEY_SECP256R1.private_key(backend) last_time = datetime.datetime(2012, 1, 16, 22, 43) next_time = datetime.datetime(2022, 1, 17, 6, 43) builder = x509.CertificateRevocationListBuilder().issuer_name( x509.Name([ x509.NameAttribute(NameOID.COMMON_NAME, u"cryptography.io CA") ]) ).last_update(last_time).next_update(next_time) with pytest.raises(ValueError): builder.sign(private_key, hashes.MD5(), backend) @pytest.mark.requires_backend_interface(interface=RSABackend) @pytest.mark.requires_backend_interface(interface=X509Backend) def test_sign_with_revoked_certificates(self, backend): private_key = RSA_KEY_2048.private_key(backend) last_update = datetime.datetime(2002, 1, 1, 12, 1) next_update = datetime.datetime(2030, 1, 1, 12, 1) invalidity_date = x509.InvalidityDate( datetime.datetime(2002, 1, 1, 0, 0) ) revoked_cert0 = x509.RevokedCertificateBuilder().serial_number( 38 ).revocation_date( datetime.datetime(2011, 1, 1, 1, 1) ).build(backend) revoked_cert1 = x509.RevokedCertificateBuilder().serial_number( 2 ).revocation_date( datetime.datetime(2012, 1, 1, 1, 1) ).add_extension( invalidity_date, False ).build(backend) builder = x509.CertificateRevocationListBuilder().issuer_name( x509.Name([ x509.NameAttribute(NameOID.COMMON_NAME, u"cryptography.io CA") ]) ).last_update( last_update ).next_update( next_update ).add_revoked_certificate( revoked_cert0 ).add_revoked_certificate( revoked_cert1 ) crl = builder.sign(private_key, hashes.SHA256(), backend) assert len(crl) == 2 assert crl.last_update == last_update assert crl.next_update == next_update assert crl[0].serial_number == revoked_cert0.serial_number assert crl[0].revocation_date == revoked_cert0.revocation_date assert len(crl[0].extensions) == 0 assert crl[1].serial_number == revoked_cert1.serial_number assert crl[1].revocation_date == revoked_cert1.revocation_date assert len(crl[1].extensions) == 1 ext = crl[1].extensions.get_extension_for_class(x509.InvalidityDate) assert ext.critical is False assert ext.value == invalidity_date
class TestCertificateRevocationListBuilder: def test_issuer_name_invalid(self): builder = x509.CertificateRevocationListBuilder() with pytest.raises(TypeError): builder.issuer_name("notanx509name") # type:ignore[arg-type] def test_set_issuer_name_twice(self): builder = x509.CertificateRevocationListBuilder().issuer_name( x509.Name([x509.NameAttribute(NameOID.COUNTRY_NAME, "US")])) with pytest.raises(ValueError): builder.issuer_name( x509.Name([x509.NameAttribute(NameOID.COUNTRY_NAME, "US")])) def test_aware_last_update(self, backend): last_time = datetime.datetime(2012, 1, 16, 22, 43) tz = pytz.timezone("US/Pacific") last_time = tz.localize(last_time) utc_last = datetime.datetime(2012, 1, 17, 6, 43) next_time = datetime.datetime(2022, 1, 17, 6, 43) private_key = RSA_KEY_2048.private_key(backend) builder = (x509.CertificateRevocationListBuilder().issuer_name( x509.Name([ x509.NameAttribute(NameOID.COMMON_NAME, "cryptography.io CA") ])).last_update(last_time).next_update(next_time)) crl = builder.sign(private_key, hashes.SHA256(), backend) assert crl.last_update == utc_last def test_last_update_invalid(self): builder = x509.CertificateRevocationListBuilder() with pytest.raises(TypeError): builder.last_update("notadatetime") # type:ignore[arg-type] def test_last_update_before_1950(self): builder = x509.CertificateRevocationListBuilder() with pytest.raises(ValueError): builder.last_update(datetime.datetime(1940, 8, 10)) def test_set_last_update_twice(self): builder = x509.CertificateRevocationListBuilder().last_update( datetime.datetime(2002, 1, 1, 12, 1)) with pytest.raises(ValueError): builder.last_update(datetime.datetime(2002, 1, 1, 12, 1)) def test_aware_next_update(self, backend): next_time = datetime.datetime(2022, 1, 16, 22, 43) tz = pytz.timezone("US/Pacific") next_time = tz.localize(next_time) utc_next = datetime.datetime(2022, 1, 17, 6, 43) last_time = datetime.datetime(2012, 1, 17, 6, 43) private_key = RSA_KEY_2048.private_key(backend) builder = (x509.CertificateRevocationListBuilder().issuer_name( x509.Name([ x509.NameAttribute(NameOID.COMMON_NAME, "cryptography.io CA") ])).last_update(last_time).next_update(next_time)) crl = builder.sign(private_key, hashes.SHA256(), backend) assert crl.next_update == utc_next def test_next_update_invalid(self): builder = x509.CertificateRevocationListBuilder() with pytest.raises(TypeError): builder.next_update("notadatetime") # type:ignore[arg-type] def test_next_update_before_1950(self): builder = x509.CertificateRevocationListBuilder() with pytest.raises(ValueError): builder.next_update(datetime.datetime(1940, 8, 10)) def test_set_next_update_twice(self): builder = x509.CertificateRevocationListBuilder().next_update( datetime.datetime(2002, 1, 1, 12, 1)) with pytest.raises(ValueError): builder.next_update(datetime.datetime(2002, 1, 1, 12, 1)) def test_last_update_after_next_update(self): builder = x509.CertificateRevocationListBuilder() builder = builder.next_update(datetime.datetime(2002, 1, 1, 12, 1)) with pytest.raises(ValueError): builder.last_update(datetime.datetime(2003, 1, 1, 12, 1)) def test_next_update_after_last_update(self): builder = x509.CertificateRevocationListBuilder() builder = builder.last_update(datetime.datetime(2002, 1, 1, 12, 1)) with pytest.raises(ValueError): builder.next_update(datetime.datetime(2001, 1, 1, 12, 1)) def test_add_extension_checks_for_duplicates(self): builder = x509.CertificateRevocationListBuilder().add_extension( x509.CRLNumber(1), False) with pytest.raises(ValueError): builder.add_extension(x509.CRLNumber(2), False) def test_add_invalid_extension(self): builder = x509.CertificateRevocationListBuilder() with pytest.raises(TypeError): builder.add_extension(object(), False) # type:ignore[arg-type] def test_add_invalid_revoked_certificate(self): builder = x509.CertificateRevocationListBuilder() with pytest.raises(TypeError): builder.add_revoked_certificate(object()) # type:ignore[arg-type] def test_no_issuer_name(self, backend): private_key = RSA_KEY_2048.private_key(backend) builder = (x509.CertificateRevocationListBuilder().last_update( datetime.datetime(2002, 1, 1, 12, 1)).next_update( datetime.datetime(2030, 1, 1, 12, 1))) with pytest.raises(ValueError): builder.sign(private_key, hashes.SHA256(), backend) def test_no_last_update(self, backend): private_key = RSA_KEY_2048.private_key(backend) builder = (x509.CertificateRevocationListBuilder().issuer_name( x509.Name([x509.NameAttribute(NameOID.COUNTRY_NAME, "US") ])).next_update(datetime.datetime(2030, 1, 1, 12, 1))) with pytest.raises(ValueError): builder.sign(private_key, hashes.SHA256(), backend) def test_no_next_update(self, backend): private_key = RSA_KEY_2048.private_key(backend) builder = (x509.CertificateRevocationListBuilder().issuer_name( x509.Name([x509.NameAttribute(NameOID.COUNTRY_NAME, "US") ])).last_update(datetime.datetime(2030, 1, 1, 12, 1))) with pytest.raises(ValueError): builder.sign(private_key, hashes.SHA256(), backend) def test_sign_empty_list(self, backend): private_key = RSA_KEY_2048.private_key(backend) last_update = datetime.datetime(2002, 1, 1, 12, 1) next_update = datetime.datetime(2030, 1, 1, 12, 1) builder = (x509.CertificateRevocationListBuilder().issuer_name( x509.Name([ x509.NameAttribute(NameOID.COMMON_NAME, "cryptography.io CA") ])).last_update(last_update).next_update(next_update)) crl = builder.sign(private_key, hashes.SHA256(), backend) assert len(crl) == 0 assert crl.last_update == last_update assert crl.next_update == next_update @pytest.mark.parametrize( "extension", [ x509.CRLNumber(13), x509.DeltaCRLIndicator(12345678901234567890), x509.AuthorityKeyIdentifier( b"\xc3\x9c\xf3\xfc\xd3F\x084\xbb\xceF\x7f\xa0|[\xf3\xe2\x08" b"\xcbY", None, None, ), x509.AuthorityInformationAccess([ x509.AccessDescription( AuthorityInformationAccessOID.CA_ISSUERS, x509.DNSName("cryptography.io"), ) ]), x509.IssuerAlternativeName( [x509.UniformResourceIdentifier("https://cryptography.io")]), ], ) def test_sign_extensions(self, backend, extension): private_key = RSA_KEY_2048.private_key(backend) last_update = datetime.datetime(2002, 1, 1, 12, 1) next_update = datetime.datetime(2030, 1, 1, 12, 1) builder = (x509.CertificateRevocationListBuilder().issuer_name( x509.Name([ x509.NameAttribute(NameOID.COMMON_NAME, "cryptography.io CA") ])).last_update(last_update).next_update( next_update).add_extension(extension, False)) crl = builder.sign(private_key, hashes.SHA256(), backend) assert len(crl) == 0 assert len(crl.extensions) == 1 ext = crl.extensions.get_extension_for_class(type(extension)) assert ext.critical is False assert ext.value == extension def test_sign_multiple_extensions_critical(self, backend): private_key = RSA_KEY_2048.private_key(backend) last_update = datetime.datetime(2002, 1, 1, 12, 1) next_update = datetime.datetime(2030, 1, 1, 12, 1) ian = x509.IssuerAlternativeName( [x509.UniformResourceIdentifier("https://cryptography.io")]) crl_number = x509.CRLNumber(13) builder = (x509.CertificateRevocationListBuilder().issuer_name( x509.Name([ x509.NameAttribute(NameOID.COMMON_NAME, "cryptography.io CA") ])).last_update(last_update).next_update( next_update).add_extension(crl_number, False).add_extension(ian, True)) crl = builder.sign(private_key, hashes.SHA256(), backend) assert len(crl) == 0 assert len(crl.extensions) == 2 ext1 = crl.extensions.get_extension_for_class(x509.CRLNumber) assert ext1.critical is False assert ext1.value == crl_number ext2 = crl.extensions.get_extension_for_class( x509.IssuerAlternativeName) assert ext2.critical is True assert ext2.value == ian def test_freshestcrl_extension(self, backend): private_key = RSA_KEY_2048.private_key(backend) last_update = datetime.datetime(2002, 1, 1, 12, 1) next_update = datetime.datetime(2030, 1, 1, 12, 1) freshest = x509.FreshestCRL([ x509.DistributionPoint( [x509.UniformResourceIdentifier("http://d.om/delta")], None, None, None, ) ]) builder = (x509.CertificateRevocationListBuilder().issuer_name( x509.Name([ x509.NameAttribute(NameOID.COMMON_NAME, "cryptography.io CA") ])).last_update(last_update).next_update( next_update).add_extension(freshest, False)) crl = builder.sign(private_key, hashes.SHA256(), backend) assert len(crl) == 0 assert len(crl.extensions) == 1 ext1 = crl.extensions.get_extension_for_class(x509.FreshestCRL) assert ext1.critical is False assert isinstance(ext1.value, x509.FreshestCRL) assert isinstance(ext1.value[0], x509.DistributionPoint) assert ext1.value[0].full_name is not None uri = ext1.value[0].full_name[0] assert isinstance(uri, x509.UniformResourceIdentifier) assert uri.value == "http://d.om/delta" def test_add_unsupported_extension(self, backend): private_key = RSA_KEY_2048.private_key(backend) last_update = datetime.datetime(2002, 1, 1, 12, 1) next_update = datetime.datetime(2030, 1, 1, 12, 1) builder = (x509.CertificateRevocationListBuilder().issuer_name( x509.Name([ x509.NameAttribute(NameOID.COMMON_NAME, "cryptography.io CA") ])).last_update(last_update).next_update( next_update).add_extension(DummyExtension(), False)) with pytest.raises(NotImplementedError): builder.sign(private_key, hashes.SHA256(), backend) def test_add_unsupported_entry_extension(self, backend): private_key = RSA_KEY_2048.private_key(backend) last_update = datetime.datetime(2002, 1, 1, 12, 1) next_update = datetime.datetime(2030, 1, 1, 12, 1) builder = (x509.CertificateRevocationListBuilder().issuer_name( x509.Name([ x509.NameAttribute(NameOID.COMMON_NAME, "cryptography.io CA") ])).last_update(last_update).next_update( next_update).add_revoked_certificate( x509.RevokedCertificateBuilder().serial_number( 1234).revocation_date( datetime.datetime.utcnow()).add_extension( DummyExtension(), critical=False).build())) with pytest.raises(NotImplementedError): builder.sign(private_key, hashes.SHA256(), backend) def test_sign_rsa_key_too_small(self, backend): private_key = RSA_KEY_512.private_key(backend) last_update = datetime.datetime(2002, 1, 1, 12, 1) next_update = datetime.datetime(2030, 1, 1, 12, 1) builder = (x509.CertificateRevocationListBuilder().issuer_name( x509.Name([ x509.NameAttribute(NameOID.COMMON_NAME, "cryptography.io CA") ])).last_update(last_update).next_update(next_update)) with pytest.raises(ValueError): builder.sign(private_key, hashes.SHA512(), backend) def test_sign_with_invalid_hash(self, backend): private_key = RSA_KEY_2048.private_key(backend) last_update = datetime.datetime(2002, 1, 1, 12, 1) next_update = datetime.datetime(2030, 1, 1, 12, 1) builder = (x509.CertificateRevocationListBuilder().issuer_name( x509.Name([ x509.NameAttribute(NameOID.COMMON_NAME, "cryptography.io CA") ])).last_update(last_update).next_update(next_update)) with pytest.raises(TypeError): builder.sign( private_key, object(), backend # type: ignore[arg-type] ) @pytest.mark.supported( only_if=lambda backend: backend.ed25519_supported(), skip_message="Requires OpenSSL with Ed25519 support", ) def test_sign_with_invalid_hash_ed25519(self, backend): private_key = ed25519.Ed25519PrivateKey.generate() last_update = datetime.datetime(2002, 1, 1, 12, 1) next_update = datetime.datetime(2030, 1, 1, 12, 1) builder = (x509.CertificateRevocationListBuilder().issuer_name( x509.Name([ x509.NameAttribute(NameOID.COMMON_NAME, "cryptography.io CA") ])).last_update(last_update).next_update(next_update)) with pytest.raises(TypeError): builder.sign( private_key, object(), # type:ignore[arg-type] backend, ) with pytest.raises(ValueError): builder.sign(private_key, hashes.SHA256(), backend) @pytest.mark.supported( only_if=lambda backend: backend.ed448_supported(), skip_message="Requires OpenSSL with Ed448 support", ) def test_sign_with_invalid_hash_ed448(self, backend): private_key = ed448.Ed448PrivateKey.generate() last_update = datetime.datetime(2002, 1, 1, 12, 1) next_update = datetime.datetime(2030, 1, 1, 12, 1) builder = (x509.CertificateRevocationListBuilder().issuer_name( x509.Name([ x509.NameAttribute(NameOID.COMMON_NAME, "cryptography.io CA") ])).last_update(last_update).next_update(next_update)) with pytest.raises(TypeError): builder.sign( private_key, object(), # type:ignore[arg-type] backend, ) with pytest.raises(ValueError): builder.sign(private_key, hashes.SHA256(), backend) def test_sign_dsa_key(self, backend): private_key = DSA_KEY_2048.private_key(backend) invalidity_date = x509.InvalidityDate( datetime.datetime(2002, 1, 1, 0, 0)) ian = x509.IssuerAlternativeName( [x509.UniformResourceIdentifier("https://cryptography.io")]) revoked_cert0 = ( x509.RevokedCertificateBuilder().serial_number(2).revocation_date( datetime.datetime(2012, 1, 1, 1, 1)).add_extension(invalidity_date, False).build(backend)) last_update = datetime.datetime(2002, 1, 1, 12, 1) next_update = datetime.datetime(2030, 1, 1, 12, 1) builder = (x509.CertificateRevocationListBuilder().issuer_name( x509.Name([ x509.NameAttribute(NameOID.COMMON_NAME, "cryptography.io CA") ])).last_update(last_update).next_update(next_update). add_revoked_certificate(revoked_cert0).add_extension( ian, False)) crl = builder.sign(private_key, hashes.SHA256(), backend) assert (crl.extensions.get_extension_for_class( x509.IssuerAlternativeName).value == ian) assert crl[0].serial_number == revoked_cert0.serial_number assert crl[0].revocation_date == revoked_cert0.revocation_date assert len(crl[0].extensions) == 1 ext = crl[0].extensions.get_extension_for_class(x509.InvalidityDate) assert ext.critical is False assert ext.value == invalidity_date def test_sign_ec_key(self, backend): _skip_curve_unsupported(backend, ec.SECP256R1()) private_key = ec.generate_private_key(ec.SECP256R1(), backend) invalidity_date = x509.InvalidityDate( datetime.datetime(2002, 1, 1, 0, 0)) ian = x509.IssuerAlternativeName( [x509.UniformResourceIdentifier("https://cryptography.io")]) revoked_cert0 = ( x509.RevokedCertificateBuilder().serial_number(2).revocation_date( datetime.datetime(2012, 1, 1, 1, 1)).add_extension(invalidity_date, False).build(backend)) last_update = datetime.datetime(2002, 1, 1, 12, 1) next_update = datetime.datetime(2030, 1, 1, 12, 1) builder = (x509.CertificateRevocationListBuilder().issuer_name( x509.Name([ x509.NameAttribute(NameOID.COMMON_NAME, "cryptography.io CA") ])).last_update(last_update).next_update(next_update). add_revoked_certificate(revoked_cert0).add_extension( ian, False)) crl = builder.sign(private_key, hashes.SHA256(), backend) assert (crl.extensions.get_extension_for_class( x509.IssuerAlternativeName).value == ian) assert crl[0].serial_number == revoked_cert0.serial_number assert crl[0].revocation_date == revoked_cert0.revocation_date assert len(crl[0].extensions) == 1 ext = crl[0].extensions.get_extension_for_class(x509.InvalidityDate) assert ext.critical is False assert ext.value == invalidity_date @pytest.mark.supported( only_if=lambda backend: backend.ed25519_supported(), skip_message="Requires OpenSSL with Ed25519 support", ) def test_sign_ed25519_key(self, backend): private_key = ed25519.Ed25519PrivateKey.generate() invalidity_date = x509.InvalidityDate( datetime.datetime(2002, 1, 1, 0, 0)) ian = x509.IssuerAlternativeName( [x509.UniformResourceIdentifier("https://cryptography.io")]) revoked_cert0 = ( x509.RevokedCertificateBuilder().serial_number(2).revocation_date( datetime.datetime(2012, 1, 1, 1, 1)).add_extension(invalidity_date, False).build(backend)) last_update = datetime.datetime(2002, 1, 1, 12, 1) next_update = datetime.datetime(2030, 1, 1, 12, 1) builder = (x509.CertificateRevocationListBuilder().issuer_name( x509.Name([ x509.NameAttribute(NameOID.COMMON_NAME, "cryptography.io CA") ])).last_update(last_update).next_update(next_update). add_revoked_certificate(revoked_cert0).add_extension( ian, False)) crl = builder.sign(private_key, None, backend) assert crl.signature_hash_algorithm is None assert crl.signature_algorithm_oid == SignatureAlgorithmOID.ED25519 assert (crl.extensions.get_extension_for_class( x509.IssuerAlternativeName).value == ian) assert crl[0].serial_number == revoked_cert0.serial_number assert crl[0].revocation_date == revoked_cert0.revocation_date assert len(crl[0].extensions) == 1 ext = crl[0].extensions.get_extension_for_class(x509.InvalidityDate) assert ext.critical is False assert ext.value == invalidity_date @pytest.mark.supported( only_if=lambda backend: backend.ed448_supported(), skip_message="Requires OpenSSL with Ed448 support", ) def test_sign_ed448_key(self, backend): private_key = ed448.Ed448PrivateKey.generate() invalidity_date = x509.InvalidityDate( datetime.datetime(2002, 1, 1, 0, 0)) ian = x509.IssuerAlternativeName( [x509.UniformResourceIdentifier("https://cryptography.io")]) revoked_cert0 = ( x509.RevokedCertificateBuilder().serial_number(2).revocation_date( datetime.datetime(2012, 1, 1, 1, 1)).add_extension(invalidity_date, False).build(backend)) last_update = datetime.datetime(2002, 1, 1, 12, 1) next_update = datetime.datetime(2030, 1, 1, 12, 1) builder = (x509.CertificateRevocationListBuilder().issuer_name( x509.Name([ x509.NameAttribute(NameOID.COMMON_NAME, "cryptography.io CA") ])).last_update(last_update).next_update(next_update). add_revoked_certificate(revoked_cert0).add_extension( ian, False)) crl = builder.sign(private_key, None, backend) assert crl.signature_hash_algorithm is None assert crl.signature_algorithm_oid == SignatureAlgorithmOID.ED448 assert (crl.extensions.get_extension_for_class( x509.IssuerAlternativeName).value == ian) assert crl[0].serial_number == revoked_cert0.serial_number assert crl[0].revocation_date == revoked_cert0.revocation_date assert len(crl[0].extensions) == 1 ext = crl[0].extensions.get_extension_for_class(x509.InvalidityDate) assert ext.critical is False assert ext.value == invalidity_date def test_dsa_key_sign_md5(self, backend): private_key = DSA_KEY_2048.private_key(backend) last_time = datetime.datetime(2012, 1, 16, 22, 43) next_time = datetime.datetime(2022, 1, 17, 6, 43) builder = (x509.CertificateRevocationListBuilder().issuer_name( x509.Name([ x509.NameAttribute(NameOID.COMMON_NAME, "cryptography.io CA") ])).last_update(last_time).next_update(next_time)) with pytest.raises(ValueError): builder.sign(private_key, hashes.MD5(), backend) def test_ec_key_sign_md5(self, backend): _skip_curve_unsupported(backend, ec.SECP256R1()) private_key = EC_KEY_SECP256R1.private_key(backend) last_time = datetime.datetime(2012, 1, 16, 22, 43) next_time = datetime.datetime(2022, 1, 17, 6, 43) builder = (x509.CertificateRevocationListBuilder().issuer_name( x509.Name([ x509.NameAttribute(NameOID.COMMON_NAME, "cryptography.io CA") ])).last_update(last_time).next_update(next_time)) with pytest.raises(ValueError): builder.sign(private_key, hashes.MD5(), backend) def test_sign_with_revoked_certificates(self, backend): private_key = RSA_KEY_2048.private_key(backend) last_update = datetime.datetime(2002, 1, 1, 12, 1) next_update = datetime.datetime(2030, 1, 1, 12, 1) invalidity_date = x509.InvalidityDate( datetime.datetime(2002, 1, 1, 0, 0)) revoked_cert0 = ( x509.RevokedCertificateBuilder().serial_number(38).revocation_date( datetime.datetime(2011, 1, 1, 1, 1)).build(backend)) revoked_cert1 = ( x509.RevokedCertificateBuilder().serial_number(2).revocation_date( datetime.datetime(2012, 1, 1, 1, 1)).add_extension( invalidity_date, False).add_extension( x509.CRLReason(x509.ReasonFlags.ca_compromise), False).build(backend)) ci = x509.CertificateIssuer([x509.DNSName("cryptography.io")]) revoked_cert2 = ( x509.RevokedCertificateBuilder().serial_number(40).revocation_date( datetime.datetime(2011, 1, 1, 1, 1)).add_extension(ci, False).build(backend)) builder = ( x509.CertificateRevocationListBuilder().issuer_name( x509.Name([ x509.NameAttribute(NameOID.COMMON_NAME, "cryptography.io CA") ])).last_update(last_update).next_update(next_update). add_revoked_certificate(revoked_cert0).add_revoked_certificate( revoked_cert1).add_revoked_certificate(revoked_cert2)) crl = builder.sign(private_key, hashes.SHA256(), backend) assert len(crl) == 3 assert crl.last_update == last_update assert crl.next_update == next_update assert crl[0].serial_number == revoked_cert0.serial_number assert crl[0].revocation_date == revoked_cert0.revocation_date assert len(crl[0].extensions) == 0 assert crl[1].serial_number == revoked_cert1.serial_number assert crl[1].revocation_date == revoked_cert1.revocation_date assert len(crl[1].extensions) == 2 ext = crl[1].extensions.get_extension_for_class(x509.InvalidityDate) assert ext.critical is False assert ext.value == invalidity_date assert (crl[2].extensions.get_extension_for_class( x509.CertificateIssuer).value == ci)
def parse_access_desc(desc): if desc['method'] == 'OCSP': method = x509.AuthorityInformationAccessOID.OCSP else: method = x509.AuthorityInformationAccessOID.CA_ISSUERS return x509.AccessDescription(method, parse_general_name(desc['value']))
def install_extensions(self, builder): """Add common extensions to Cert- or CSR builder. """ # BasicConstraints, critical if self.ca: ext = x509.BasicConstraints(ca=True, path_length=self.path_length) else: ext = x509.BasicConstraints(ca=False, path_length=None) builder = builder.add_extension(ext, critical=True) # KeyUsage, critical ku_args = {k: k in self.usage for k in KU_FIELDS} if self.ca: ku_args['key_cert_sign'] = True ku_args['crl_sign'] = True ext = make_key_usage(**ku_args) else: ku_args['digital_signature'] = True ku_args['key_encipherment'] = True ext = make_key_usage(**ku_args) builder = builder.add_extension(ext, critical=True) # ExtendedKeyUsage, critical xku = [x for x in self.usage if x not in KU_FIELDS] xku_bad = [x for x in xku if x not in XKU_CODE_TO_OID] if xku_bad: die("Unknown usage keywords: %s", ','.join(xku_bad)) if xku: xku_oids = [XKU_CODE_TO_OID[x] for x in xku] ext = x509.ExtendedKeyUsage(xku_oids) builder = builder.add_extension(ext, critical=True) # NameConstraints, critical if (self.exclude_subtrees or self.permit_subtrees) and self.ca: allow = self.load_gnames(self.permit_subtrees) or None disallow = self.load_gnames(self.exclude_subtrees) or None ext = x509.NameConstraints(allow, disallow) builder = builder.add_extension(ext, critical=True) # SubjectAlternativeName if self.san: ext = x509.SubjectAlternativeName(self.get_san_gnames()) builder = builder.add_extension(ext, critical=False) # CRLDistributionPoints if self.crl_urls: full_names = self.get_crl_gnames() reasons = None crl_issuer = None point = x509.DistributionPoint(full_names, None, reasons, crl_issuer) ext = x509.CRLDistributionPoints([point]) builder = builder.add_extension(ext, critical=False) # AuthorityInformationAccess if self.ocsp_urls or self.issuer_urls: oid = AuthorityInformationAccessOID.OCSP ocsp_list = [x509.AccessDescription(oid, gn) for gn in self.get_ocsp_gnames()] oid = AuthorityInformationAccessOID.CA_ISSUERS ca_list = [x509.AccessDescription(oid, gn) for gn in self.get_issuer_urls_gnames()] ext = x509.AuthorityInformationAccess(ocsp_list + ca_list) builder = builder.add_extension(ext, critical=False) # OCSPNoCheck if self.ocsp_nocheck: ext = x509.OCSPNoCheck() builder = builder.add_extension(ext, critical=False) # configured builder return builder
def create_ica_cert(self, issuer_private_key, private_key, outpath=None): subject_entries = [ (NameOID.COUNTRY_NAME, "US"), (NameOID.ORGANIZATION_NAME, "Intermediate CA"), (NameOID.COMMON_NAME, "Intermediate CA Name"), ] issuer_entries = [ (NameOID.COUNTRY_NAME, "US"), (NameOID.ORGANIZATION_NAME, "Root CA"), (NameOID.COMMON_NAME, "Root CA Name"), ] not_before = datetime.datetime.fromisoformat("2016-01-01 11:11:11") not_after = datetime.datetime.fromisoformat("2031-01-01 11:11:11") serial_number = x509.random_serial_number() aia_descriptions = [ x509.AccessDescription( x509.oid.AuthorityInformationAccessOID.OCSP, x509.UniformResourceIdentifier("http://ocsp.rootca.com")), x509.AccessDescription( x509.oid.AuthorityInformationAccessOID.CA_ISSUERS, x509.UniformResourceIdentifier( "http://sub.rootca.com/rca.crt")), ] # basic constraints basic_constraints_ca = True basic_constraints_pathlen = None certificate_policies = [ x509.PolicyInformation(x509.oid.CertificatePoliciesOID.ANY_POLICY, ["http://www.rootca.com/repo"]), ] crl_distribution_points = [ x509.DistributionPoint([ x509.UniformResourceIdentifier("http://crl.rootca.com/rca.crl") ], None, None, None) ] # key usage: Certificate Sign and CRL Sign set to True, rest set to False key_usages = 5 * [False] + 2 * [True] + 2 * [False] extensions = [ (x509.BasicConstraints(basic_constraints_ca, basic_constraints_pathlen), True), (x509.CertificatePolicies(policies=certificate_policies), False), (x509.AuthorityInformationAccess(descriptions=aia_descriptions), False), (x509.KeyUsage(*key_usages), True), (x509.AuthorityKeyIdentifier.from_issuer_public_key( issuer_private_key.public_key()), False), (x509.CRLDistributionPoints(crl_distribution_points), False), (x509.SubjectKeyIdentifier.from_public_key( private_key.public_key()), False), ] cert = self.create_cert(not_before, not_after, serial_number, issuer_private_key, private_key, subject_entries=subject_entries, issuer_entries=issuer_entries, extensions=extensions) self.save_to_pem(cert, outpath) return cert
def create_ca_issuer(url): return x509.AccessDescription( x509.AuthorityInformationAccessOID.CA_ISSUERS, x509.UniformResourceIdentifier(url))
def create_certificate(common_name, not_valid_before, not_valid_after, issuer_common_name=None, issuer_private_key=None, crl_distribution_point=None, authority_info_uri=None, is_ca_certificate=False): private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048, backend=default_backend()) public_key = private_key.public_key() if not issuer_common_name: issuer_common_name = common_name if not issuer_private_key: issuer_private_key = private_key is_ca_certificate = True builder = x509.CertificateBuilder() builder = builder.subject_name( x509.Name( [x509.NameAttribute(NameOID.COMMON_NAME, unicode(common_name))])) builder = builder.issuer_name( x509.Name([ x509.NameAttribute(NameOID.COMMON_NAME, unicode(issuer_common_name)) ])) builder = builder.not_valid_before(not_valid_before) builder = builder.not_valid_after(not_valid_after) builder = builder.serial_number(int(uuid.uuid4())) builder = builder.public_key(public_key) builder = builder.add_extension(x509.BasicConstraints(ca=is_ca_certificate, path_length=None), critical=True) if crl_distribution_point: builder = builder.add_extension(x509.CRLDistributionPoints([ x509.DistributionPoint(full_name=[ x509.UniformResourceIdentifier(unicode(crl_distribution_point)) ], relative_name=None, reasons=None, crl_issuer=None) ]), critical=True) if authority_info_uri: builder = builder.add_extension( x509.AuthorityInformationAccess([ x509.AccessDescription( access_method = \ x509.oid.AuthorityInformationAccessOID.CA_ISSUERS, access_location = x509.UniformResourceIdentifier( unicode(authority_info_uri)) ) ]), critical = True) certificate = builder.sign(private_key=issuer_private_key, algorithm=hashes.SHA256(), backend=default_backend()) return private_key, certificate
def create_sica_cert(self, issuer_private_key, private_key, outpath=None, ocsp_signing_eku=True): subject_entries = [ (NameOID.COUNTRY_NAME, "US"), (NameOID.ORGANIZATION_NAME, "My Company"), (NameOID.COMMON_NAME, "My Company Name"), ] issuer_entries = [ (NameOID.COUNTRY_NAME, "US"), (NameOID.ORGANIZATION_NAME, "Intermediate CA"), (NameOID.COMMON_NAME, "Intermediate CA Name"), ] not_before = datetime.datetime.fromisoformat("2016-02-02 10:10:10") not_after = datetime.datetime.fromisoformat("2022-02-02 10:10:10") serial_number = x509.random_serial_number() aia_descriptions = [ x509.AccessDescription( x509.oid.AuthorityInformationAccessOID.CA_ISSUERS, x509.UniformResourceIdentifier( "http://sub.rootca.com/ica.crt")), x509.AccessDescription( x509.oid.AuthorityInformationAccessOID.OCSP, x509.UniformResourceIdentifier("http://ica.ocsp.rootca.com")), ] eku_usages = [ x509.oid.ExtendedKeyUsageOID.CLIENT_AUTH, x509.oid.ExtendedKeyUsageOID.EMAIL_PROTECTION, x509.ObjectIdentifier( "1.3.6.1.4.1.311.20.2.2" ), # smartcardLogon (Microsoft enhanced key usage) ] if ocsp_signing_eku: eku_usages.append(x509.oid.ExtendedKeyUsageOID.OCSP_SIGNING) # basic constraints basic_constraints_ca = True basic_constraints_pathlen = 0 certificate_policies = [ x509.PolicyInformation( x509.oid.CertificatePoliciesOID.CPS_QUALIFIER, ["http://www.rootca.com/repo"]), x509.PolicyInformation( x509.oid.CertificatePoliciesOID.CPS_QUALIFIER, ["http://www.mycompany.com/cp/"]), ] crl_distribution_points = [ x509.DistributionPoint([ x509.UniformResourceIdentifier("http://crl.rootca.com/ica.crl") ], None, None, None) ] # key usage: Certificate Sign and CRL Sign set to True, rest set to False key_usages = 5 * [False] + 2 * [True] + 2 * [False] extensions = [ (x509.AuthorityInformationAccess(descriptions=aia_descriptions), False), (x509.SubjectKeyIdentifier.from_public_key( private_key.public_key()), False), (x509.BasicConstraints(basic_constraints_ca, basic_constraints_pathlen), True), (x509.AuthorityKeyIdentifier.from_issuer_public_key( issuer_private_key.public_key()), False), (x509.CertificatePolicies(policies=certificate_policies), False), (x509.CRLDistributionPoints(crl_distribution_points), False), (x509.KeyUsage(*key_usages), True), (x509.ExtendedKeyUsage(usages=eku_usages), False), ] cert = self.create_cert( not_before, not_after, serial_number, issuer_private_key, # use issuer private key to sign => do not self sign private_key, subject_entries=subject_entries, issuer_entries=issuer_entries, extensions=extensions) self.save_to_pem(cert, outpath) return cert
def create_smartcard_cert(self, issuer_private_key, private_key, outpath=None): subject_entries = [ (NameOID.COUNTRY_NAME, "US"), (NameOID.ORGANIZATION_NAME, "My Company"), (NameOID.COMMON_NAME, "*****@*****.**"), ] issuer_entries = [ (NameOID.COUNTRY_NAME, "US"), (NameOID.ORGANIZATION_NAME, "My Company"), (NameOID.COMMON_NAME, "My Company Name"), ] not_before = datetime.datetime.fromisoformat("2016-08-08 15:15:15") not_after = datetime.datetime.fromisoformat("2025-08-08 15:15:15") serial_number = x509.random_serial_number() aia_descriptions = [ x509.AccessDescription( x509.oid.AuthorityInformationAccessOID.CA_ISSUERS, x509.UniformResourceIdentifier( "http://sub.mycompany.com/sica.crt")), ] eku_usages = [ x509.oid.ExtendedKeyUsageOID.CLIENT_AUTH, x509.oid.ExtendedKeyUsageOID.EMAIL_PROTECTION, x509.ObjectIdentifier( "1.3.6.1.4.1.311.20.2.2" ), # smartcardLogon (Microsoft enhanced key usage) ] # basic constraints basic_constraints_ca = False basic_constraints_pathlen = None certificate_policies = [ x509.PolicyInformation( x509.oid.CertificatePoliciesOID.CPS_QUALIFIER, ["http://www.mycompany.com/cp/"]), ] # key usage: Signing only key_usages = [True] + 8 * [False] # subject alternative names subject_alternative_names = [ x509.DNSName("*****@*****.**"), ] crl_distribution_points = [ x509.DistributionPoint([ x509.UniformResourceIdentifier( "http://crl.mycompany.com/sica.crl") ], None, None, None) ] extensions = [ (x509.AuthorityInformationAccess(descriptions=aia_descriptions), False), (x509.SubjectKeyIdentifier.from_public_key( private_key.public_key()), False), (x509.BasicConstraints(basic_constraints_ca, basic_constraints_pathlen), True), (x509.AuthorityKeyIdentifier.from_issuer_public_key( issuer_private_key.public_key()), False), (x509.CertificatePolicies(policies=certificate_policies), False), (x509.CRLDistributionPoints(crl_distribution_points), False), (x509.KeyUsage(*key_usages), True), (x509.ExtendedKeyUsage(usages=eku_usages), False), (x509.SubjectAlternativeName(subject_alternative_names), False), ] cert = self.create_cert( not_before, not_after, serial_number, issuer_private_key, # use issuer private key to sign => do not self sign private_key, subject_entries=subject_entries, issuer_entries=issuer_entries, extensions=extensions) self.save_to_pem(cert, outpath) return cert
def signing_publickey(self, user: User, service: Service, publickey: str, valid_time=DAY * 365): _public_key = serialization.load_pem_public_key( publickey.encode(), backend=default_backend()) ca_private_key, ca_cert = self._init_ca(service) ca_name = service.name username = str(user.username) config = service.pki_config #TODO use this config domain = self._domain not_valid_before = datetime.datetime.utcnow() ca_public_key = ca_private_key.public_key() end_entity_cert_builder = x509.CertificateBuilder().\ subject_name(x509.Name([ x509.NameAttribute(NameOID.COMMON_NAME, config['cn'].format(username=username, domain=domain)), x509.NameAttribute(NameOID.EMAIL_ADDRESS, config['email'].format(username=username, domain=domain)), ])).\ issuer_name(ca_cert.subject).\ not_valid_before(not_valid_before).\ not_valid_after(not_valid_before + valid_time).\ serial_number(x509.random_serial_number()).\ public_key(_public_key).\ add_extension( x509.SubjectAlternativeName([ x509.DNSName(f'{username}'), ]), critical=False).\ add_extension( x509.BasicConstraints(ca=False, path_length=None), critical=True).\ add_extension( x509.KeyUsage(digital_signature=True, content_commitment=True, # False key_encipherment=True, data_encipherment=False, key_agreement=False, key_cert_sign=False, crl_sign=False, encipher_only=False, decipher_only=False), critical=True).\ add_extension( x509.ExtendedKeyUsage([ ExtendedKeyUsageOID.CLIENT_AUTH, ExtendedKeyUsageOID.SERVER_AUTH, ]), critical=False).\ add_extension( x509.AuthorityKeyIdentifier.from_issuer_public_key(ca_public_key), critical=False).\ add_extension( x509.SubjectKeyIdentifier.from_public_key(_public_key), critical=False).\ add_extension( x509.CRLDistributionPoints([ x509.DistributionPoint( full_name=[x509.UniformResourceIdentifier(f'http://crl.{self._domain}/{ca_name}.crl')], relative_name=None, crl_issuer=None, reasons=None) ]), critical=False).\ add_extension( x509.AuthorityInformationAccess([ x509.AccessDescription( access_method=x509.AuthorityInformationAccessOID.CA_ISSUERS, access_location=x509.UniformResourceIdentifier(f'https://www.{self._domain}')), x509.AccessDescription( access_method=x509.AuthorityInformationAccessOID.OCSP, access_location=x509.UniformResourceIdentifier(f'http://ocsp.{self._domain}/{ca_name}/')) ]), critical=False) end_entity_cert = end_entity_cert_builder.\ sign( private_key=ca_private_key, algorithm=hashes.SHA256(), backend=default_backend() ) serial_number = f'{end_entity_cert.serial_number:X}' end_entity_cert_filename = self._pki_path / ca_name / \ f'{safe_filename(username)}-{serial_number}.crt.pem' # save cert with end_entity_cert_filename.open("wb") as end_entity_cert_file: end_entity_cert_file.write( end_entity_cert.public_bytes( encoding=serialization.Encoding.PEM)) return Certificate(user.username, service.name, end_entity_cert)