Add Certificate.verify_signed_by (#8011)

* Add Certificate.verify_signed_by

Verify that the signature on a certificate was created by the
private key belonging to another certificate's public key.

This code does not validate anything else! It is not a path builder,
general x509 validator, etc.

* switch to issued_by

validate issuer subject matches certificate issuer and refactor

* two fixes

* signed_by isn't the right target now

* coverage

* skip test on some *ssls

* extensive refactoring

* lol

* does any of this work

* final commit i swear
This commit is contained in:
Paul Kehrer 2023-01-12 12:32:52 +08:00 committed by GitHub
parent 796ebf6702
commit db7dd61de3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 550 additions and 3 deletions

View file

@ -24,6 +24,8 @@ Changelog
continues to support only public keys.
* Added support for generating SSH certificates with
:class:`~cryptography.hazmat.primitives.serialization.SSHCertificateBuilder`.
* Added :meth:`~cryptography.x509.Certificate.verify_directly_issued_by` to
:class:`~cryptography.x509.Certificate`.
.. _v39-0-0:

View file

@ -486,6 +486,35 @@ X.509 Certificate Object
An :class:`~cryptography.exceptions.InvalidSignature` exception will be
raised if the signature fails to verify.
.. method:: verify_directly_issued_by(issuer)
.. versionadded:: 40.0
:param issuer: The issuer certificate to check against.
:type issuer: :class:`~cryptography.x509.Certificate`
.. warning::
This method verifies that the certificate issuer name matches the
issuer subject name and that the certificate is signed by the
issuer's private key. **No other validation is performed.**
Callers are responsible for performing any additional
validations required for their use case (e.g. checking the validity
period, whether the signer is allowed to issue certificates,
that the issuing certificate has a strong public key, etc).
Validates that the certificate is signed by the provided issuer and
that the issuer's subject name matches the issuer name of the
certificate.
:return: None
:raise ValueError: If the issuer name on the certificate does
not match the subject name of the issuer or the signature
algorithm is unsupported.
:raise TypeError: If the issuer does not have a supported public
key type.
:raise cryptography.exceptions.InvalidSignature: If the
signature fails to verify.
.. attribute:: tbs_precertificate_bytes

View file

@ -265,6 +265,14 @@ class Certificate(metaclass=abc.ABCMeta):
Serializes the certificate to PEM or DER format.
"""
@abc.abstractmethod
def verify_directly_issued_by(self, issuer: "Certificate") -> None:
"""
This method verifies that certificate issuer name matches the
issuer subject name and that the certificate is signed by the
issuer's private key. No other validation is performed.
"""
# Runtime isinstance checks need this since the rust class is not a subclass.
Certificate.register(rust_x509.Certificate)

View file

@ -7,7 +7,7 @@ use crate::asn1::{
PyAsn1Error, PyAsn1Result,
};
use crate::x509;
use crate::x509::{crl, extensions, oid, sct, Asn1ReadableOrWritable};
use crate::x509::{crl, extensions, oid, sct, sign, Asn1ReadableOrWritable};
use chrono::Datelike;
use pyo3::ToPyObject;
use std::collections::hash_map::DefaultHasher;
@ -319,6 +319,30 @@ impl Certificate {
},
)
}
fn verify_directly_issued_by<'p>(
&self,
py: pyo3::Python<'p>,
issuer: pyo3::PyRef<'_, Certificate>,
) -> PyAsn1Result<()> {
if self.raw.borrow_value().tbs_cert.signature_alg != self.raw.borrow_value().signature_alg {
return Err(PyAsn1Error::from(pyo3::exceptions::PyValueError::new_err(
"Inner and outer signature algorithms do not match. This is an invalid certificate."
)));
};
if self.raw.borrow_value().tbs_cert.issuer != issuer.raw.borrow_value().tbs_cert.subject {
return Err(PyAsn1Error::from(pyo3::exceptions::PyValueError::new_err(
"Issuer certificate subject does not match certificate issuer.",
)));
};
sign::verify_signature_with_oid(
py,
issuer.public_key(py)?,
&self.raw.borrow_value().signature_alg.oid,
self.raw.borrow_value().signature.as_bytes(),
&asn1::write_single(&self.raw.borrow_value().tbs_cert)?,
)
}
}
fn cert_version(py: pyo3::Python<'_>, version: u8) -> Result<&pyo3::PyAny, PyAsn1Error> {

View file

@ -2,6 +2,7 @@
// 2.0, and the BSD License. See the LICENSE file in the root of this repository
// for complete details.
use crate::asn1::{PyAsn1Error, PyAsn1Result};
use crate::x509;
use crate::x509::oid;
@ -14,6 +15,7 @@ static NULL_DER: Lazy<Vec<u8>> = Lazy::new(|| {
pub(crate) static NULL_TLV: Lazy<asn1::Tlv<'static>> =
Lazy::new(|| asn1::parse_single(&NULL_DER).unwrap());
#[derive(Debug, PartialEq)]
enum KeyType {
Rsa,
Dsa,
@ -22,6 +24,7 @@ enum KeyType {
Ed448,
}
#[derive(Debug, PartialEq)]
enum HashType {
None,
Sha224,
@ -256,3 +259,253 @@ pub(crate) fn sign_data<'p>(
};
signature.extract()
}
fn py_hash_name_from_hash_type(hash_type: HashType) -> Option<&'static str> {
match hash_type {
HashType::None => None,
HashType::Sha224 => Some("SHA224"),
HashType::Sha256 => Some("SHA256"),
HashType::Sha384 => Some("SHA384"),
HashType::Sha512 => Some("SHA512"),
HashType::Sha3_224 => Some("SHA3_224"),
HashType::Sha3_256 => Some("SHA3_256"),
HashType::Sha3_384 => Some("SHA3_384"),
HashType::Sha3_512 => Some("SHA3_512"),
}
}
pub(crate) fn verify_signature_with_oid<'p>(
py: pyo3::Python<'p>,
issuer_public_key: &'p pyo3::PyAny,
signature_oid: &asn1::ObjectIdentifier,
signature: &[u8],
data: &[u8],
) -> PyAsn1Result<()> {
let key_type = identify_public_key_type(py, issuer_public_key)?;
let (sig_key_type, sig_hash_type) = identify_key_hash_type_for_oid(signature_oid)?;
if key_type != sig_key_type {
return Err(PyAsn1Error::from(pyo3::exceptions::PyValueError::new_err(
"Signature algorithm does not match issuer key type",
)));
}
let sig_hash_name = py_hash_name_from_hash_type(sig_hash_type);
let hashes = py.import("cryptography.hazmat.primitives.hashes")?;
let signature_hash = match sig_hash_name {
Some(data) => hashes.getattr(data)?.call0()?,
None => py.None().into_ref(py),
};
match key_type {
KeyType::Ed25519 | KeyType::Ed448 => {
issuer_public_key.call_method1("verify", (signature, data))?
}
KeyType::Ec => {
let ec_mod = py.import("cryptography.hazmat.primitives.asymmetric.ec")?;
let ecdsa = ec_mod
.getattr(crate::intern!(py, "ECDSA"))?
.call1((signature_hash,))?;
issuer_public_key.call_method1("verify", (signature, data, ecdsa))?
}
KeyType::Rsa => {
let padding_mod = py.import("cryptography.hazmat.primitives.asymmetric.padding")?;
let pkcs1v15 = padding_mod
.getattr(crate::intern!(py, "PKCS1v15"))?
.call0()?;
issuer_public_key.call_method1("verify", (signature, data, pkcs1v15, signature_hash))?
}
KeyType::Dsa => {
issuer_public_key.call_method1("verify", (signature, data, signature_hash))?
}
};
Ok(())
}
fn identify_public_key_type(
py: pyo3::Python<'_>,
public_key: &pyo3::PyAny,
) -> pyo3::PyResult<KeyType> {
let rsa_key_type: &pyo3::types::PyType = py
.import("cryptography.hazmat.primitives.asymmetric.rsa")?
.getattr(crate::intern!(py, "RSAPublicKey"))?
.extract()?;
let dsa_key_type: &pyo3::types::PyType = py
.import("cryptography.hazmat.primitives.asymmetric.dsa")?
.getattr(crate::intern!(py, "DSAPublicKey"))?
.extract()?;
let ec_key_type: &pyo3::types::PyType = py
.import("cryptography.hazmat.primitives.asymmetric.ec")?
.getattr(crate::intern!(py, "EllipticCurvePublicKey"))?
.extract()?;
let ed25519_key_type: &pyo3::types::PyType = py
.import("cryptography.hazmat.primitives.asymmetric.ed25519")?
.getattr(crate::intern!(py, "Ed25519PublicKey"))?
.extract()?;
let ed448_key_type: &pyo3::types::PyType = py
.import("cryptography.hazmat.primitives.asymmetric.ed448")?
.getattr(crate::intern!(py, "Ed448PublicKey"))?
.extract()?;
if rsa_key_type.is_instance(public_key)? {
Ok(KeyType::Rsa)
} else if dsa_key_type.is_instance(public_key)? {
Ok(KeyType::Dsa)
} else if ec_key_type.is_instance(public_key)? {
Ok(KeyType::Ec)
} else if ed25519_key_type.is_instance(public_key)? {
Ok(KeyType::Ed25519)
} else if ed448_key_type.is_instance(public_key)? {
Ok(KeyType::Ed448)
} else {
Err(pyo3::exceptions::PyTypeError::new_err(
"Key must be an rsa, dsa, ec, ed25519, or ed448 public key.",
))
}
}
fn identify_key_hash_type_for_oid(
oid: &asn1::ObjectIdentifier,
) -> pyo3::PyResult<(KeyType, HashType)> {
match *oid {
oid::RSA_WITH_SHA224_OID => Ok((KeyType::Rsa, HashType::Sha224)),
oid::RSA_WITH_SHA256_OID => Ok((KeyType::Rsa, HashType::Sha256)),
oid::RSA_WITH_SHA384_OID => Ok((KeyType::Rsa, HashType::Sha384)),
oid::RSA_WITH_SHA512_OID => Ok((KeyType::Rsa, HashType::Sha512)),
oid::RSA_WITH_SHA3_224_OID => Ok((KeyType::Rsa, HashType::Sha3_224)),
oid::RSA_WITH_SHA3_256_OID => Ok((KeyType::Rsa, HashType::Sha3_256)),
oid::RSA_WITH_SHA3_384_OID => Ok((KeyType::Rsa, HashType::Sha3_384)),
oid::RSA_WITH_SHA3_512_OID => Ok((KeyType::Rsa, HashType::Sha3_512)),
oid::ECDSA_WITH_SHA224_OID => Ok((KeyType::Ec, HashType::Sha224)),
oid::ECDSA_WITH_SHA256_OID => Ok((KeyType::Ec, HashType::Sha256)),
oid::ECDSA_WITH_SHA384_OID => Ok((KeyType::Ec, HashType::Sha384)),
oid::ECDSA_WITH_SHA512_OID => Ok((KeyType::Ec, HashType::Sha512)),
oid::ECDSA_WITH_SHA3_224_OID => Ok((KeyType::Ec, HashType::Sha3_224)),
oid::ECDSA_WITH_SHA3_256_OID => Ok((KeyType::Ec, HashType::Sha3_256)),
oid::ECDSA_WITH_SHA3_384_OID => Ok((KeyType::Ec, HashType::Sha3_384)),
oid::ECDSA_WITH_SHA3_512_OID => Ok((KeyType::Ec, HashType::Sha3_512)),
oid::ED25519_OID => Ok((KeyType::Ed25519, HashType::None)),
oid::ED448_OID => Ok((KeyType::Ed448, HashType::None)),
oid::DSA_WITH_SHA224_OID => Ok((KeyType::Dsa, HashType::Sha224)),
oid::DSA_WITH_SHA256_OID => Ok((KeyType::Dsa, HashType::Sha256)),
oid::DSA_WITH_SHA384_OID => Ok((KeyType::Dsa, HashType::Sha384)),
oid::DSA_WITH_SHA512_OID => Ok((KeyType::Dsa, HashType::Sha512)),
_ => Err(pyo3::exceptions::PyValueError::new_err(
"Unsupported signature algorithm",
)),
}
}
#[cfg(test)]
mod tests {
use super::{identify_key_hash_type_for_oid, py_hash_name_from_hash_type, HashType, KeyType};
use crate::x509::oid;
#[test]
fn test_identify_key_hash_type_for_oid() {
assert_eq!(
identify_key_hash_type_for_oid(&oid::RSA_WITH_SHA224_OID).unwrap(),
(KeyType::Rsa, HashType::Sha224)
);
assert_eq!(
identify_key_hash_type_for_oid(&oid::RSA_WITH_SHA256_OID).unwrap(),
(KeyType::Rsa, HashType::Sha256)
);
assert_eq!(
identify_key_hash_type_for_oid(&oid::RSA_WITH_SHA384_OID).unwrap(),
(KeyType::Rsa, HashType::Sha384)
);
assert_eq!(
identify_key_hash_type_for_oid(&oid::RSA_WITH_SHA512_OID).unwrap(),
(KeyType::Rsa, HashType::Sha512)
);
assert_eq!(
identify_key_hash_type_for_oid(&oid::RSA_WITH_SHA3_224_OID).unwrap(),
(KeyType::Rsa, HashType::Sha3_224)
);
assert_eq!(
identify_key_hash_type_for_oid(&oid::RSA_WITH_SHA3_256_OID).unwrap(),
(KeyType::Rsa, HashType::Sha3_256)
);
assert_eq!(
identify_key_hash_type_for_oid(&oid::RSA_WITH_SHA3_384_OID).unwrap(),
(KeyType::Rsa, HashType::Sha3_384)
);
assert_eq!(
identify_key_hash_type_for_oid(&oid::RSA_WITH_SHA3_512_OID).unwrap(),
(KeyType::Rsa, HashType::Sha3_512)
);
assert_eq!(
identify_key_hash_type_for_oid(&oid::ECDSA_WITH_SHA224_OID).unwrap(),
(KeyType::Ec, HashType::Sha224)
);
assert_eq!(
identify_key_hash_type_for_oid(&oid::ECDSA_WITH_SHA256_OID).unwrap(),
(KeyType::Ec, HashType::Sha256)
);
assert_eq!(
identify_key_hash_type_for_oid(&oid::ECDSA_WITH_SHA384_OID).unwrap(),
(KeyType::Ec, HashType::Sha384)
);
assert_eq!(
identify_key_hash_type_for_oid(&oid::ECDSA_WITH_SHA512_OID).unwrap(),
(KeyType::Ec, HashType::Sha512)
);
assert_eq!(
identify_key_hash_type_for_oid(&oid::ECDSA_WITH_SHA3_224_OID).unwrap(),
(KeyType::Ec, HashType::Sha3_224)
);
assert_eq!(
identify_key_hash_type_for_oid(&oid::ECDSA_WITH_SHA3_256_OID).unwrap(),
(KeyType::Ec, HashType::Sha3_256)
);
assert_eq!(
identify_key_hash_type_for_oid(&oid::ECDSA_WITH_SHA3_384_OID).unwrap(),
(KeyType::Ec, HashType::Sha3_384)
);
assert_eq!(
identify_key_hash_type_for_oid(&oid::ECDSA_WITH_SHA3_512_OID).unwrap(),
(KeyType::Ec, HashType::Sha3_512)
);
assert_eq!(
identify_key_hash_type_for_oid(&oid::ED25519_OID).unwrap(),
(KeyType::Ed25519, HashType::None)
);
assert_eq!(
identify_key_hash_type_for_oid(&oid::ED448_OID).unwrap(),
(KeyType::Ed448, HashType::None)
);
assert_eq!(
identify_key_hash_type_for_oid(&oid::DSA_WITH_SHA224_OID).unwrap(),
(KeyType::Dsa, HashType::Sha224)
);
assert_eq!(
identify_key_hash_type_for_oid(&oid::DSA_WITH_SHA256_OID).unwrap(),
(KeyType::Dsa, HashType::Sha256)
);
assert_eq!(
identify_key_hash_type_for_oid(&oid::DSA_WITH_SHA384_OID).unwrap(),
(KeyType::Dsa, HashType::Sha384)
);
assert_eq!(
identify_key_hash_type_for_oid(&oid::DSA_WITH_SHA512_OID).unwrap(),
(KeyType::Dsa, HashType::Sha512)
);
assert!(identify_key_hash_type_for_oid(&oid::TLS_FEATURE_OID).is_err());
}
#[test]
fn test_py_hash_name_from_hash_type() {
for (hash, name) in [
(HashType::Sha224, "SHA224"),
(HashType::Sha256, "SHA256"),
(HashType::Sha384, "SHA384"),
(HashType::Sha512, "SHA512"),
(HashType::Sha3_224, "SHA3_224"),
(HashType::Sha3_256, "SHA3_256"),
(HashType::Sha3_384, "SHA3_384"),
(HashType::Sha3_512, "SHA3_512"),
] {
let hash_str = py_hash_name_from_hash_type(hash).unwrap();
assert_eq!(hash_str, name);
}
}
}

View file

@ -15,6 +15,7 @@ import pytest
import pytz
from cryptography import utils, x509
from cryptography.exceptions import InvalidSignature
from cryptography.hazmat.bindings._rust import asn1
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import (
@ -25,6 +26,7 @@ from cryptography.hazmat.primitives.asymmetric import (
ed25519,
padding,
rsa,
types,
x448,
x25519,
)
@ -41,9 +43,13 @@ from cryptography.x509.oid import (
SubjectInformationAccessOID,
)
from ..hazmat.primitives.fixtures_dsa import DSA_KEY_2048
from ..hazmat.primitives.fixtures_dsa import DSA_KEY_2048, DSA_KEY_3072
from ..hazmat.primitives.fixtures_ec import EC_KEY_SECP256R1
from ..hazmat.primitives.fixtures_rsa import RSA_KEY_512, RSA_KEY_2048
from ..hazmat.primitives.fixtures_rsa import (
RSA_KEY_512,
RSA_KEY_2048,
RSA_KEY_2048_ALT,
)
from ..hazmat.primitives.test_ec import _skip_curve_unsupported
from ..utils import (
load_nist_vectors,
@ -77,6 +83,57 @@ def _load_cert(filename, loader: typing.Callable[..., T], backend=None) -> T:
return cert
def _generate_ca_and_leaf(
issuer_private_key: types.CERTIFICATE_PRIVATE_KEY_TYPES,
subject_private_key: types.CERTIFICATE_PRIVATE_KEY_TYPES,
):
if isinstance(
issuer_private_key,
(ed25519.Ed25519PrivateKey, ed448.Ed448PrivateKey),
):
hash_alg = None
else:
hash_alg = hashes.SHA256()
builder = (
x509.CertificateBuilder()
.subject_name(
x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, "PyCA CA")])
)
.issuer_name(
x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, "PyCA CA")])
)
.public_key(issuer_private_key.public_key())
.serial_number(1)
.not_valid_before(datetime.datetime(2020, 1, 1))
.not_valid_after(datetime.datetime(2030, 1, 1))
)
ca = builder.sign(issuer_private_key, hash_alg)
builder = (
x509.CertificateBuilder()
.subject_name(
x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, "leaf")])
)
.issuer_name(
x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, "PyCA CA")])
)
.public_key(subject_private_key.public_key())
.serial_number(100)
.not_valid_before(datetime.datetime(2020, 1, 1))
.not_valid_after(datetime.datetime(2025, 1, 1))
)
cert = builder.sign(issuer_private_key, hash_alg)
return ca, cert
def _break_cert_sig(cert: x509.Certificate) -> x509.Certificate:
cert_bad_sig = bytearray(cert.public_bytes(serialization.Encoding.PEM))
# Break the sig by mutating 5 bytes. This has a 2**-40 chance of
# not breaking the sig. Spin that roulette wheel.
cert_bad_sig[-40:-35] = 90, 90, 90, 90, 90
return x509.load_pem_x509_certificate(bytes(cert_bad_sig))
class TestCertificateRevocationList:
def test_load_pem_crl(self, backend):
crl = _load_cert(
@ -1473,6 +1530,108 @@ class TestRSACertificate:
[x509.TLSFeatureType.status_request]
)
def test_verify_directly_issued_by_rsa(self):
issuer_private_key = RSA_KEY_2048.private_key()
subject_private_key = RSA_KEY_2048_ALT.private_key()
ca, cert = _generate_ca_and_leaf(
issuer_private_key, subject_private_key
)
cert.verify_directly_issued_by(ca)
def test_verify_directly_issued_by_rsa_bad_sig(self):
issuer_private_key = RSA_KEY_2048.private_key()
subject_private_key = RSA_KEY_2048_ALT.private_key()
ca, cert = _generate_ca_and_leaf(
issuer_private_key, subject_private_key
)
cert_bad_sig = _break_cert_sig(cert)
with pytest.raises(InvalidSignature):
cert_bad_sig.verify_directly_issued_by(ca)
def test_verify_directly_issued_by_rsa_mismatched_inner_out_oid(self):
cert = _load_cert(
os.path.join(
"x509", "custom", "mismatch_inner_outer_sig_algorithm.der"
),
x509.load_der_x509_certificate,
)
with pytest.raises(ValueError) as exc:
cert.verify_directly_issued_by(cert)
assert str(exc.value) == (
"Inner and outer signature algorithms do not match. This is an "
"invalid certificate."
)
def test_verify_directly_issued_by_subject_issuer_mismatch(self):
cert = _load_cert(
os.path.join("x509", "cryptography.io.pem"),
x509.load_pem_x509_certificate,
)
with pytest.raises(ValueError) as exc:
cert.verify_directly_issued_by(cert)
assert str(exc.value) == (
"Issuer certificate subject does not match certificate issuer."
)
def test_verify_directly_issued_by_algorithm_mismatch(self):
issuer_private_key = RSA_KEY_2048.private_key()
subject_private_key = RSA_KEY_2048_ALT.private_key()
_, cert = _generate_ca_and_leaf(
issuer_private_key, subject_private_key
)
# We need a CA with the same issuer DN but diff signature algorithm
secondary_issuer_key = ec.generate_private_key(ec.SECP256R1())
ca2, _ = _generate_ca_and_leaf(
secondary_issuer_key, subject_private_key
)
with pytest.raises(ValueError):
cert.verify_directly_issued_by(ca2)
@pytest.mark.supported(
only_if=lambda backend: (
backend.ed25519_supported() and backend.x25519_supported()
),
skip_message="Requires OpenSSL with Ed25519 and X25519 support",
)
def test_verify_directly_issued_by_unsupported_key_type(self, backend):
private_key = ed25519.Ed25519PrivateKey.generate()
x25519_public = x25519.X25519PrivateKey.generate().public_key()
# Generate an ed25519 CA
builder = (
x509.CertificateBuilder()
.subject_name(
x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, "PyCA CA")])
)
.issuer_name(
x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, "PyCA CA")])
)
.public_key(private_key.public_key())
.serial_number(1)
.not_valid_before(datetime.datetime(2020, 1, 1))
.not_valid_after(datetime.datetime(2030, 1, 1))
)
cert = builder.sign(private_key, None)
# Make a cert with the right issuer name but the wrong public key
builder = (
x509.CertificateBuilder()
.subject_name(
x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, "PyCA CA")])
)
.issuer_name(
x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, "PyCA CA")])
)
.public_key(x25519_public)
.serial_number(1)
.not_valid_before(datetime.datetime(2020, 1, 1))
.not_valid_after(datetime.datetime(2030, 1, 1))
)
leaf = builder.sign(private_key, None)
with pytest.raises(TypeError):
cert.verify_directly_issued_by(leaf)
class TestRSACertificateRequest:
@pytest.mark.parametrize(
@ -4562,6 +4721,24 @@ class TestDSACertificate:
cert.signature_hash_algorithm,
)
def test_verify_directly_issued_by_dsa(self):
issuer_private_key = DSA_KEY_3072.private_key()
subject_private_key = DSA_KEY_2048.private_key()
ca, cert = _generate_ca_and_leaf(
issuer_private_key, subject_private_key
)
cert.verify_directly_issued_by(ca)
def test_verify_directly_issued_by_dsa_bad_sig(self):
issuer_private_key = DSA_KEY_3072.private_key()
subject_private_key = DSA_KEY_2048.private_key()
ca, cert = _generate_ca_and_leaf(
issuer_private_key, subject_private_key
)
cert_bad_sig = _break_cert_sig(cert)
with pytest.raises(InvalidSignature):
cert_bad_sig.verify_directly_issued_by(ca)
@pytest.mark.supported(
only_if=lambda backend: backend.dsa_supported(),
@ -4788,6 +4965,24 @@ class TestECDSACertificate:
with pytest.raises(ValueError, match="explicit parameters"):
cert.public_key()
def test_verify_directly_issued_by_ec(self):
issuer_private_key = ec.generate_private_key(ec.SECP256R1())
subject_private_key = ec.generate_private_key(ec.SECP256R1())
ca, cert = _generate_ca_and_leaf(
issuer_private_key, subject_private_key
)
cert.verify_directly_issued_by(ca)
def test_verify_directly_issued_by_ec_bad_sig(self):
issuer_private_key = ec.generate_private_key(ec.SECP256R1())
subject_private_key = ec.generate_private_key(ec.SECP256R1())
ca, cert = _generate_ca_and_leaf(
issuer_private_key, subject_private_key
)
cert_bad_sig = _break_cert_sig(cert)
with pytest.raises(InvalidSignature):
cert_bad_sig.verify_directly_issued_by(ca)
class TestECDSACertificateRequest:
@pytest.mark.parametrize(
@ -5411,6 +5606,24 @@ class TestEd25519Certificate:
)
assert copy.deepcopy(cert) is cert
def test_verify_directly_issued_by_ed25519(self, backend):
issuer_private_key = ed25519.Ed25519PrivateKey.generate()
subject_private_key = ed25519.Ed25519PrivateKey.generate()
ca, cert = _generate_ca_and_leaf(
issuer_private_key, subject_private_key
)
cert.verify_directly_issued_by(ca)
def test_verify_directly_issued_by_ed25519_bad_sig(self, backend):
issuer_private_key = ed25519.Ed25519PrivateKey.generate()
subject_private_key = ed25519.Ed25519PrivateKey.generate()
ca, cert = _generate_ca_and_leaf(
issuer_private_key, subject_private_key
)
cert_bad_sig = _break_cert_sig(cert)
with pytest.raises(InvalidSignature):
cert_bad_sig.verify_directly_issued_by(ca)
@pytest.mark.supported(
only_if=lambda backend: backend.ed448_supported(),
@ -5432,6 +5645,24 @@ class TestEd448Certificate:
assert cert.signature_hash_algorithm is None
assert cert.signature_algorithm_oid == SignatureAlgorithmOID.ED448
def test_verify_directly_issued_by_ed448(self, backend):
issuer_private_key = ed448.Ed448PrivateKey.generate()
subject_private_key = ed448.Ed448PrivateKey.generate()
ca, cert = _generate_ca_and_leaf(
issuer_private_key, subject_private_key
)
cert.verify_directly_issued_by(ca)
def test_verify_directly_issued_by_ed448_bad_sig(self, backend):
issuer_private_key = ed448.Ed448PrivateKey.generate()
subject_private_key = ed448.Ed448PrivateKey.generate()
ca, cert = _generate_ca_and_leaf(
issuer_private_key, subject_private_key
)
cert_bad_sig = _break_cert_sig(cert)
with pytest.raises(InvalidSignature):
cert_bad_sig.verify_directly_issued_by(ca)
@pytest.mark.supported(
only_if=lambda backend: backend.dh_supported(),