def _set_crl_distribution_url(self, cert: Certificate_model) -> None: if cert.type == CertificateTypes.SERVER_CERT or cert.type == CertificateTypes.CLIENT_CERT: cert = cert.parent if cert.crl_distribution_url: self._builder = self._builder.add_extension( x509.CRLDistributionPoints([ x509.DistributionPoint( full_name=[ x509.UniformResourceIdentifier( 'URI:{}{}{}.crl'.format( cert.crl_distribution_url, "/" if not cert.crl_distribution_url.endswith("/") else "", cert.shortname)) ], relative_name=None, 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, ]), crl_issuer=None) ]), critical=True)
def profile_ca(builder, ca_nick, ca): now = datetime.datetime.utcnow() builder = builder.not_valid_before(now) builder = builder.not_valid_after(now + 10 * YEAR) crl_uri = 'file://{}.crl'.format(os.path.join(DIR, ca_nick)) builder = builder.add_extension( x509.KeyUsage( digital_signature=True, content_commitment=True, key_encipherment=False, data_encipherment=False, key_agreement=False, key_cert_sign=True, crl_sign=True, encipher_only=False, decipher_only=False, ), critical=True, ) builder = builder.add_extension( x509.BasicConstraints(ca=True, path_length=None), critical=True, ) builder = builder.add_extension( x509.CRLDistributionPoints([ x509.DistributionPoint( full_name=[x509.UniformResourceIdentifier(crl_uri)], relative_name=None, crl_issuer=None, reasons=None, ), ]), critical=False, ) public_key = builder._public_key builder = builder.add_extension( x509.SubjectKeyIdentifier.from_public_key(public_key), critical=False, ) if not ca: builder = builder.add_extension( x509.AuthorityKeyIdentifier.from_issuer_public_key(public_key), critical=False, ) # here we get "ca" object only for "ca1/subca" else: ski = ca.cert.extensions.get_extension_for_class( x509.SubjectKeyIdentifier) builder = builder.add_extension( x509.AuthorityKeyIdentifier.from_issuer_subject_key_identifier( ski), critical=False, ) return builder
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(force_str(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_str(ocsp_url)) auth_info_access.append( x509.AccessDescription(access_method=AuthorityInformationAccessOID.OCSP, access_location=uri) ) if issuer_url: uri = x509.UniformResourceIdentifier(force_str(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 _decode_dist_points(backend, cdps): cdps = backend._ffi.cast("Cryptography_STACK_OF_DIST_POINT *", cdps) cdps = backend._ffi.gc(cdps, backend._lib.CRL_DIST_POINTS_free) num = backend._lib.sk_DIST_POINT_num(cdps) dist_points = [] for i in range(num): full_name = None relative_name = None crl_issuer = None reasons = None cdp = backend._lib.sk_DIST_POINT_value(cdps, i) if cdp.reasons != backend._ffi.NULL: reasons = _decode_reasons(backend, cdp.reasons) if cdp.CRLissuer != backend._ffi.NULL: crl_issuer = _decode_general_names(backend, cdp.CRLissuer) # Certificates may have a crl_issuer/reasons and no distribution # point so make sure it's not null. if cdp.distpoint != backend._ffi.NULL: full_name, relative_name = _decode_distpoint( backend, cdp.distpoint) dist_points.append( x509.DistributionPoint(full_name, relative_name, reasons, crl_issuer)) return dist_points
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 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 mk_cacert(name=None): """ Make a CA certificate. Returns the certificate, private key and public key. """ if name is None: name = config.get("ca", "cert_ca_name") cert_req, privkey = mk_request(config.getint("ca", "cert_bits"), name) pubkey = privkey.public_key() cert_req = cert_req.public_key(pubkey) cert_req = cert_req.serial_number(1) cert_req = mk_cert_valid(cert_req, config.getint("ca", "cert_ca_lifetime")) cert_req = cert_req.issuer_name(mk_name(name)) # Extensions. extensions = [ # Basic Constraints. x509.BasicConstraints(ca=True, path_length=None), # Subject Key Identifier. x509.SubjectKeyIdentifier.from_public_key(pubkey), # CRL Distribution Points. x509.CRLDistributionPoints([ x509.DistributionPoint( full_name=[ x509.UniformResourceIdentifier("http://localhost/crl.pem"), ], relative_name=None, reasons=None, crl_issuer=None, ), ]), # Key Usage. x509.KeyUsage( key_cert_sign=True, crl_sign=True, digital_signature=False, content_commitment=False, key_encipherment=False, data_encipherment=False, key_agreement=False, encipher_only=False, decipher_only=False, ), ] for ext in extensions: cert_req = cert_req.add_extension(ext, critical=False) cert = cert_req.sign( private_key=privkey, algorithm=hashes.SHA256(), backend=default_backend(), ) return cert, privkey, pubkey
def for_extension_type(self) -> x509.DistributionPoint: """Convert instance to a suitable cryptography class.""" reasons: Optional[FrozenSet[x509.ReasonFlags]] = frozenset( self.reasons) if self.reasons else None return x509.DistributionPoint( full_name=self.full_name, relative_name=self.relative_name, crl_issuer=self.crl_issuer, reasons=reasons, )
def profile_server(builder, ca_nick, ca, warp=datetime.timedelta(days=0), dns_name=None, badusage=False, wildcard=False): now = datetime.datetime.utcnow() + warp builder = builder.not_valid_before(now) builder = builder.not_valid_after(now + YEAR) crl_uri = u'file://{}.crl'.format(os.path.join(cert_dir, ca_nick)) builder = builder.add_extension( x509.CRLDistributionPoints([ x509.DistributionPoint( full_name=[x509.UniformResourceIdentifier(crl_uri)], relative_name=None, crl_issuer=None, reasons=None, ), ]), critical=False, ) if dns_name is not None: builder = builder.add_extension( x509.SubjectAlternativeName([x509.DNSName(dns_name)]), critical=False, ) if badusage: builder = builder.add_extension(x509.KeyUsage(digital_signature=False, content_commitment=False, key_encipherment=False, data_encipherment=True, key_agreement=True, key_cert_sign=False, crl_sign=False, encipher_only=False, decipher_only=False), critical=False) if wildcard: names = [x509.DNSName(u'*.' + domain)] server_split = server1.split('.', 1) if len(server_split) == 2 and domain != server_split[1]: names.append(x509.DNSName(u'*.' + server_split[1])) builder = builder.add_extension( x509.SubjectAlternativeName(names), critical=False, ) return builder
def for_extension_type(self) -> x509.DistributionPoint: """Convert instance to a suitable cryptography class.""" full_name: typing.Optional[GeneralNameList] = self.full_name crl_issuer: typing.Optional[GeneralNameList] = self.crl_issuer if not full_name: full_name = None if not crl_issuer: crl_issuer = None reasons: Optional[FrozenSet[x509.ReasonFlags]] = frozenset(self.reasons) if self.reasons else None return x509.DistributionPoint( full_name=full_name, relative_name=self.relative_name, crl_issuer=crl_issuer, reasons=reasons )
def test_verify_crl_unknown_scheme(cert_builder, private_key): """Unknown distribution point URI schemes should be ignored.""" ldap_uri = 'ldap://ldap.example.org/cn=Example%20Certificate%20Authority?certificateRevocationList;binary' crl_dp = x509.DistributionPoint([UniformResourceIdentifier(ldap_uri)], relative_name=None, reasons=None, crl_issuer=None) cert = (cert_builder .add_extension(x509.CRLDistributionPoints([crl_dp]), critical=False) .sign(private_key, hashes.SHA256(), default_backend())) with mktempfile() as cert_tmp: with open(cert_tmp, 'wb') as f: f.write(cert.public_bytes(serialization.Encoding.PEM)) # Must not raise exception crl_verify(cert_tmp)
def test_verify_crl_unreachable(cert_builder, private_key): """Unreachable CRL distribution point results in error.""" ldap_uri = 'http://invalid.example.org/crl/foobar.crl' crl_dp = x509.DistributionPoint([UniformResourceIdentifier(ldap_uri)], relative_name=None, reasons=None, crl_issuer=None) cert = (cert_builder .add_extension(x509.CRLDistributionPoints([crl_dp]), critical=False) .sign(private_key, hashes.SHA256(), default_backend())) with mktempfile() as cert_tmp: with open(cert_tmp, 'wb') as f: f.write(cert.public_bytes(serialization.Encoding.PEM)) with pytest.raises(Exception, match="Unable to retrieve CRL:"): crl_verify(cert_tmp)
def mk_signed_cert(cacert, ca_privkey, name, serialnum): """ Create a CA cert + server cert + server private key. """ cert_req, privkey = mk_request(config.getint("ca", "cert_bits"), common_name=name) pubkey = privkey.public_key() cert_req = cert_req.public_key(pubkey) cert_req = cert_req.serial_number(serialnum) cert_req = mk_cert_valid(cert_req) cert_req = cert_req.issuer_name(cacert.issuer) # Extensions. extensions = [ # OID 2.16.840.1.113730.1.13 is Netscape Comment. # http://oid-info.com/get/2.16.840.1.113730.1.13 x509.UnrecognizedExtension( oid=x509.ObjectIdentifier("2.16.840.1.113730.1.13"), value=b"SSL Server", ), # Subject Alternative Name. x509.SubjectAlternativeName([x509.DNSName(name)]), # CRL Distribution Points. x509.CRLDistributionPoints([ x509.DistributionPoint( full_name=[ x509.UniformResourceIdentifier("http://localhost/crl.pem"), ], relative_name=None, reasons=None, crl_issuer=None, ), ]), ] for ext in extensions: cert_req = cert_req.add_extension(ext, critical=False) cert = cert_req.sign( private_key=ca_privkey, algorithm=hashes.SHA256(), backend=default_backend(), ) return cert, privkey
def profile_server(builder, ca_nick, _ca, warp=datetime.timedelta(days=0), dns_name=None, badusage=False): now = datetime.datetime.utcnow() + warp builder = builder.not_valid_before(now) builder = builder.not_valid_after(now + YEAR) crl_uri = 'file://{}.crl'.format(os.path.join(DIR, ca_nick)) builder = builder.add_extension( x509.CRLDistributionPoints([ x509.DistributionPoint( full_name=[x509.UniformResourceIdentifier(crl_uri)], relative_name=None, crl_issuer=None, reasons=None, ), ]), critical=False, ) if dns_name is not None: builder = builder.add_extension( x509.SubjectAlternativeName([x509.DNSName(dns_name)]), critical=False, ) if badusage: builder = builder.add_extension(x509.KeyUsage(digital_signature=False, content_commitment=False, key_encipherment=False, data_encipherment=True, key_agreement=True, key_cert_sign=False, crl_sign=False, encipher_only=False, decipher_only=False), critical=False) return builder
def _set_crl_distribution_url(self, cert: CertificateType) -> None: if cert.type is not CertificateTypes.ROOT: cert = cert.parent crl_distribution_url = cert.crl_distribution_url if crl_distribution_url: self._builder = self._builder.add_extension( x509.CRLDistributionPoints([ x509.DistributionPoint( full_name=[ x509.UniformResourceIdentifier( crl_distribution_url) ], relative_name=None, reasons=None, crl_issuer=None, ) ]), critical=False, )
def profile_ca(builder, ca_nick): now = datetime.datetime.utcnow() builder = builder.not_valid_before(now) builder = builder.not_valid_after(now + 10 * YEAR) crl_uri = 'file://{}.crl'.format(os.path.join(DIR, ca_nick)) builder = builder.add_extension( x509.KeyUsage( digital_signature=True, content_commitment=True, key_encipherment=False, data_encipherment=False, key_agreement=False, key_cert_sign=True, crl_sign=True, encipher_only=False, decipher_only=False, ), critical=True, ) builder = builder.add_extension( x509.BasicConstraints(ca=True, path_length=None), critical=True, ) builder = builder.add_extension( x509.CRLDistributionPoints([ x509.DistributionPoint( full_name=[x509.UniformResourceIdentifier(crl_uri)], relative_name=None, crl_issuer=None, reasons=None, ), ]), critical=False, ) builder = builder.add_extension( x509.SubjectKeyIdentifier(digest=base64.b64encode(os.urandom(64))), critical=False, ) return builder
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 update_cert(self, csr, lifetime): """Given a CSR, look it up in the database, update it and present the new certificate. Args: csr (cryptography.x509.CertificateSigningRequest): A CSR. lifetime (int): Lifetime in seconds. Raises: CertProcessorMismatchedPublicKeyError: The public key from the new CSR does not match the in database Certificate. Returns: cryptography.x509.Certificate: A Signed Certificate for a user. """ common_name = csr.subject.get_attributes_for_oid(NameOID.COMMON_NAME) common_name = common_name[0].value bcert = bytes(str(self.storage.get_cert(common_name=common_name)[0]), "UTF-8") old_cert = x509.load_pem_x509_certificate(bcert, backend=default_backend()) old_cert_pub = (old_cert.public_key().public_bytes( encoding=serialization.Encoding.PEM, format=serialization.PublicFormat.SubjectPublicKeyInfo, ).decode("UTF-8")) csr_pub = (csr.public_key().public_bytes( encoding=serialization.Encoding.PEM, format=serialization.PublicFormat.SubjectPublicKeyInfo, ).decode("UTF-8")) if old_cert_pub != csr_pub: raise CertProcessorMismatchedPublicKeyError ca_pkey = self.get_ca_key() ca_cert = self.get_ca_cert(ca_pkey) now = datetime.datetime.utcnow() lifetime_delta = now + datetime.timedelta(seconds=int(lifetime)) alts = [] for alt in self.config.get("ca", "alternate_name").split(","): alts.append(x509.DNSName(u"{}".format(alt))) cert = (x509.CertificateBuilder().subject_name( old_cert.subject).issuer_name(ca_cert.subject).public_key( csr.public_key()).serial_number( old_cert.serial_number).not_valid_before( old_cert.not_valid_before).not_valid_after( lifetime_delta)) if len(alts) > 0: cert = cert.add_extension(x509.SubjectAlternativeName(alts), critical=False) crl_dp = x509.DistributionPoint( [ x509.UniformResourceIdentifier( "{protocol}://{server_url}/crl".format( protocol=self.PROTOCOL, server_url=self.SERVER_URL)) ], relative_name=None, reasons=None, crl_issuer=None, ) cert = cert.add_extension(x509.CRLDistributionPoints([crl_dp]), critical=False) cert = cert.sign(private_key=ca_pkey, algorithm=hashes.SHA256(), backend=default_backend()) self.storage.update_cert(cert=cert, serial_number=cert.serial_number) return cert
def _decode_crl_distribution_points(backend, cdps): cdps = backend._ffi.cast("Cryptography_STACK_OF_DIST_POINT *", cdps) dp_freefunc = backend._ffi.addressof(backend._lib._original_lib, "DIST_POINT_free") cdps = backend._ffi.gc( cdps, lambda c: backend._lib.sk_DIST_POINT_pop_free(c, dp_freefunc)) num = backend._lib.sk_DIST_POINT_num(cdps) dist_points = [] for i in range(num): full_name = None relative_name = None crl_issuer = None reasons = None cdp = backend._lib.sk_DIST_POINT_value(cdps, i) if cdp.reasons != backend._ffi.NULL: # We will check each bit from RFC 5280 # ReasonFlags ::= BIT STRING { # unused (0), # keyCompromise (1), # cACompromise (2), # affiliationChanged (3), # superseded (4), # cessationOfOperation (5), # certificateHold (6), # privilegeWithdrawn (7), # aACompromise (8) } reasons = [] get_bit = backend._lib.ASN1_BIT_STRING_get_bit if get_bit(cdp.reasons, 1): reasons.append(x509.ReasonFlags.key_compromise) if get_bit(cdp.reasons, 2): reasons.append(x509.ReasonFlags.ca_compromise) if get_bit(cdp.reasons, 3): reasons.append(x509.ReasonFlags.affiliation_changed) if get_bit(cdp.reasons, 4): reasons.append(x509.ReasonFlags.superseded) if get_bit(cdp.reasons, 5): reasons.append(x509.ReasonFlags.cessation_of_operation) if get_bit(cdp.reasons, 6): reasons.append(x509.ReasonFlags.certificate_hold) if get_bit(cdp.reasons, 7): reasons.append(x509.ReasonFlags.privilege_withdrawn) if get_bit(cdp.reasons, 8): reasons.append(x509.ReasonFlags.aa_compromise) reasons = frozenset(reasons) if cdp.CRLissuer != backend._ffi.NULL: crl_issuer = _decode_general_names(backend, cdp.CRLissuer) # Certificates may have a crl_issuer/reasons and no distribution # point so make sure it's not null. if cdp.distpoint != backend._ffi.NULL: # Type 0 is fullName, there is no #define for it in the code. if cdp.distpoint.type == _DISTPOINT_TYPE_FULLNAME: full_name = _decode_general_names(backend, cdp.distpoint.name.fullname) # OpenSSL code doesn't test for a specific type for # relativename, everything that isn't fullname is considered # relativename. Per RFC 5280: # # DistributionPointName ::= CHOICE { # fullName [0] GeneralNames, # nameRelativeToCRLIssuer [1] RelativeDistinguishedName } else: rns = cdp.distpoint.name.relativename rnum = backend._lib.sk_X509_NAME_ENTRY_num(rns) attributes = set() for i in range(rnum): rn = backend._lib.sk_X509_NAME_ENTRY_value(rns, i) backend.openssl_assert(rn != backend._ffi.NULL) attributes.add(_decode_x509_name_entry(backend, rn)) relative_name = x509.RelativeDistinguishedName(attributes) dist_points.append( x509.DistributionPoint(full_name, relative_name, reasons, crl_issuer)) return x509.CRLDistributionPoints(dist_points)
def _decode_crl_distribution_points(backend, ext): cdps = backend._ffi.cast( "Cryptography_STACK_OF_DIST_POINT *", backend._lib.X509V3_EXT_d2i(ext) ) assert cdps != backend._ffi.NULL cdps = backend._ffi.gc( cdps, backend._lib.sk_DIST_POINT_free) num = backend._lib.sk_DIST_POINT_num(cdps) dist_points = [] for i in range(num): full_name = None relative_name = None crl_issuer = None reasons = None cdp = backend._lib.sk_DIST_POINT_value(cdps, i) if cdp.reasons != backend._ffi.NULL: # We will check each bit from RFC 5280 # ReasonFlags ::= BIT STRING { # unused (0), # keyCompromise (1), # cACompromise (2), # affiliationChanged (3), # superseded (4), # cessationOfOperation (5), # certificateHold (6), # privilegeWithdrawn (7), # aACompromise (8) } reasons = [] get_bit = backend._lib.ASN1_BIT_STRING_get_bit if get_bit(cdp.reasons, 1): reasons.append(x509.ReasonFlags.key_compromise) if get_bit(cdp.reasons, 2): reasons.append(x509.ReasonFlags.ca_compromise) if get_bit(cdp.reasons, 3): reasons.append(x509.ReasonFlags.affiliation_changed) if get_bit(cdp.reasons, 4): reasons.append(x509.ReasonFlags.superseded) if get_bit(cdp.reasons, 5): reasons.append(x509.ReasonFlags.cessation_of_operation) if get_bit(cdp.reasons, 6): reasons.append(x509.ReasonFlags.certificate_hold) if get_bit(cdp.reasons, 7): reasons.append(x509.ReasonFlags.privilege_withdrawn) if get_bit(cdp.reasons, 8): reasons.append(x509.ReasonFlags.aa_compromise) reasons = frozenset(reasons) if cdp.CRLissuer != backend._ffi.NULL: crl_issuer = _decode_general_names(backend, cdp.CRLissuer) # Certificates may have a crl_issuer/reasons and no distribution # point so make sure it's not null. if cdp.distpoint != backend._ffi.NULL: # Type 0 is fullName, there is no #define for it in the code. if cdp.distpoint.type == 0: full_name = _decode_general_names( backend, cdp.distpoint.name.fullname ) # OpenSSL code doesn't test for a specific type for # relativename, everything that isn't fullname is considered # relativename. else: rns = cdp.distpoint.name.relativename rnum = backend._lib.sk_X509_NAME_ENTRY_num(rns) attributes = [] for i in range(rnum): rn = backend._lib.sk_X509_NAME_ENTRY_value( rns, i ) assert rn != backend._ffi.NULL attributes.append( _decode_x509_name_entry(backend, rn) ) relative_name = x509.Name(attributes) dist_points.append( x509.DistributionPoint( full_name, relative_name, reasons, crl_issuer ) ) return x509.CRLDistributionPoints(dist_points)
def generate_cert(self, csr, lifetime, fingerprint): """Generate a Certificate from a CSR. Args: csr: The CSR object lifetime: The lifetime of the certificate in seconds fingerprint: The fingerprint of the signer for the CSR. Raises: CertProcessorNotAdminUserError: When an admin request is made without and admin key CertProcessorInvalidSignatureError: When an invalid user attempts to sign a request for a certificate Returns: The certificates public bytes """ ca_pkey = self.get_ca_key() ca_cert = self.get_ca_cert(ca_pkey) now = datetime.datetime.utcnow() lifetime_delta = now + datetime.timedelta(seconds=int(lifetime)) alts = [] is_admin = self.is_admin(fingerprint) logger.info(f"generate_cert: getting gpg key for {fingerprint}") user_gpg_key = self.get_gpg_key_by_fingerprint(fingerprint, is_admin) if user_gpg_key is None: raise CertProcessorNoPGPKeyFoundError() email_in_key = self.check_subject_against_key(csr.subject, user_gpg_key) if not email_in_key and not is_admin: raise CertProcessorNotAdminUserError() for alt in self.config.get("ca", "alternate_name").split(","): alts.append(x509.DNSName(u"{}".format(alt))) cert = (x509.CertificateBuilder().subject_name( csr.subject).issuer_name(ca_cert.subject).public_key( csr.public_key()).serial_number( uuid.uuid4().int).not_valid_before(now).not_valid_after( lifetime_delta)) if len(alts) > 0: cert = cert.add_extension(x509.SubjectAlternativeName(alts), critical=False) crl_dp = x509.DistributionPoint( [ x509.UniformResourceIdentifier( "{protocol}://{server_url}/crl".format( protocol=self.PROTOCOL, server_url=self.SERVER_URL)) ], relative_name=None, reasons=None, crl_issuer=None, ) cert = cert.add_extension(x509.CRLDistributionPoints([crl_dp]), critical=False) logger.info(f"generate_cert: Signing certificate for {fingerprint}") cert = cert.sign(private_key=ca_pkey, algorithm=hashes.SHA256(), backend=default_backend()) try: logger.info(f"generate_cert: saving certificate for {fingerprint}") self.storage.save_cert(cert, fingerprint) except StorageEngineCertificateConflict: logger.info( f"generate_cert: updating certificate for {fingerprint}") cert = self.update_cert(csr, lifetime) return cert.public_bytes(serialization.Encoding.PEM)
def profile_ca(builder, ca_nick, ca): now = datetime.datetime.utcnow() builder = builder.not_valid_before(now) builder = builder.not_valid_after(now + 10 * YEAR) crl_uri = u'file://{}.crl'.format(os.path.join(cert_dir, ca_nick)) builder = builder.add_extension( x509.KeyUsage( digital_signature=True, content_commitment=True, key_encipherment=False, data_encipherment=False, key_agreement=False, key_cert_sign=True, crl_sign=True, encipher_only=False, decipher_only=False, ), critical=True, ) builder = builder.add_extension( x509.BasicConstraints(ca=True, path_length=None), critical=True, ) builder = builder.add_extension( x509.CRLDistributionPoints([ x509.DistributionPoint( full_name=[x509.UniformResourceIdentifier(crl_uri)], relative_name=None, crl_issuer=None, reasons=None, ), ]), critical=False, ) public_key = builder._public_key builder = builder.add_extension( x509.SubjectKeyIdentifier.from_public_key(public_key), critical=False, ) # here we get "ca" only for "ca1/subca" CA if not ca: builder = builder.add_extension( x509.AuthorityKeyIdentifier.from_issuer_public_key(public_key), critical=False, ) else: ski_ext = ca.cert.extensions.get_extension_for_class( x509.SubjectKeyIdentifier) auth_keyidentifier = ( x509.AuthorityKeyIdentifier.from_issuer_subject_key_identifier) ''' cryptography < 2.7 accepts only Extension object. Remove this workaround when all supported platforms update python-cryptography. ''' if (parse_version(cryptography_version) >= parse_version('2.7')): extension = auth_keyidentifier(ski_ext.value) else: extension = auth_keyidentifier(ski_ext) builder = builder.add_extension(extension, critical=False) return builder
def profile_kdc(builder, ca_nick, ca, warp=datetime.timedelta(days=0), dns_name=None, badusage=False): now = datetime.datetime.utcnow() + warp builder = builder.not_valid_before(now) builder = builder.not_valid_after(now + YEAR) crl_uri = u'file://{}.crl'.format(os.path.join(cert_dir, ca_nick)) builder = builder.add_extension( x509.ExtendedKeyUsage([x509.ObjectIdentifier('1.3.6.1.5.2.3.5')]), critical=False, ) name = { 'realm': realm, 'principalName': { 'name-type': 2, 'name-string': ['krbtgt', realm], }, } name = native_decoder.decode(name, asn1Spec=KRB5PrincipalName()) name = der_encoder.encode(name) names = [x509.OtherName(x509.ObjectIdentifier('1.3.6.1.5.2.2'), name)] if dns_name is not None: names += [x509.DNSName(dns_name)] builder = builder.add_extension( x509.SubjectAlternativeName(names), critical=False, ) builder = builder.add_extension( x509.CRLDistributionPoints([ x509.DistributionPoint( full_name=[x509.UniformResourceIdentifier(crl_uri)], relative_name=None, crl_issuer=None, reasons=None, ), ]), critical=False, ) if badusage: builder = builder.add_extension(x509.KeyUsage(digital_signature=False, content_commitment=False, key_encipherment=False, data_encipherment=True, key_agreement=True, key_cert_sign=False, crl_sign=False, encipher_only=False, decipher_only=False), critical=False) return builder
def for_extension_type(self) -> x509.DistributionPoint: """Convert instance to a suitable cryptography class.""" return x509.DistributionPoint(full_name=self.full_name, relative_name=self.relative_name, crl_issuer=self.crl_issuer, reasons=self.reasons)
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_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 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 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)