示例#1
0
 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)
示例#2
0
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
示例#3
0
    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
示例#4
0
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
示例#5
0
    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"
示例#6
0
 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
示例#7
0
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
示例#8
0
 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,
     )
示例#9
0
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
示例#10
0
    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
        )
示例#11
0
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)
示例#12
0
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)
示例#13
0
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
示例#14
0
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
示例#15
0
 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,
             )
示例#16
0
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
示例#17
0
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
示例#18
0
    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
示例#19
0
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)
示例#20
0
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)
示例#21
0
    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)
示例#22
0
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
示例#23
0
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
示例#24
0
 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)
示例#25
0
    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
示例#29
0
    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
示例#30
0
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)