Skip to content

Commit 9d46788

Browse files
committed
added tests for valid & invalid certificates
added SAN checking (incomplete though)
1 parent ec18bc0 commit 9d46788

File tree

3 files changed

+145
-23
lines changed

3 files changed

+145
-23
lines changed

src/cryptography/hazmat/primitives/serialization/pkcs7.py

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -89,16 +89,38 @@ def _validate_key_usage(
8989
raise ValueError(
9090
"Key Usage, if specified, must have at least one of the "
9191
"digital signature or content commitment (formerly non "
92-
"repudiation) bits set"
92+
"repudiation) bits set."
9393
)
9494

9595
def _validate_subject_alternative_name(
9696
policy: Policy,
9797
cert: Certificate,
98-
san: x509.SubjectAlternativeName | None,
98+
san: x509.SubjectAlternativeName,
9999
) -> None:
100-
if san is not None:
101-
pass
100+
"""
101+
For each general name in the SAN, for those which are email addresses:
102+
- If it is an RFC822Name, general part must be ascii.
103+
- If it is an OtherName, general part must be non-ascii.
104+
"""
105+
for general_name in san:
106+
if (
107+
isinstance(general_name, x509.RFC822Name)
108+
and "@" in general_name.value
109+
and not general_name.value.split("@")[0].isascii()
110+
):
111+
raise ValueError(
112+
f"RFC822Name {general_name.value} contains non-ASCII "
113+
"characters."
114+
)
115+
if (
116+
isinstance(general_name, x509.OtherName)
117+
and "@" in general_name.value.decode()
118+
and general_name.value.decode().split("@")[0].isascii()
119+
):
120+
raise ValueError(
121+
f"OtherName {general_name.value.decode()} is ASCII, "
122+
"so must be stored in RFC822Name."
123+
)
102124

103125
def _validate_extended_key_usage(
104126
policy: Policy, cert: Certificate, eku: x509.ExtendedKeyUsage | None
@@ -109,11 +131,11 @@ def _validate_extended_key_usage(
109131
if not (ep or aeku):
110132
raise ValueError(
111133
"Extended Key Usage, if specified, must include "
112-
"emailProtection or anyExtendedKeyUsage"
134+
"emailProtection or anyExtendedKeyUsage."
113135
)
114136

115137
ee_policy = (
116-
ExtensionPolicy.permit_all()
138+
ExtensionPolicy.webpki_defaults_ee()
117139
.may_be_present(
118140
x509.BasicConstraints,
119141
Criticality.AGNOSTIC,
@@ -124,7 +146,7 @@ def _validate_extended_key_usage(
124146
Criticality.CRITICAL,
125147
_validate_key_usage,
126148
)
127-
.may_be_present(
149+
.require_present(
128150
x509.SubjectAlternativeName,
129151
Criticality.AGNOSTIC,
130152
_validate_subject_alternative_name,

tests/hazmat/primitives/test_pkcs7.py

Lines changed: 110 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,11 @@
1818
from cryptography.hazmat.primitives.asymmetric import ed25519, padding, rsa
1919
from cryptography.hazmat.primitives.ciphers import algorithms
2020
from cryptography.hazmat.primitives.serialization import pkcs7
21-
from cryptography.x509.verification import PolicyBuilder, Store
21+
from cryptography.x509.verification import (
22+
PolicyBuilder,
23+
Store,
24+
VerificationError,
25+
)
2226
from tests.x509.test_x509 import _generate_ca_and_leaf
2327

2428
from ...hazmat.primitives.fixtures_rsa import (
@@ -140,18 +144,113 @@ def _load_cert_key():
140144
return cert, key
141145

142146

143-
def test_verify_pkcs7_certificate():
144-
certificate, _ = _load_cert_key()
145-
ca_policy, ee_policy = pkcs7.pkcs7_x509_extension_policies()
147+
class TestPKCS7VerifyCertificate:
148+
def test_verify_pkcs7_certificate(self):
149+
certificate, _ = _load_cert_key()
150+
ca_policy, ee_policy = pkcs7.pkcs7_x509_extension_policies()
146151

147-
builder = (
148-
PolicyBuilder()
149-
.store(Store([certificate]))
150-
.extension_policies(ca_policy, ee_policy)
151-
)
152+
verifier = (
153+
PolicyBuilder()
154+
.store(Store([certificate]))
155+
.extension_policies(ca_policy, ee_policy)
156+
.build_client_verifier()
157+
)
158+
verifier.verify(certificate, [])
159+
160+
@pytest.fixture(name="certificate_builder")
161+
def fixture_certificate_builder(self) -> x509.CertificateBuilder:
162+
certificate, private_key = _load_cert_key()
163+
return (
164+
x509.CertificateBuilder()
165+
.serial_number(certificate.serial_number)
166+
.subject_name(certificate.subject)
167+
.issuer_name(certificate.issuer)
168+
.public_key(private_key.public_key())
169+
.not_valid_before(certificate.not_valid_before)
170+
.not_valid_after(certificate.not_valid_after)
171+
)
172+
173+
def test_verify_pkcs7_certificate_wrong_bc(self, certificate_builder):
174+
certificate, private_key = _load_cert_key()
175+
176+
# Add an invalid extension
177+
extension = x509.BasicConstraints(ca=True, path_length=None)
178+
certificate_builder = certificate_builder.add_extension(
179+
extension, True
180+
)
181+
182+
# Build the certificate
183+
pkcs7_certificate = certificate_builder.sign(
184+
private_key, certificate.signature_hash_algorithm, None
185+
)
186+
187+
# Verify the certificate
188+
self.verify_invalid_pkcs7_certificate(pkcs7_certificate)
189+
190+
def test_verify_pkcs7_certificate_wrong_ku(self, certificate_builder):
191+
certificate, private_key = _load_cert_key()
192+
193+
# Add an invalid extension
194+
extension = x509.KeyUsage(
195+
digital_signature=False,
196+
content_commitment=False,
197+
key_encipherment=True,
198+
data_encipherment=True,
199+
key_agreement=True,
200+
key_cert_sign=True,
201+
crl_sign=True,
202+
encipher_only=False,
203+
decipher_only=False,
204+
)
205+
certificate_builder = certificate_builder.add_extension(
206+
extension, True
207+
)
208+
209+
# Build the certificate
210+
pkcs7_certificate = certificate_builder.sign(
211+
private_key, certificate.signature_hash_algorithm, None
212+
)
213+
214+
# Verify the certificate
215+
self.verify_invalid_pkcs7_certificate(pkcs7_certificate)
216+
217+
def test_verify_pkcs7_certificate_wrong_eku(self, certificate_builder):
218+
certificate, private_key = _load_cert_key()
219+
220+
# Add an invalid extension
221+
usages = [x509.ExtendedKeyUsageOID.CLIENT_AUTH] # type: ignore[attr-defined]
222+
extension = x509.ExtendedKeyUsage(usages)
223+
certificate_builder = certificate_builder.add_extension(
224+
extension, True
225+
)
226+
227+
# Add an invalid extension
228+
usages = [x509.ExtendedKeyUsageOID.CLIENT_AUTH] # type: ignore[attr-defined]
229+
extension = x509.ExtendedKeyUsage(usages)
230+
certificate_builder = certificate_builder.add_extension(
231+
extension, True
232+
)
233+
234+
# Build the certificate
235+
pkcs7_certificate = certificate_builder.sign(
236+
private_key, certificate.signature_hash_algorithm, None
237+
)
238+
239+
# Verify the certificate
240+
self.verify_invalid_pkcs7_certificate(pkcs7_certificate)
241+
242+
@staticmethod
243+
def verify_invalid_pkcs7_certificate(certificate: x509.Certificate):
244+
ca_policy, ee_policy = pkcs7.pkcs7_x509_extension_policies()
245+
verifier = (
246+
PolicyBuilder()
247+
.store(Store([certificate]))
248+
.extension_policies(ca_policy, ee_policy)
249+
.build_client_verifier()
250+
)
152251

153-
verifier = builder.build_client_verifier()
154-
verifier.verify(certificate, [])
252+
with pytest.raises(VerificationError):
253+
verifier.verify(certificate, [])
155254

156255

157256
@pytest.mark.supported(
Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
-----BEGIN CERTIFICATE-----
2-
MIIBZzCCAQ6gAwIBAgICAwkwCgYIKoZIzj0EAwIwJzELMAkGA1UEBhMCVVMxGDAW
2+
MIIBhjCCASygAwIBAgICAwkwCgYIKoZIzj0EAwIwJzELMAkGA1UEBhMCVVMxGDAW
33
BgNVBAMMD2NyeXB0b2dyYXBoeSBDQTAgFw0xNzAxMDEwMTAwMDBaGA8yMTAwMDEw
44
MTAwMDAwMFowJzELMAkGA1UEBhMCVVMxGDAWBgNVBAMMD2NyeXB0b2dyYXBoeSBD
55
QTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABBj/z7v5Obj13cPuwECLBnUGq0/N
6-
2CxSJE4f4BBGZ7VfFblivTvPDG++Gve0oQ+0uctuhrNQ+WxRv8GC177F+QWjKDAm
7-
MAwGA1UdEwEB/wQCMAAwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwQwCgYIKoZIzj0E
8-
AwIDRwAwRAIgeUqOfJV3+l/GAUYwEnrfpks43W81QpUO9bcKml3N2bECIDZ1vM5p
9-
Wm+IO/fjVtpIPa3UwcfSguUb3Dut0PpxKPOF
6+
2CxSJE4f4BBGZ7VfFblivTvPDG++Gve0oQ+0uctuhrNQ+WxRv8GC177F+QWjRjBE
7+
MCEGA1UdEQEB/wQXMBWBE2V4YW1wbGVAZXhhbXBsZS5jb20wHwYDVR0jBBgwFoAU
8+
/Ou02BLyyT2Zwzxn9H03feYT7fowCgYIKoZIzj0EAwIDSAAwRQIgUwIdC0Emkd6f
9+
17DeOXTlmTAhwSDJ2FTuyHESwei7wJcCIQCnr9NpBxbtJfEzxHGGyd7PxgpOLi5u
10+
rk+8QfzGMmg/fw==
1011
-----END CERTIFICATE-----

0 commit comments

Comments
 (0)