예제 #1
0
    def test_sign_ed448_key(self, backend):
        private_key = ed448.Ed448PrivateKey.generate()
        invalidity_date = x509.InvalidityDate(
            datetime.datetime(2002, 1, 1, 0, 0))
        ian = x509.IssuerAlternativeName(
            [x509.UniformResourceIdentifier("https://cryptography.io")])
        revoked_cert0 = (
            x509.RevokedCertificateBuilder().serial_number(2).revocation_date(
                datetime.datetime(2012, 1, 1, 1,
                                  1)).add_extension(invalidity_date,
                                                    False).build(backend))
        last_update = datetime.datetime(2002, 1, 1, 12, 1)
        next_update = datetime.datetime(2030, 1, 1, 12, 1)
        builder = (x509.CertificateRevocationListBuilder().issuer_name(
            x509.Name([
                x509.NameAttribute(NameOID.COMMON_NAME, "cryptography.io CA")
            ])).last_update(last_update).next_update(next_update).
                   add_revoked_certificate(revoked_cert0).add_extension(
                       ian, False))

        crl = builder.sign(private_key, None, backend)
        assert crl.signature_hash_algorithm is None
        assert crl.signature_algorithm_oid == SignatureAlgorithmOID.ED448
        assert (crl.extensions.get_extension_for_class(
            x509.IssuerAlternativeName).value == ian)
        assert crl[0].serial_number == revoked_cert0.serial_number
        assert crl[0].revocation_date == revoked_cert0.revocation_date
        assert len(crl[0].extensions) == 1
        ext = crl[0].extensions.get_extension_for_class(x509.InvalidityDate)
        assert ext.critical is False
        assert ext.value == invalidity_date
예제 #2
0
    def test_sign_multiple_extensions_critical(self, backend):
        private_key = RSA_KEY_2048.private_key(backend)
        last_update = datetime.datetime(2002, 1, 1, 12, 1)
        next_update = datetime.datetime(2030, 1, 1, 12, 1)
        ian = x509.IssuerAlternativeName([
            x509.UniformResourceIdentifier(u"https://cryptography.io"),
        ])
        crl_number = x509.CRLNumber(13)
        builder = x509.CertificateRevocationListBuilder().issuer_name(
            x509.Name([
                x509.NameAttribute(NameOID.COMMON_NAME, u"cryptography.io CA")
            ])
        ).last_update(
            last_update
        ).next_update(
            next_update
        ).add_extension(
            crl_number, False
        ).add_extension(
            ian, True
        )

        crl = builder.sign(private_key, hashes.SHA256(), backend)
        assert len(crl) == 0
        assert len(crl.extensions) == 2
        ext1 = crl.extensions.get_extension_for_class(x509.CRLNumber)
        assert ext1.critical is False
        assert ext1.value == crl_number
        ext2 = crl.extensions.get_extension_for_class(
            x509.IssuerAlternativeName
        )
        assert ext2.critical is True
        assert ext2.value == ian
예제 #3
0
    def test_sign_ec_key(self, backend):
        _skip_curve_unsupported(backend, ec.SECP256R1())
        private_key = ec.generate_private_key(ec.SECP256R1(), backend)
        invalidity_date = x509.InvalidityDate(
            datetime.datetime(2002, 1, 1, 0, 0))
        ian = x509.IssuerAlternativeName(
            [x509.UniformResourceIdentifier("https://cryptography.io")])
        revoked_cert0 = (
            x509.RevokedCertificateBuilder().serial_number(2).revocation_date(
                datetime.datetime(2012, 1, 1, 1,
                                  1)).add_extension(invalidity_date,
                                                    False).build(backend))
        last_update = datetime.datetime(2002, 1, 1, 12, 1)
        next_update = datetime.datetime(2030, 1, 1, 12, 1)
        builder = (x509.CertificateRevocationListBuilder().issuer_name(
            x509.Name([
                x509.NameAttribute(NameOID.COMMON_NAME, "cryptography.io CA")
            ])).last_update(last_update).next_update(next_update).
                   add_revoked_certificate(revoked_cert0).add_extension(
                       ian, False))

        crl = builder.sign(private_key, hashes.SHA256(), backend)
        assert (crl.extensions.get_extension_for_class(
            x509.IssuerAlternativeName).value == ian)
        assert crl[0].serial_number == revoked_cert0.serial_number
        assert crl[0].revocation_date == revoked_cert0.revocation_date
        assert len(crl[0].extensions) == 1
        ext = crl[0].extensions.get_extension_for_class(x509.InvalidityDate)
        assert ext.critical is False
        assert ext.value == invalidity_date
    def test_sign_dsa_key(self, backend):
        if backend._lib.OPENSSL_VERSION_NUMBER < 0x10001000:
            pytest.skip("Requires a newer OpenSSL. Must be >= 1.0.1")
        private_key = DSA_KEY_2048.private_key(backend)
        invalidity_date = x509.InvalidityDate(
            datetime.datetime(2002, 1, 1, 0, 0))
        ian = x509.IssuerAlternativeName([
            x509.UniformResourceIdentifier(u"https://cryptography.io"),
        ])
        revoked_cert0 = x509.RevokedCertificateBuilder().serial_number(
            2).revocation_date(datetime.datetime(2012, 1, 1, 1,
                                                 1)).add_extension(
                                                     invalidity_date,
                                                     False).build(backend)
        last_update = datetime.datetime(2002, 1, 1, 12, 1)
        next_update = datetime.datetime(2030, 1, 1, 12, 1)
        builder = x509.CertificateRevocationListBuilder().issuer_name(
            x509.Name([
                x509.NameAttribute(NameOID.COMMON_NAME, u"cryptography.io CA")
            ])).last_update(last_update).next_update(
                next_update).add_revoked_certificate(
                    revoked_cert0).add_extension(ian, False)

        crl = builder.sign(private_key, hashes.SHA256(), backend)
        assert crl.extensions.get_extension_for_class(
            x509.IssuerAlternativeName).value == ian
        assert crl[0].serial_number == revoked_cert0.serial_number
        assert crl[0].revocation_date == revoked_cert0.revocation_date
        assert len(crl[0].extensions) == 1
        ext = crl[0].extensions.get_extension_for_class(x509.InvalidityDate)
        assert ext.critical is False
        assert ext.value == invalidity_date
예제 #5
0
def create_x509_cert(privkey, pubkey, subject_info, issuer_info, days):
    """Main cert creation code.
    """
    if not isinstance(subject_info, CertInfo):
        info = CertInfo()
        info.load_from_existing(subject_info)
        subject_info = info
    if not isinstance(issuer_info, CertInfo):
        info = CertInfo()
        info.load_from_existing(issuer_info)
        issuer_info = info

    dt_now = datetime.utcnow()
    dt_start = dt_now - timedelta(hours=1)
    dt_end = dt_now + timedelta(days=days)

    builder = (x509.CertificateBuilder()
        .subject_name(subject_info.get_name())
        .issuer_name(issuer_info.get_name())
        .not_valid_before(dt_start)
        .not_valid_after(dt_end)
        .serial_number(int(uuid.uuid4()))
        .public_key(pubkey))

    builder = subject_info.install_extensions(builder)

    # SubjectKeyIdentifier
    ext = x509.SubjectKeyIdentifier.from_public_key(pubkey)
    builder = builder.add_extension(ext, critical=False)

    # AuthorityKeyIdentifier
    ext = x509.AuthorityKeyIdentifier.from_issuer_public_key(privkey.public_key())
    builder = builder.add_extension(ext, critical=False)

    # IssuerAlternativeName
    if issuer_info.san:
        ext = x509.IssuerAlternativeName(issuer_info.get_san_gnames())
        builder = builder.add_extension(ext, critical=False)

    # final cert
    cert = builder.sign(private_key=privkey, algorithm=SHA256(), backend=get_backend())
    return cert
예제 #6
0
def _decode_issuer_alt_name(backend, ext):
    return x509.IssuerAlternativeName(
        _decode_general_names_extension(backend, ext))
예제 #7
0
class TestCertificateRevocationListBuilder(object):
    def test_issuer_name_invalid(self):
        builder = x509.CertificateRevocationListBuilder()
        with pytest.raises(TypeError):
            builder.issuer_name("notanx509name")

    def test_set_issuer_name_twice(self):
        builder = x509.CertificateRevocationListBuilder().issuer_name(
            x509.Name([x509.NameAttribute(NameOID.COUNTRY_NAME, u'US')])
        )
        with pytest.raises(ValueError):
            builder.issuer_name(
                x509.Name([x509.NameAttribute(NameOID.COUNTRY_NAME, u'US')])
            )

    @pytest.mark.requires_backend_interface(interface=RSABackend)
    @pytest.mark.requires_backend_interface(interface=X509Backend)
    def test_aware_last_update(self, backend):
        last_time = datetime.datetime(2012, 1, 16, 22, 43)
        tz = pytz.timezone("US/Pacific")
        last_time = tz.localize(last_time)
        utc_last = datetime.datetime(2012, 1, 17, 6, 43)
        next_time = datetime.datetime(2022, 1, 17, 6, 43)
        private_key = RSA_KEY_2048.private_key(backend)
        builder = x509.CertificateRevocationListBuilder().issuer_name(
            x509.Name([
                x509.NameAttribute(NameOID.COMMON_NAME, u"cryptography.io CA")
            ])
        ).last_update(last_time).next_update(next_time)

        crl = builder.sign(private_key, hashes.SHA256(), backend)
        assert crl.last_update == utc_last

    def test_last_update_invalid(self):
        builder = x509.CertificateRevocationListBuilder()
        with pytest.raises(TypeError):
            builder.last_update("notadatetime")

    def test_last_update_before_unix_epoch(self):
        builder = x509.CertificateRevocationListBuilder()
        with pytest.raises(ValueError):
            builder.last_update(datetime.datetime(1960, 8, 10))

    def test_set_last_update_twice(self):
        builder = x509.CertificateRevocationListBuilder().last_update(
            datetime.datetime(2002, 1, 1, 12, 1)
        )
        with pytest.raises(ValueError):
            builder.last_update(datetime.datetime(2002, 1, 1, 12, 1))

    @pytest.mark.requires_backend_interface(interface=RSABackend)
    @pytest.mark.requires_backend_interface(interface=X509Backend)
    def test_aware_next_update(self, backend):
        next_time = datetime.datetime(2022, 1, 16, 22, 43)
        tz = pytz.timezone("US/Pacific")
        next_time = tz.localize(next_time)
        utc_next = datetime.datetime(2022, 1, 17, 6, 43)
        last_time = datetime.datetime(2012, 1, 17, 6, 43)
        private_key = RSA_KEY_2048.private_key(backend)
        builder = x509.CertificateRevocationListBuilder().issuer_name(
            x509.Name([
                x509.NameAttribute(NameOID.COMMON_NAME, u"cryptography.io CA")
            ])
        ).last_update(last_time).next_update(next_time)

        crl = builder.sign(private_key, hashes.SHA256(), backend)
        assert crl.next_update == utc_next

    def test_next_update_invalid(self):
        builder = x509.CertificateRevocationListBuilder()
        with pytest.raises(TypeError):
            builder.next_update("notadatetime")

    def test_next_update_before_unix_epoch(self):
        builder = x509.CertificateRevocationListBuilder()
        with pytest.raises(ValueError):
            builder.next_update(datetime.datetime(1960, 8, 10))

    def test_set_next_update_twice(self):
        builder = x509.CertificateRevocationListBuilder().next_update(
            datetime.datetime(2002, 1, 1, 12, 1)
        )
        with pytest.raises(ValueError):
            builder.next_update(datetime.datetime(2002, 1, 1, 12, 1))

    def test_last_update_after_next_update(self):
        builder = x509.CertificateRevocationListBuilder()

        builder = builder.next_update(
            datetime.datetime(2002, 1, 1, 12, 1)
        )
        with pytest.raises(ValueError):
            builder.last_update(datetime.datetime(2003, 1, 1, 12, 1))

    def test_next_update_after_last_update(self):
        builder = x509.CertificateRevocationListBuilder()

        builder = builder.last_update(
            datetime.datetime(2002, 1, 1, 12, 1)
        )
        with pytest.raises(ValueError):
            builder.next_update(datetime.datetime(2001, 1, 1, 12, 1))

    def test_add_extension_checks_for_duplicates(self):
        builder = x509.CertificateRevocationListBuilder().add_extension(
            x509.CRLNumber(1), False
        )

        with pytest.raises(ValueError):
            builder.add_extension(x509.CRLNumber(2), False)

    def test_add_invalid_extension(self):
        builder = x509.CertificateRevocationListBuilder()

        with pytest.raises(TypeError):
            builder.add_extension(
                object(), False
            )

    def test_add_invalid_revoked_certificate(self):
        builder = x509.CertificateRevocationListBuilder()

        with pytest.raises(TypeError):
            builder.add_revoked_certificate(object())

    @pytest.mark.requires_backend_interface(interface=RSABackend)
    @pytest.mark.requires_backend_interface(interface=X509Backend)
    def test_no_issuer_name(self, backend):
        private_key = RSA_KEY_2048.private_key(backend)
        builder = x509.CertificateRevocationListBuilder().last_update(
            datetime.datetime(2002, 1, 1, 12, 1)
        ).next_update(
            datetime.datetime(2030, 1, 1, 12, 1)
        )

        with pytest.raises(ValueError):
            builder.sign(private_key, hashes.SHA256(), backend)

    @pytest.mark.requires_backend_interface(interface=RSABackend)
    @pytest.mark.requires_backend_interface(interface=X509Backend)
    def test_no_last_update(self, backend):
        private_key = RSA_KEY_2048.private_key(backend)
        builder = x509.CertificateRevocationListBuilder().issuer_name(
            x509.Name([x509.NameAttribute(NameOID.COUNTRY_NAME, u'US')])
        ).next_update(
            datetime.datetime(2030, 1, 1, 12, 1)
        )

        with pytest.raises(ValueError):
            builder.sign(private_key, hashes.SHA256(), backend)

    @pytest.mark.requires_backend_interface(interface=RSABackend)
    @pytest.mark.requires_backend_interface(interface=X509Backend)
    def test_no_next_update(self, backend):
        private_key = RSA_KEY_2048.private_key(backend)
        builder = x509.CertificateRevocationListBuilder().issuer_name(
            x509.Name([x509.NameAttribute(NameOID.COUNTRY_NAME, u'US')])
        ).last_update(
            datetime.datetime(2030, 1, 1, 12, 1)
        )

        with pytest.raises(ValueError):
            builder.sign(private_key, hashes.SHA256(), backend)

    @pytest.mark.requires_backend_interface(interface=RSABackend)
    @pytest.mark.requires_backend_interface(interface=X509Backend)
    def test_sign_empty_list(self, backend):
        private_key = RSA_KEY_2048.private_key(backend)
        last_update = datetime.datetime(2002, 1, 1, 12, 1)
        next_update = datetime.datetime(2030, 1, 1, 12, 1)
        builder = x509.CertificateRevocationListBuilder().issuer_name(
            x509.Name([
                x509.NameAttribute(NameOID.COMMON_NAME, u"cryptography.io CA")
            ])
        ).last_update(last_update).next_update(next_update)

        crl = builder.sign(private_key, hashes.SHA256(), backend)
        assert len(crl) == 0
        assert crl.last_update == last_update
        assert crl.next_update == next_update

    @pytest.mark.parametrize(
        "extension",
        [
            x509.CRLNumber(13),
            x509.AuthorityKeyIdentifier(
                b"\xc3\x9c\xf3\xfc\xd3F\x084\xbb\xceF\x7f\xa0|[\xf3\xe2\x08"
                b"\xcbY",
                None,
                None
            ),
            x509.AuthorityInformationAccess([
                x509.AccessDescription(
                    AuthorityInformationAccessOID.CA_ISSUERS,
                    x509.DNSName(u"cryptography.io")
                )
            ]),
            x509.IssuerAlternativeName([
                x509.UniformResourceIdentifier(u"https://cryptography.io"),
            ])
        ]
    )
    @pytest.mark.requires_backend_interface(interface=RSABackend)
    @pytest.mark.requires_backend_interface(interface=X509Backend)
    def test_sign_extensions(self, backend, extension):
        private_key = RSA_KEY_2048.private_key(backend)
        last_update = datetime.datetime(2002, 1, 1, 12, 1)
        next_update = datetime.datetime(2030, 1, 1, 12, 1)
        builder = x509.CertificateRevocationListBuilder().issuer_name(
            x509.Name([
                x509.NameAttribute(NameOID.COMMON_NAME, u"cryptography.io CA")
            ])
        ).last_update(
            last_update
        ).next_update(
            next_update
        ).add_extension(
            extension, False
        )

        crl = builder.sign(private_key, hashes.SHA256(), backend)
        assert len(crl) == 0
        assert len(crl.extensions) == 1
        ext = crl.extensions.get_extension_for_class(type(extension))
        assert ext.critical is False
        assert ext.value == extension

    @pytest.mark.requires_backend_interface(interface=RSABackend)
    @pytest.mark.requires_backend_interface(interface=X509Backend)
    def test_sign_multiple_extensions_critical(self, backend):
        private_key = RSA_KEY_2048.private_key(backend)
        last_update = datetime.datetime(2002, 1, 1, 12, 1)
        next_update = datetime.datetime(2030, 1, 1, 12, 1)
        ian = x509.IssuerAlternativeName([
            x509.UniformResourceIdentifier(u"https://cryptography.io"),
        ])
        crl_number = x509.CRLNumber(13)
        builder = x509.CertificateRevocationListBuilder().issuer_name(
            x509.Name([
                x509.NameAttribute(NameOID.COMMON_NAME, u"cryptography.io CA")
            ])
        ).last_update(
            last_update
        ).next_update(
            next_update
        ).add_extension(
            crl_number, False
        ).add_extension(
            ian, True
        )

        crl = builder.sign(private_key, hashes.SHA256(), backend)
        assert len(crl) == 0
        assert len(crl.extensions) == 2
        ext1 = crl.extensions.get_extension_for_class(x509.CRLNumber)
        assert ext1.critical is False
        assert ext1.value == crl_number
        ext2 = crl.extensions.get_extension_for_class(
            x509.IssuerAlternativeName
        )
        assert ext2.critical is True
        assert ext2.value == ian

    @pytest.mark.requires_backend_interface(interface=RSABackend)
    @pytest.mark.requires_backend_interface(interface=X509Backend)
    def test_add_unsupported_extension(self, backend):
        private_key = RSA_KEY_2048.private_key(backend)
        last_update = datetime.datetime(2002, 1, 1, 12, 1)
        next_update = datetime.datetime(2030, 1, 1, 12, 1)
        builder = x509.CertificateRevocationListBuilder().issuer_name(
            x509.Name([
                x509.NameAttribute(NameOID.COMMON_NAME, u"cryptography.io CA")
            ])
        ).last_update(
            last_update
        ).next_update(
            next_update
        ).add_extension(
            x509.OCSPNoCheck(), False
        )
        with pytest.raises(NotImplementedError):
            builder.sign(private_key, hashes.SHA256(), backend)

    @pytest.mark.requires_backend_interface(interface=RSABackend)
    @pytest.mark.requires_backend_interface(interface=X509Backend)
    def test_sign_rsa_key_too_small(self, backend):
        private_key = RSA_KEY_512.private_key(backend)
        last_update = datetime.datetime(2002, 1, 1, 12, 1)
        next_update = datetime.datetime(2030, 1, 1, 12, 1)
        builder = x509.CertificateRevocationListBuilder().issuer_name(
            x509.Name([
                x509.NameAttribute(NameOID.COMMON_NAME, u"cryptography.io CA")
            ])
        ).last_update(
            last_update
        ).next_update(
            next_update
        )

        with pytest.raises(ValueError):
            builder.sign(private_key, hashes.SHA512(), backend)

    @pytest.mark.requires_backend_interface(interface=RSABackend)
    @pytest.mark.requires_backend_interface(interface=X509Backend)
    def test_sign_with_invalid_hash(self, backend):
        private_key = RSA_KEY_2048.private_key(backend)
        last_update = datetime.datetime(2002, 1, 1, 12, 1)
        next_update = datetime.datetime(2030, 1, 1, 12, 1)
        builder = x509.CertificateRevocationListBuilder().issuer_name(
            x509.Name([
                x509.NameAttribute(NameOID.COMMON_NAME, u"cryptography.io CA")
            ])
        ).last_update(
            last_update
        ).next_update(
            next_update
        )

        with pytest.raises(TypeError):
            builder.sign(private_key, object(), backend)

    @pytest.mark.requires_backend_interface(interface=DSABackend)
    @pytest.mark.requires_backend_interface(interface=X509Backend)
    def test_sign_dsa_key(self, backend):
        private_key = DSA_KEY_2048.private_key(backend)
        invalidity_date = x509.InvalidityDate(
            datetime.datetime(2002, 1, 1, 0, 0)
        )
        ian = x509.IssuerAlternativeName([
            x509.UniformResourceIdentifier(u"https://cryptography.io"),
        ])
        revoked_cert0 = x509.RevokedCertificateBuilder().serial_number(
            2
        ).revocation_date(
            datetime.datetime(2012, 1, 1, 1, 1)
        ).add_extension(
            invalidity_date, False
        ).build(backend)
        last_update = datetime.datetime(2002, 1, 1, 12, 1)
        next_update = datetime.datetime(2030, 1, 1, 12, 1)
        builder = x509.CertificateRevocationListBuilder().issuer_name(
            x509.Name([
                x509.NameAttribute(NameOID.COMMON_NAME, u"cryptography.io CA")
            ])
        ).last_update(
            last_update
        ).next_update(
            next_update
        ).add_revoked_certificate(
            revoked_cert0
        ).add_extension(
            ian, False
        )

        crl = builder.sign(private_key, hashes.SHA256(), backend)
        assert crl.extensions.get_extension_for_class(
            x509.IssuerAlternativeName
        ).value == ian
        assert crl[0].serial_number == revoked_cert0.serial_number
        assert crl[0].revocation_date == revoked_cert0.revocation_date
        assert len(crl[0].extensions) == 1
        ext = crl[0].extensions.get_extension_for_class(x509.InvalidityDate)
        assert ext.critical is False
        assert ext.value == invalidity_date

    @pytest.mark.requires_backend_interface(interface=EllipticCurveBackend)
    @pytest.mark.requires_backend_interface(interface=X509Backend)
    def test_sign_ec_key(self, backend):
        _skip_curve_unsupported(backend, ec.SECP256R1())
        private_key = ec.generate_private_key(ec.SECP256R1(), backend)
        invalidity_date = x509.InvalidityDate(
            datetime.datetime(2002, 1, 1, 0, 0)
        )
        ian = x509.IssuerAlternativeName([
            x509.UniformResourceIdentifier(u"https://cryptography.io"),
        ])
        revoked_cert0 = x509.RevokedCertificateBuilder().serial_number(
            2
        ).revocation_date(
            datetime.datetime(2012, 1, 1, 1, 1)
        ).add_extension(
            invalidity_date, False
        ).build(backend)
        last_update = datetime.datetime(2002, 1, 1, 12, 1)
        next_update = datetime.datetime(2030, 1, 1, 12, 1)
        builder = x509.CertificateRevocationListBuilder().issuer_name(
            x509.Name([
                x509.NameAttribute(NameOID.COMMON_NAME, u"cryptography.io CA")
            ])
        ).last_update(
            last_update
        ).next_update(
            next_update
        ).add_revoked_certificate(
            revoked_cert0
        ).add_extension(
            ian, False
        )

        crl = builder.sign(private_key, hashes.SHA256(), backend)
        assert crl.extensions.get_extension_for_class(
            x509.IssuerAlternativeName
        ).value == ian
        assert crl[0].serial_number == revoked_cert0.serial_number
        assert crl[0].revocation_date == revoked_cert0.revocation_date
        assert len(crl[0].extensions) == 1
        ext = crl[0].extensions.get_extension_for_class(x509.InvalidityDate)
        assert ext.critical is False
        assert ext.value == invalidity_date

    @pytest.mark.requires_backend_interface(interface=DSABackend)
    @pytest.mark.requires_backend_interface(interface=X509Backend)
    def test_dsa_key_sign_md5(self, backend):
        private_key = DSA_KEY_2048.private_key(backend)
        last_time = datetime.datetime(2012, 1, 16, 22, 43)
        next_time = datetime.datetime(2022, 1, 17, 6, 43)
        builder = x509.CertificateRevocationListBuilder().issuer_name(
            x509.Name([
                x509.NameAttribute(NameOID.COMMON_NAME, u"cryptography.io CA")
            ])
        ).last_update(last_time).next_update(next_time)

        with pytest.raises(ValueError):
            builder.sign(private_key, hashes.MD5(), backend)

    @pytest.mark.requires_backend_interface(interface=EllipticCurveBackend)
    @pytest.mark.requires_backend_interface(interface=X509Backend)
    def test_ec_key_sign_md5(self, backend):
        _skip_curve_unsupported(backend, ec.SECP256R1())
        private_key = EC_KEY_SECP256R1.private_key(backend)
        last_time = datetime.datetime(2012, 1, 16, 22, 43)
        next_time = datetime.datetime(2022, 1, 17, 6, 43)
        builder = x509.CertificateRevocationListBuilder().issuer_name(
            x509.Name([
                x509.NameAttribute(NameOID.COMMON_NAME, u"cryptography.io CA")
            ])
        ).last_update(last_time).next_update(next_time)

        with pytest.raises(ValueError):
            builder.sign(private_key, hashes.MD5(), backend)

    @pytest.mark.requires_backend_interface(interface=RSABackend)
    @pytest.mark.requires_backend_interface(interface=X509Backend)
    def test_sign_with_revoked_certificates(self, backend):
        private_key = RSA_KEY_2048.private_key(backend)
        last_update = datetime.datetime(2002, 1, 1, 12, 1)
        next_update = datetime.datetime(2030, 1, 1, 12, 1)
        invalidity_date = x509.InvalidityDate(
            datetime.datetime(2002, 1, 1, 0, 0)
        )
        revoked_cert0 = x509.RevokedCertificateBuilder().serial_number(
            38
        ).revocation_date(
            datetime.datetime(2011, 1, 1, 1, 1)
        ).build(backend)
        revoked_cert1 = x509.RevokedCertificateBuilder().serial_number(
            2
        ).revocation_date(
            datetime.datetime(2012, 1, 1, 1, 1)
        ).add_extension(
            invalidity_date, False
        ).build(backend)
        builder = x509.CertificateRevocationListBuilder().issuer_name(
            x509.Name([
                x509.NameAttribute(NameOID.COMMON_NAME, u"cryptography.io CA")
            ])
        ).last_update(
            last_update
        ).next_update(
            next_update
        ).add_revoked_certificate(
            revoked_cert0
        ).add_revoked_certificate(
            revoked_cert1
        )

        crl = builder.sign(private_key, hashes.SHA256(), backend)
        assert len(crl) == 2
        assert crl.last_update == last_update
        assert crl.next_update == next_update
        assert crl[0].serial_number == revoked_cert0.serial_number
        assert crl[0].revocation_date == revoked_cert0.revocation_date
        assert len(crl[0].extensions) == 0
        assert crl[1].serial_number == revoked_cert1.serial_number
        assert crl[1].revocation_date == revoked_cert1.revocation_date
        assert len(crl[1].extensions) == 1
        ext = crl[1].extensions.get_extension_for_class(x509.InvalidityDate)
        assert ext.critical is False
        assert ext.value == invalidity_date
예제 #8
0
    def sign_cert(self,
                  ca,
                  csr,
                  expires,
                  algorithm,
                  subject=None,
                  cn_in_san=True,
                  csr_format=Encoding.PEM,
                  subjectAltName=None,
                  keyUsage=None,
                  extendedKeyUsage=None,
                  tls_features=None,
                  password=None):
        """Create a signed certificate from a CSR.

        X509 extensions (`key_usage`, `ext_key_usage`) may either be None (in which case they are
        not added) or a tuple with the first value being a bool indicating if the value is critical
        and the second value being a byte-array indicating the extension value. Example::

            (True, b'value')

        Parameters
        ----------

        ca : :py:class:`~django_ca.models.CertificateAuthority`
            The certificate authority to sign the certificate with.
        csr : str
            A valid CSR. The format is given by the ``csr_format`` parameter.
        expires : int
            When the certificate should expire (passed to :py:func:`~django_ca.utils.get_cert_builder`).
        algorithm : {'sha512', 'sha256', ...}
            Algorithm used to sign the certificate. The default is the CA_DIGEST_ALGORITHM setting.
        subject : dict, optional
            The Subject to use in the certificate.  The keys of this dict are the fields of an X509
            subject, that is `"C"`, `"ST"`, `"L"`, `"OU"` and `"CN"`. If ommited or if the value
            does not contain a `"CN"` key, the first value of the `subjectAltName` parameter is
            used as CommonName (and is obviously mandatory in this case).
        cn_in_san : bool, optional
            Wether the CommonName should also be included as subjectAlternativeName. The default is
            `True`, but the parameter is ignored if no CommonName is given. This is typically set
            to `False` when creating a client certificate, where the subjects CommonName has no
            meaningful value as subjectAltName.
        csr_format : :py:class:`~cryptography:cryptography.hazmat.primitives.serialization.Encoding`, optional
            The format of the CSR. The default is ``PEM``.
        subjectAltName : list of str, optional
            A list of values for the subjectAltName extension. Values are passed to
            :py:func:`~django_ca.utils.parse_general_name`, see function documentation for how this value is
            parsed.
        keyUsage : tuple or None
            Value for the `keyUsage` X509 extension. See description for format details.
        extendedKeyUsage : tuple or None
            Value for the `extendedKeyUsage` X509 extension. See description for format details.
        tls_features : tuple
            Value for the `TLS Feature` X509 extension. See description for format details.
        password : bytes, optional
            Password used to load the private key of the certificate authority. If not passed, the private key
            is assumed to be unencrypted.

        Returns
        -------

        cryptography.x509.Certificate
            The signed certificate.
        """
        if subject is None:
            subject = {}
        if not subject.get('CN') and not subjectAltName:
            raise ValueError("Must name at least a CN or a subjectAltName.")

        if subjectAltName:
            subjectAltName = [
                parse_general_name(san) for san in subjectAltName
            ]
        else:
            subjectAltName = []  # so we can append the CN if requested

        if not subject.get('CN'):  # use first SAN as CN if CN is not set
            subject['CN'] = subjectAltName[0].value
        elif cn_in_san and subject.get(
                'CN'):  # add CN to SAN if cn_in_san is True (default)
            try:
                cn_name = parse_general_name(subject['CN'])
            except idna.IDNAError:
                raise ValueError(
                    '%s: Could not parse CommonName as subjectAltName.' %
                    subject['CN'])
            else:
                if cn_name not in subjectAltName:
                    subjectAltName.insert(0, cn_name)

        if csr_format == Encoding.PEM:
            req = x509.load_pem_x509_csr(force_bytes(csr), default_backend())
        elif csr_format == Encoding.DER:
            req = x509.load_der_x509_csr(force_bytes(csr), default_backend())
        else:
            raise ValueError('Unknown CSR format passed: %s' % csr_format)

        public_key = req.public_key()

        builder = get_cert_builder(expires)
        builder = builder.public_key(public_key)
        builder = builder.issuer_name(ca.x509.subject)

        builder = builder.subject_name(x509_name(subject))

        # Add extensions
        builder = builder.add_extension(x509.BasicConstraints(
            ca=False, path_length=None),
                                        critical=True)
        builder = builder.add_extension(
            x509.SubjectKeyIdentifier.from_public_key(public_key),
            critical=False)

        # Get authorityKeyIdentifier from subjectKeyIdentifier from signing CA
        ca_subject_key_id = ca.x509.extensions.get_extension_for_oid(
            ExtensionOID.SUBJECT_KEY_IDENTIFIER)
        auth_key_id = x509.AuthorityKeyIdentifier(
            key_identifier=ca_subject_key_id.value.digest,
            authority_cert_issuer=None,
            authority_cert_serial_number=None)
        builder = builder.add_extension(auth_key_id, critical=False)

        for critical, ext in self.get_common_extensions(
                ca.issuer_url, ca.crl_url, ca.ocsp_url):
            builder = builder.add_extension(ext, critical=critical)

        if subjectAltName:
            builder = builder.add_extension(
                x509.SubjectAlternativeName(subjectAltName), critical=False)

        if keyUsage:
            critical, values = keyUsage
            params = {v: False for v in KEY_USAGE_MAPPING.values()}
            for value in [KEY_USAGE_MAPPING[k] for k in values.split(',')]:
                params[value] = True
            builder = builder.add_extension(x509.KeyUsage(**params),
                                            critical=critical)

        if extendedKeyUsage:
            critical, usages = extendedKeyUsage
            usages = [EXTENDED_KEY_USAGE_MAPPING[u] for u in usages.split(',')]
            builder = builder.add_extension(x509.ExtendedKeyUsage(usages),
                                            critical=critical)

        if tls_features:
            critical, features = tls_features
            features = [TLS_FEATURE_MAPPING[f] for f in features.split(',')]
            builder = builder.add_extension(TLSFeature(features),
                                            critical=critical)

        if ca.issuer_alt_name:
            builder = builder.add_extension(x509.IssuerAlternativeName(
                [parse_general_name(ca.issuer_alt_name)]),
                                            critical=False)

        return builder.sign(private_key=ca.key(password),
                            algorithm=algorithm,
                            backend=default_backend()), req
예제 #9
0
class TestCertificateRevocationListBuilder:
    def test_issuer_name_invalid(self):
        builder = x509.CertificateRevocationListBuilder()
        with pytest.raises(TypeError):
            builder.issuer_name("notanx509name")  # type:ignore[arg-type]

    def test_set_issuer_name_twice(self):
        builder = x509.CertificateRevocationListBuilder().issuer_name(
            x509.Name([x509.NameAttribute(NameOID.COUNTRY_NAME, "US")]))
        with pytest.raises(ValueError):
            builder.issuer_name(
                x509.Name([x509.NameAttribute(NameOID.COUNTRY_NAME, "US")]))

    def test_aware_last_update(self, backend):
        last_time = datetime.datetime(2012, 1, 16, 22, 43)
        tz = pytz.timezone("US/Pacific")
        last_time = tz.localize(last_time)
        utc_last = datetime.datetime(2012, 1, 17, 6, 43)
        next_time = datetime.datetime(2022, 1, 17, 6, 43)
        private_key = RSA_KEY_2048.private_key(backend)
        builder = (x509.CertificateRevocationListBuilder().issuer_name(
            x509.Name([
                x509.NameAttribute(NameOID.COMMON_NAME, "cryptography.io CA")
            ])).last_update(last_time).next_update(next_time))

        crl = builder.sign(private_key, hashes.SHA256(), backend)
        assert crl.last_update == utc_last

    def test_last_update_invalid(self):
        builder = x509.CertificateRevocationListBuilder()
        with pytest.raises(TypeError):
            builder.last_update("notadatetime")  # type:ignore[arg-type]

    def test_last_update_before_1950(self):
        builder = x509.CertificateRevocationListBuilder()
        with pytest.raises(ValueError):
            builder.last_update(datetime.datetime(1940, 8, 10))

    def test_set_last_update_twice(self):
        builder = x509.CertificateRevocationListBuilder().last_update(
            datetime.datetime(2002, 1, 1, 12, 1))
        with pytest.raises(ValueError):
            builder.last_update(datetime.datetime(2002, 1, 1, 12, 1))

    def test_aware_next_update(self, backend):
        next_time = datetime.datetime(2022, 1, 16, 22, 43)
        tz = pytz.timezone("US/Pacific")
        next_time = tz.localize(next_time)
        utc_next = datetime.datetime(2022, 1, 17, 6, 43)
        last_time = datetime.datetime(2012, 1, 17, 6, 43)
        private_key = RSA_KEY_2048.private_key(backend)
        builder = (x509.CertificateRevocationListBuilder().issuer_name(
            x509.Name([
                x509.NameAttribute(NameOID.COMMON_NAME, "cryptography.io CA")
            ])).last_update(last_time).next_update(next_time))

        crl = builder.sign(private_key, hashes.SHA256(), backend)
        assert crl.next_update == utc_next

    def test_next_update_invalid(self):
        builder = x509.CertificateRevocationListBuilder()
        with pytest.raises(TypeError):
            builder.next_update("notadatetime")  # type:ignore[arg-type]

    def test_next_update_before_1950(self):
        builder = x509.CertificateRevocationListBuilder()
        with pytest.raises(ValueError):
            builder.next_update(datetime.datetime(1940, 8, 10))

    def test_set_next_update_twice(self):
        builder = x509.CertificateRevocationListBuilder().next_update(
            datetime.datetime(2002, 1, 1, 12, 1))
        with pytest.raises(ValueError):
            builder.next_update(datetime.datetime(2002, 1, 1, 12, 1))

    def test_last_update_after_next_update(self):
        builder = x509.CertificateRevocationListBuilder()

        builder = builder.next_update(datetime.datetime(2002, 1, 1, 12, 1))
        with pytest.raises(ValueError):
            builder.last_update(datetime.datetime(2003, 1, 1, 12, 1))

    def test_next_update_after_last_update(self):
        builder = x509.CertificateRevocationListBuilder()

        builder = builder.last_update(datetime.datetime(2002, 1, 1, 12, 1))
        with pytest.raises(ValueError):
            builder.next_update(datetime.datetime(2001, 1, 1, 12, 1))

    def test_add_extension_checks_for_duplicates(self):
        builder = x509.CertificateRevocationListBuilder().add_extension(
            x509.CRLNumber(1), False)

        with pytest.raises(ValueError):
            builder.add_extension(x509.CRLNumber(2), False)

    def test_add_invalid_extension(self):
        builder = x509.CertificateRevocationListBuilder()

        with pytest.raises(TypeError):
            builder.add_extension(object(), False)  # type:ignore[arg-type]

    def test_add_invalid_revoked_certificate(self):
        builder = x509.CertificateRevocationListBuilder()

        with pytest.raises(TypeError):
            builder.add_revoked_certificate(object())  # type:ignore[arg-type]

    def test_no_issuer_name(self, backend):
        private_key = RSA_KEY_2048.private_key(backend)
        builder = (x509.CertificateRevocationListBuilder().last_update(
            datetime.datetime(2002, 1, 1, 12, 1)).next_update(
                datetime.datetime(2030, 1, 1, 12, 1)))

        with pytest.raises(ValueError):
            builder.sign(private_key, hashes.SHA256(), backend)

    def test_no_last_update(self, backend):
        private_key = RSA_KEY_2048.private_key(backend)
        builder = (x509.CertificateRevocationListBuilder().issuer_name(
            x509.Name([x509.NameAttribute(NameOID.COUNTRY_NAME, "US")
                       ])).next_update(datetime.datetime(2030, 1, 1, 12, 1)))

        with pytest.raises(ValueError):
            builder.sign(private_key, hashes.SHA256(), backend)

    def test_no_next_update(self, backend):
        private_key = RSA_KEY_2048.private_key(backend)
        builder = (x509.CertificateRevocationListBuilder().issuer_name(
            x509.Name([x509.NameAttribute(NameOID.COUNTRY_NAME, "US")
                       ])).last_update(datetime.datetime(2030, 1, 1, 12, 1)))

        with pytest.raises(ValueError):
            builder.sign(private_key, hashes.SHA256(), backend)

    def test_sign_empty_list(self, backend):
        private_key = RSA_KEY_2048.private_key(backend)
        last_update = datetime.datetime(2002, 1, 1, 12, 1)
        next_update = datetime.datetime(2030, 1, 1, 12, 1)
        builder = (x509.CertificateRevocationListBuilder().issuer_name(
            x509.Name([
                x509.NameAttribute(NameOID.COMMON_NAME, "cryptography.io CA")
            ])).last_update(last_update).next_update(next_update))

        crl = builder.sign(private_key, hashes.SHA256(), backend)
        assert len(crl) == 0
        assert crl.last_update == last_update
        assert crl.next_update == next_update

    @pytest.mark.parametrize(
        "extension",
        [
            x509.CRLNumber(13),
            x509.DeltaCRLIndicator(12345678901234567890),
            x509.AuthorityKeyIdentifier(
                b"\xc3\x9c\xf3\xfc\xd3F\x084\xbb\xceF\x7f\xa0|[\xf3\xe2\x08"
                b"\xcbY",
                None,
                None,
            ),
            x509.AuthorityInformationAccess([
                x509.AccessDescription(
                    AuthorityInformationAccessOID.CA_ISSUERS,
                    x509.DNSName("cryptography.io"),
                )
            ]),
            x509.IssuerAlternativeName(
                [x509.UniformResourceIdentifier("https://cryptography.io")]),
        ],
    )
    def test_sign_extensions(self, backend, extension):
        private_key = RSA_KEY_2048.private_key(backend)
        last_update = datetime.datetime(2002, 1, 1, 12, 1)
        next_update = datetime.datetime(2030, 1, 1, 12, 1)
        builder = (x509.CertificateRevocationListBuilder().issuer_name(
            x509.Name([
                x509.NameAttribute(NameOID.COMMON_NAME, "cryptography.io CA")
            ])).last_update(last_update).next_update(
                next_update).add_extension(extension, False))

        crl = builder.sign(private_key, hashes.SHA256(), backend)
        assert len(crl) == 0
        assert len(crl.extensions) == 1
        ext = crl.extensions.get_extension_for_class(type(extension))
        assert ext.critical is False
        assert ext.value == extension

    def test_sign_multiple_extensions_critical(self, backend):
        private_key = RSA_KEY_2048.private_key(backend)
        last_update = datetime.datetime(2002, 1, 1, 12, 1)
        next_update = datetime.datetime(2030, 1, 1, 12, 1)
        ian = x509.IssuerAlternativeName(
            [x509.UniformResourceIdentifier("https://cryptography.io")])
        crl_number = x509.CRLNumber(13)
        builder = (x509.CertificateRevocationListBuilder().issuer_name(
            x509.Name([
                x509.NameAttribute(NameOID.COMMON_NAME, "cryptography.io CA")
            ])).last_update(last_update).next_update(
                next_update).add_extension(crl_number,
                                           False).add_extension(ian, True))

        crl = builder.sign(private_key, hashes.SHA256(), backend)
        assert len(crl) == 0
        assert len(crl.extensions) == 2
        ext1 = crl.extensions.get_extension_for_class(x509.CRLNumber)
        assert ext1.critical is False
        assert ext1.value == crl_number
        ext2 = crl.extensions.get_extension_for_class(
            x509.IssuerAlternativeName)
        assert ext2.critical is True
        assert ext2.value == ian

    def test_freshestcrl_extension(self, backend):
        private_key = RSA_KEY_2048.private_key(backend)
        last_update = datetime.datetime(2002, 1, 1, 12, 1)
        next_update = datetime.datetime(2030, 1, 1, 12, 1)
        freshest = x509.FreshestCRL([
            x509.DistributionPoint(
                [x509.UniformResourceIdentifier("http://d.om/delta")],
                None,
                None,
                None,
            )
        ])
        builder = (x509.CertificateRevocationListBuilder().issuer_name(
            x509.Name([
                x509.NameAttribute(NameOID.COMMON_NAME, "cryptography.io CA")
            ])).last_update(last_update).next_update(
                next_update).add_extension(freshest, False))

        crl = builder.sign(private_key, hashes.SHA256(), backend)
        assert len(crl) == 0
        assert len(crl.extensions) == 1
        ext1 = crl.extensions.get_extension_for_class(x509.FreshestCRL)
        assert ext1.critical is False
        assert isinstance(ext1.value, x509.FreshestCRL)
        assert isinstance(ext1.value[0], x509.DistributionPoint)
        assert ext1.value[0].full_name is not None
        uri = ext1.value[0].full_name[0]
        assert isinstance(uri, x509.UniformResourceIdentifier)
        assert uri.value == "http://d.om/delta"

    def test_add_unsupported_extension(self, backend):
        private_key = RSA_KEY_2048.private_key(backend)
        last_update = datetime.datetime(2002, 1, 1, 12, 1)
        next_update = datetime.datetime(2030, 1, 1, 12, 1)
        builder = (x509.CertificateRevocationListBuilder().issuer_name(
            x509.Name([
                x509.NameAttribute(NameOID.COMMON_NAME, "cryptography.io CA")
            ])).last_update(last_update).next_update(
                next_update).add_extension(DummyExtension(), False))
        with pytest.raises(NotImplementedError):
            builder.sign(private_key, hashes.SHA256(), backend)

    def test_add_unsupported_entry_extension(self, backend):
        private_key = RSA_KEY_2048.private_key(backend)
        last_update = datetime.datetime(2002, 1, 1, 12, 1)
        next_update = datetime.datetime(2030, 1, 1, 12, 1)
        builder = (x509.CertificateRevocationListBuilder().issuer_name(
            x509.Name([
                x509.NameAttribute(NameOID.COMMON_NAME, "cryptography.io CA")
            ])).last_update(last_update).next_update(
                next_update).add_revoked_certificate(
                    x509.RevokedCertificateBuilder().serial_number(
                        1234).revocation_date(
                            datetime.datetime.utcnow()).add_extension(
                                DummyExtension(), critical=False).build()))
        with pytest.raises(NotImplementedError):
            builder.sign(private_key, hashes.SHA256(), backend)

    def test_sign_rsa_key_too_small(self, backend):
        private_key = RSA_KEY_512.private_key(backend)
        last_update = datetime.datetime(2002, 1, 1, 12, 1)
        next_update = datetime.datetime(2030, 1, 1, 12, 1)
        builder = (x509.CertificateRevocationListBuilder().issuer_name(
            x509.Name([
                x509.NameAttribute(NameOID.COMMON_NAME, "cryptography.io CA")
            ])).last_update(last_update).next_update(next_update))

        with pytest.raises(ValueError):
            builder.sign(private_key, hashes.SHA512(), backend)

    def test_sign_with_invalid_hash(self, backend):
        private_key = RSA_KEY_2048.private_key(backend)
        last_update = datetime.datetime(2002, 1, 1, 12, 1)
        next_update = datetime.datetime(2030, 1, 1, 12, 1)
        builder = (x509.CertificateRevocationListBuilder().issuer_name(
            x509.Name([
                x509.NameAttribute(NameOID.COMMON_NAME, "cryptography.io CA")
            ])).last_update(last_update).next_update(next_update))

        with pytest.raises(TypeError):
            builder.sign(
                private_key,
                object(),
                backend  # type: ignore[arg-type]
            )

    @pytest.mark.supported(
        only_if=lambda backend: backend.ed25519_supported(),
        skip_message="Requires OpenSSL with Ed25519 support",
    )
    def test_sign_with_invalid_hash_ed25519(self, backend):
        private_key = ed25519.Ed25519PrivateKey.generate()
        last_update = datetime.datetime(2002, 1, 1, 12, 1)
        next_update = datetime.datetime(2030, 1, 1, 12, 1)
        builder = (x509.CertificateRevocationListBuilder().issuer_name(
            x509.Name([
                x509.NameAttribute(NameOID.COMMON_NAME, "cryptography.io CA")
            ])).last_update(last_update).next_update(next_update))

        with pytest.raises(TypeError):
            builder.sign(
                private_key,
                object(),  # type:ignore[arg-type]
                backend,
            )
        with pytest.raises(ValueError):
            builder.sign(private_key, hashes.SHA256(), backend)

    @pytest.mark.supported(
        only_if=lambda backend: backend.ed448_supported(),
        skip_message="Requires OpenSSL with Ed448 support",
    )
    def test_sign_with_invalid_hash_ed448(self, backend):
        private_key = ed448.Ed448PrivateKey.generate()
        last_update = datetime.datetime(2002, 1, 1, 12, 1)
        next_update = datetime.datetime(2030, 1, 1, 12, 1)
        builder = (x509.CertificateRevocationListBuilder().issuer_name(
            x509.Name([
                x509.NameAttribute(NameOID.COMMON_NAME, "cryptography.io CA")
            ])).last_update(last_update).next_update(next_update))

        with pytest.raises(TypeError):
            builder.sign(
                private_key,
                object(),  # type:ignore[arg-type]
                backend,
            )
        with pytest.raises(ValueError):
            builder.sign(private_key, hashes.SHA256(), backend)

    def test_sign_dsa_key(self, backend):
        private_key = DSA_KEY_2048.private_key(backend)
        invalidity_date = x509.InvalidityDate(
            datetime.datetime(2002, 1, 1, 0, 0))
        ian = x509.IssuerAlternativeName(
            [x509.UniformResourceIdentifier("https://cryptography.io")])
        revoked_cert0 = (
            x509.RevokedCertificateBuilder().serial_number(2).revocation_date(
                datetime.datetime(2012, 1, 1, 1,
                                  1)).add_extension(invalidity_date,
                                                    False).build(backend))
        last_update = datetime.datetime(2002, 1, 1, 12, 1)
        next_update = datetime.datetime(2030, 1, 1, 12, 1)
        builder = (x509.CertificateRevocationListBuilder().issuer_name(
            x509.Name([
                x509.NameAttribute(NameOID.COMMON_NAME, "cryptography.io CA")
            ])).last_update(last_update).next_update(next_update).
                   add_revoked_certificate(revoked_cert0).add_extension(
                       ian, False))

        crl = builder.sign(private_key, hashes.SHA256(), backend)
        assert (crl.extensions.get_extension_for_class(
            x509.IssuerAlternativeName).value == ian)
        assert crl[0].serial_number == revoked_cert0.serial_number
        assert crl[0].revocation_date == revoked_cert0.revocation_date
        assert len(crl[0].extensions) == 1
        ext = crl[0].extensions.get_extension_for_class(x509.InvalidityDate)
        assert ext.critical is False
        assert ext.value == invalidity_date

    def test_sign_ec_key(self, backend):
        _skip_curve_unsupported(backend, ec.SECP256R1())
        private_key = ec.generate_private_key(ec.SECP256R1(), backend)
        invalidity_date = x509.InvalidityDate(
            datetime.datetime(2002, 1, 1, 0, 0))
        ian = x509.IssuerAlternativeName(
            [x509.UniformResourceIdentifier("https://cryptography.io")])
        revoked_cert0 = (
            x509.RevokedCertificateBuilder().serial_number(2).revocation_date(
                datetime.datetime(2012, 1, 1, 1,
                                  1)).add_extension(invalidity_date,
                                                    False).build(backend))
        last_update = datetime.datetime(2002, 1, 1, 12, 1)
        next_update = datetime.datetime(2030, 1, 1, 12, 1)
        builder = (x509.CertificateRevocationListBuilder().issuer_name(
            x509.Name([
                x509.NameAttribute(NameOID.COMMON_NAME, "cryptography.io CA")
            ])).last_update(last_update).next_update(next_update).
                   add_revoked_certificate(revoked_cert0).add_extension(
                       ian, False))

        crl = builder.sign(private_key, hashes.SHA256(), backend)
        assert (crl.extensions.get_extension_for_class(
            x509.IssuerAlternativeName).value == ian)
        assert crl[0].serial_number == revoked_cert0.serial_number
        assert crl[0].revocation_date == revoked_cert0.revocation_date
        assert len(crl[0].extensions) == 1
        ext = crl[0].extensions.get_extension_for_class(x509.InvalidityDate)
        assert ext.critical is False
        assert ext.value == invalidity_date

    @pytest.mark.supported(
        only_if=lambda backend: backend.ed25519_supported(),
        skip_message="Requires OpenSSL with Ed25519 support",
    )
    def test_sign_ed25519_key(self, backend):
        private_key = ed25519.Ed25519PrivateKey.generate()
        invalidity_date = x509.InvalidityDate(
            datetime.datetime(2002, 1, 1, 0, 0))
        ian = x509.IssuerAlternativeName(
            [x509.UniformResourceIdentifier("https://cryptography.io")])
        revoked_cert0 = (
            x509.RevokedCertificateBuilder().serial_number(2).revocation_date(
                datetime.datetime(2012, 1, 1, 1,
                                  1)).add_extension(invalidity_date,
                                                    False).build(backend))
        last_update = datetime.datetime(2002, 1, 1, 12, 1)
        next_update = datetime.datetime(2030, 1, 1, 12, 1)
        builder = (x509.CertificateRevocationListBuilder().issuer_name(
            x509.Name([
                x509.NameAttribute(NameOID.COMMON_NAME, "cryptography.io CA")
            ])).last_update(last_update).next_update(next_update).
                   add_revoked_certificate(revoked_cert0).add_extension(
                       ian, False))

        crl = builder.sign(private_key, None, backend)
        assert crl.signature_hash_algorithm is None
        assert crl.signature_algorithm_oid == SignatureAlgorithmOID.ED25519
        assert (crl.extensions.get_extension_for_class(
            x509.IssuerAlternativeName).value == ian)
        assert crl[0].serial_number == revoked_cert0.serial_number
        assert crl[0].revocation_date == revoked_cert0.revocation_date
        assert len(crl[0].extensions) == 1
        ext = crl[0].extensions.get_extension_for_class(x509.InvalidityDate)
        assert ext.critical is False
        assert ext.value == invalidity_date

    @pytest.mark.supported(
        only_if=lambda backend: backend.ed448_supported(),
        skip_message="Requires OpenSSL with Ed448 support",
    )
    def test_sign_ed448_key(self, backend):
        private_key = ed448.Ed448PrivateKey.generate()
        invalidity_date = x509.InvalidityDate(
            datetime.datetime(2002, 1, 1, 0, 0))
        ian = x509.IssuerAlternativeName(
            [x509.UniformResourceIdentifier("https://cryptography.io")])
        revoked_cert0 = (
            x509.RevokedCertificateBuilder().serial_number(2).revocation_date(
                datetime.datetime(2012, 1, 1, 1,
                                  1)).add_extension(invalidity_date,
                                                    False).build(backend))
        last_update = datetime.datetime(2002, 1, 1, 12, 1)
        next_update = datetime.datetime(2030, 1, 1, 12, 1)
        builder = (x509.CertificateRevocationListBuilder().issuer_name(
            x509.Name([
                x509.NameAttribute(NameOID.COMMON_NAME, "cryptography.io CA")
            ])).last_update(last_update).next_update(next_update).
                   add_revoked_certificate(revoked_cert0).add_extension(
                       ian, False))

        crl = builder.sign(private_key, None, backend)
        assert crl.signature_hash_algorithm is None
        assert crl.signature_algorithm_oid == SignatureAlgorithmOID.ED448
        assert (crl.extensions.get_extension_for_class(
            x509.IssuerAlternativeName).value == ian)
        assert crl[0].serial_number == revoked_cert0.serial_number
        assert crl[0].revocation_date == revoked_cert0.revocation_date
        assert len(crl[0].extensions) == 1
        ext = crl[0].extensions.get_extension_for_class(x509.InvalidityDate)
        assert ext.critical is False
        assert ext.value == invalidity_date

    def test_dsa_key_sign_md5(self, backend):
        private_key = DSA_KEY_2048.private_key(backend)
        last_time = datetime.datetime(2012, 1, 16, 22, 43)
        next_time = datetime.datetime(2022, 1, 17, 6, 43)
        builder = (x509.CertificateRevocationListBuilder().issuer_name(
            x509.Name([
                x509.NameAttribute(NameOID.COMMON_NAME, "cryptography.io CA")
            ])).last_update(last_time).next_update(next_time))

        with pytest.raises(ValueError):
            builder.sign(private_key, hashes.MD5(), backend)

    def test_ec_key_sign_md5(self, backend):
        _skip_curve_unsupported(backend, ec.SECP256R1())
        private_key = EC_KEY_SECP256R1.private_key(backend)
        last_time = datetime.datetime(2012, 1, 16, 22, 43)
        next_time = datetime.datetime(2022, 1, 17, 6, 43)
        builder = (x509.CertificateRevocationListBuilder().issuer_name(
            x509.Name([
                x509.NameAttribute(NameOID.COMMON_NAME, "cryptography.io CA")
            ])).last_update(last_time).next_update(next_time))

        with pytest.raises(ValueError):
            builder.sign(private_key, hashes.MD5(), backend)

    def test_sign_with_revoked_certificates(self, backend):
        private_key = RSA_KEY_2048.private_key(backend)
        last_update = datetime.datetime(2002, 1, 1, 12, 1)
        next_update = datetime.datetime(2030, 1, 1, 12, 1)
        invalidity_date = x509.InvalidityDate(
            datetime.datetime(2002, 1, 1, 0, 0))
        revoked_cert0 = (
            x509.RevokedCertificateBuilder().serial_number(38).revocation_date(
                datetime.datetime(2011, 1, 1, 1, 1)).build(backend))
        revoked_cert1 = (
            x509.RevokedCertificateBuilder().serial_number(2).revocation_date(
                datetime.datetime(2012, 1, 1, 1, 1)).add_extension(
                    invalidity_date, False).add_extension(
                        x509.CRLReason(x509.ReasonFlags.ca_compromise),
                        False).build(backend))
        ci = x509.CertificateIssuer([x509.DNSName("cryptography.io")])
        revoked_cert2 = (
            x509.RevokedCertificateBuilder().serial_number(40).revocation_date(
                datetime.datetime(2011, 1, 1, 1,
                                  1)).add_extension(ci, False).build(backend))
        builder = (
            x509.CertificateRevocationListBuilder().issuer_name(
                x509.Name([
                    x509.NameAttribute(NameOID.COMMON_NAME,
                                       "cryptography.io CA")
                ])).last_update(last_update).next_update(next_update).
            add_revoked_certificate(revoked_cert0).add_revoked_certificate(
                revoked_cert1).add_revoked_certificate(revoked_cert2))

        crl = builder.sign(private_key, hashes.SHA256(), backend)
        assert len(crl) == 3
        assert crl.last_update == last_update
        assert crl.next_update == next_update
        assert crl[0].serial_number == revoked_cert0.serial_number
        assert crl[0].revocation_date == revoked_cert0.revocation_date
        assert len(crl[0].extensions) == 0
        assert crl[1].serial_number == revoked_cert1.serial_number
        assert crl[1].revocation_date == revoked_cert1.revocation_date
        assert len(crl[1].extensions) == 2
        ext = crl[1].extensions.get_extension_for_class(x509.InvalidityDate)
        assert ext.critical is False
        assert ext.value == invalidity_date
        assert (crl[2].extensions.get_extension_for_class(
            x509.CertificateIssuer).value == ci)
예제 #10
0
 def extension_type(self) -> x509.IssuerAlternativeName:
     return x509.IssuerAlternativeName(self.value)
예제 #11
0
def generate_ca_cert(ca_crt: str, ca_key: str, organization: str, unit: str,
                     locality: str, state: str, country: str, validity: int,
                     key_length: int, verbose: bool):

    today = datetime.datetime.today()
    one_day = datetime.timedelta(days=1)
    one_year = datetime.timedelta(days=validity)
    ca_cn = f'CA-{uuid.uuid1()}'
    email = f'nobody@{ca_cn}'
    serial = x509.random_serial_number()
    name = x509.Name([
        x509.NameAttribute(NameOID.COMMON_NAME, ca_cn),
        x509.NameAttribute(NameOID.ORGANIZATION_NAME, organization),
        x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME, unit),
        x509.NameAttribute(NameOID.LOCALITY_NAME, locality),
        x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, state),
        x509.NameAttribute(NameOID.COUNTRY_NAME, country),
        x509.NameAttribute(NameOID.EMAIL_ADDRESS, email),
    ])

    # Generate private key
    private_key = rsa.generate_private_key(public_exponent=65537,
                                           key_size=key_length,
                                           backend=default_backend())

    # Generate public key
    public_key = private_key.public_key()
    builder = x509.CertificateBuilder()
    builder = builder.subject_name(
        x509.Name([
            x509.NameAttribute(NameOID.COMMON_NAME, ca_cn),
            x509.NameAttribute(NameOID.ORGANIZATION_NAME, organization),
            x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME, unit),
            x509.NameAttribute(NameOID.LOCALITY_NAME, locality),
            x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, state),
            x509.NameAttribute(NameOID.COUNTRY_NAME, country),
            x509.NameAttribute(NameOID.EMAIL_ADDRESS, email),
        ]))

    builder = builder.issuer_name(name)
    builder = builder.not_valid_before(today - one_day)
    builder = builder.not_valid_after(today + one_year)
    builder = builder.serial_number(serial)
    builder = builder.public_key(public_key)

    # X509v3 Basic Constraints
    builder = builder.add_extension(x509.BasicConstraints(ca=True,
                                                          path_length=None),
                                    critical=True)

    # X509v3 Subject Key Identifier
    builder = builder.add_extension(
        x509.SubjectKeyIdentifier.from_public_key(public_key), critical=False)

    # X509v3 Authority Key Identifier
    ki = x509.SubjectKeyIdentifier.from_public_key(private_key.public_key())
    builder = builder.add_extension(x509.AuthorityKeyIdentifier(
        key_identifier=ki.digest,
        authority_cert_issuer=[x509.DirectoryName(name)],
        authority_cert_serial_number=serial),
                                    critical=False)

    # X509v3 Key Usage: critical
    builder = builder.add_extension(x509.KeyUsage(digital_signature=True,
                                                  key_encipherment=True,
                                                  content_commitment=False,
                                                  data_encipherment=False,
                                                  key_agreement=False,
                                                  encipher_only=False,
                                                  decipher_only=False,
                                                  key_cert_sign=True,
                                                  crl_sign=False),
                                    critical=True)

    # X509v3 Extended Key Usage
    # ekus = [ExtendedKeyUsageOID.SERVER_AUTH, ExtendedKeyUsageOID.CLIENT_AUTH]
    # builder = builder.add_extension(x509.ExtendedKeyUsage(ekus), critical=False)

    # X509v3 Subject Alternative Name
    builder = builder.add_extension(x509.SubjectAlternativeName(
        [x509.DNSName(ca_cn)]),
                                    critical=False)

    # X509v3 Issuer Alternative Name
    builder = builder.add_extension(x509.IssuerAlternativeName(
        [x509.RFC822Name(email)]),
                                    critical=False)

    certificate = builder.sign(private_key=private_key,
                               algorithm=hashes.SHA256(),
                               backend=default_backend())

    with Path(ca_key) as file:
        ca_key_bytes = private_key.private_bytes(
            encoding=serialization.Encoding.PEM,
            format=serialization.PrivateFormat.PKCS8,
            encryption_algorithm=serialization.NoEncryption())
        file.write_bytes(ca_key_bytes)

    with Path(ca_crt) as file:
        ca_crt_bytes = certificate.public_bytes(
            encoding=serialization.Encoding.PEM)
        file.write_bytes(ca_crt_bytes)