0
# X.509 Certificates
1
2
Comprehensive X.509 certificate handling including certificate creation, parsing, validation, and extension management. Supports certificate signing requests (CSRs), certificate revocation lists (CRLs), and certificate verification workflows.
3
4
## Core Imports
5
6
```python
7
from cryptography import x509
8
from cryptography.x509.oid import NameOID, ExtensionOID, SignatureAlgorithmOID
9
from cryptography.hazmat.primitives import hashes
10
from cryptography.hazmat.primitives.asymmetric import rsa
11
```
12
13
## Capabilities
14
15
### Certificate Loading and Parsing
16
17
Load and parse X.509 certificates from various formats.
18
19
```python { .api }
20
def load_pem_x509_certificate(data: bytes) -> Certificate:
21
"""
22
Load X.509 certificate from PEM format.
23
24
Args:
25
data (bytes): PEM-encoded certificate data
26
27
Returns:
28
Certificate: Parsed certificate object
29
"""
30
31
def load_der_x509_certificate(data: bytes) -> Certificate:
32
"""
33
Load X.509 certificate from DER format.
34
35
Args:
36
data (bytes): DER-encoded certificate data
37
38
Returns:
39
Certificate: Parsed certificate object
40
"""
41
42
def load_pem_x509_certificates(data: bytes) -> List[Certificate]:
43
"""
44
Load multiple X.509 certificates from PEM format.
45
46
Args:
47
data (bytes): PEM data containing multiple certificates
48
49
Returns:
50
List[Certificate]: List of parsed certificate objects
51
"""
52
```
53
54
### Certificate Objects
55
56
```python { .api }
57
class Certificate:
58
def public_key(self):
59
"""
60
Get the certificate's public key.
61
62
Returns:
63
PublicKey: The public key (RSA, DSA, EC, Ed25519, Ed448, etc.)
64
"""
65
66
@property
67
def subject(self) -> Name:
68
"""Certificate subject name"""
69
70
@property
71
def issuer(self) -> Name:
72
"""Certificate issuer name"""
73
74
@property
75
def serial_number(self) -> int:
76
"""Certificate serial number"""
77
78
@property
79
def version(self) -> Version:
80
"""Certificate version (typically Version.v3)"""
81
82
@property
83
def not_valid_before(self) -> datetime:
84
"""Certificate validity start time"""
85
86
@property
87
def not_valid_after(self) -> datetime:
88
"""Certificate validity end time"""
89
90
@property
91
def signature_algorithm_oid(self) -> ObjectIdentifier:
92
"""OID of signature algorithm used"""
93
94
@property
95
def signature_hash_algorithm(self):
96
"""Hash algorithm used in signature"""
97
98
@property
99
def extensions(self) -> Extensions:
100
"""Certificate extensions"""
101
102
def fingerprint(self, algorithm) -> bytes:
103
"""
104
Calculate certificate fingerprint.
105
106
Args:
107
algorithm: Hash algorithm (e.g., hashes.SHA256())
108
109
Returns:
110
bytes: Certificate fingerprint
111
"""
112
113
def public_bytes(self, encoding: Encoding) -> bytes:
114
"""
115
Serialize certificate to bytes.
116
117
Args:
118
encoding: Encoding format (PEM or DER)
119
120
Returns:
121
bytes: Serialized certificate
122
"""
123
```
124
125
### Certificate Creation
126
127
```python { .api }
128
class CertificateBuilder:
129
def subject_name(self, name: Name) -> 'CertificateBuilder':
130
"""Set certificate subject name"""
131
132
def issuer_name(self, name: Name) -> 'CertificateBuilder':
133
"""Set certificate issuer name"""
134
135
def public_key(self, key) -> 'CertificateBuilder':
136
"""Set certificate public key"""
137
138
def serial_number(self, serial: int) -> 'CertificateBuilder':
139
"""Set certificate serial number"""
140
141
def not_valid_before(self, time: datetime) -> 'CertificateBuilder':
142
"""Set certificate validity start time"""
143
144
def not_valid_after(self, time: datetime) -> 'CertificateBuilder':
145
"""Set certificate validity end time"""
146
147
def add_extension(self, extension, critical: bool) -> 'CertificateBuilder':
148
"""Add extension to certificate"""
149
150
def sign(self, private_key, algorithm) -> Certificate:
151
"""
152
Sign and build the certificate.
153
154
Args:
155
private_key: Private key for signing
156
algorithm: Hash algorithm (e.g., hashes.SHA256())
157
158
Returns:
159
Certificate: Signed certificate
160
"""
161
162
def random_serial_number() -> int:
163
"""Generate cryptographically secure random serial number"""
164
```
165
166
### Distinguished Names
167
168
```python { .api }
169
class Name:
170
def __init__(self, attributes: List[NameAttribute]):
171
"""
172
Create distinguished name from attributes.
173
174
Args:
175
attributes: List of name attributes
176
"""
177
178
def rfc4514_string(self) -> str:
179
"""RFC 4514 string representation"""
180
181
def get_attributes_for_oid(self, oid: ObjectIdentifier) -> List[NameAttribute]:
182
"""Get all attributes matching an OID"""
183
184
class NameAttribute:
185
def __init__(self, oid: ObjectIdentifier, value: str):
186
"""
187
Create name attribute.
188
189
Args:
190
oid: Attribute OID (e.g., NameOID.COMMON_NAME)
191
value: Attribute value
192
"""
193
194
@property
195
def oid(self) -> ObjectIdentifier:
196
"""Attribute OID"""
197
198
@property
199
def value(self) -> str:
200
"""Attribute value"""
201
202
class RelativeDistinguishedName:
203
def __init__(self, attributes: List[NameAttribute]):
204
"""Create RDN from attributes"""
205
206
def get_attributes_for_oid(self, oid: ObjectIdentifier) -> List[NameAttribute]:
207
"""Get attributes for specific OID"""
208
```
209
210
### Certificate Extensions
211
212
```python { .api }
213
class Extensions:
214
def get_extension_for_oid(self, oid: ObjectIdentifier) -> Extension:
215
"""Get extension by OID"""
216
217
def __iter__(self):
218
"""Iterate over all extensions"""
219
220
class Extension:
221
@property
222
def oid(self) -> ObjectIdentifier:
223
"""Extension OID"""
224
225
@property
226
def critical(self) -> bool:
227
"""Whether extension is critical"""
228
229
@property
230
def value(self) -> ExtensionType:
231
"""Extension value object"""
232
233
class BasicConstraints:
234
def __init__(self, ca: bool, path_length: int = None):
235
"""
236
Basic constraints extension.
237
238
Args:
239
ca: Whether this is a CA certificate
240
path_length: Maximum path length for intermediate CAs
241
"""
242
243
@property
244
def ca(self) -> bool:
245
"""Is this a CA certificate"""
246
247
@property
248
def path_length(self) -> int:
249
"""Path length constraint"""
250
251
class KeyUsage:
252
def __init__(self, digital_signature: bool = False, key_agreement: bool = False,
253
key_cert_sign: bool = False, crl_sign: bool = False,
254
content_commitment: bool = False, data_encipherment: bool = False,
255
key_encipherment: bool = False, encipher_only: bool = False,
256
decipher_only: bool = False):
257
"""Key usage extension indicating allowed key operations"""
258
259
class SubjectAlternativeName:
260
def __init__(self, general_names: List[GeneralName]):
261
"""Subject alternative name extension"""
262
263
def get_values_for_type(self, type_: type) -> List:
264
"""Get values for specific general name type"""
265
266
class AuthorityKeyIdentifier:
267
def __init__(self, key_identifier: bytes = None,
268
authority_cert_issuer: List[GeneralName] = None,
269
authority_cert_serial_number: int = None):
270
"""Authority key identifier extension"""
271
272
class SubjectKeyIdentifier:
273
def __init__(self, digest: bytes):
274
"""Subject key identifier extension"""
275
276
@property
277
def digest(self) -> bytes:
278
"""Key identifier digest"""
279
```
280
281
### General Names
282
283
```python { .api }
284
class GeneralName:
285
"""Abstract base for general name types"""
286
287
class DNSName:
288
def __init__(self, value: str):
289
"""DNS name general name"""
290
291
@property
292
def value(self) -> str:
293
"""DNS name value"""
294
295
class IPAddress:
296
def __init__(self, value):
297
"""IP address general name (IPv4/IPv6 address or network)"""
298
299
@property
300
def value(self):
301
"""IP address value"""
302
303
class RFC822Name:
304
def __init__(self, value: str):
305
"""Email address general name"""
306
307
@property
308
def value(self) -> str:
309
"""Email address"""
310
311
class UniformResourceIdentifier:
312
def __init__(self, value: str):
313
"""URI general name"""
314
315
@property
316
def value(self) -> str:
317
"""URI value"""
318
319
class DirectoryName:
320
def __init__(self, value: Name):
321
"""Directory name (distinguished name) general name"""
322
323
@property
324
def value(self) -> Name:
325
"""Distinguished name"""
326
```
327
328
### Certificate Signing Requests (CSRs)
329
330
```python { .api }
331
def load_pem_x509_csr(data: bytes) -> CertificateSigningRequest:
332
"""Load CSR from PEM format"""
333
334
def load_der_x509_csr(data: bytes) -> CertificateSigningRequest:
335
"""Load CSR from DER format"""
336
337
class CertificateSigningRequest:
338
@property
339
def subject(self) -> Name:
340
"""CSR subject name"""
341
342
def public_key(self):
343
"""CSR public key"""
344
345
@property
346
def signature_algorithm_oid(self) -> ObjectIdentifier:
347
"""Signature algorithm OID"""
348
349
@property
350
def extensions(self) -> Extensions:
351
"""CSR extension requests"""
352
353
def is_signature_valid(self) -> bool:
354
"""Verify CSR signature"""
355
356
class CertificateSigningRequestBuilder:
357
def subject_name(self, name: Name) -> 'CertificateSigningRequestBuilder':
358
"""Set CSR subject"""
359
360
def add_extension(self, extension, critical: bool) -> 'CertificateSigningRequestBuilder':
361
"""Add extension request"""
362
363
def sign(self, private_key, algorithm) -> CertificateSigningRequest:
364
"""Sign and build CSR"""
365
```
366
367
### Certificate Revocation Lists (CRLs)
368
369
```python { .api }
370
def load_pem_x509_crl(data: bytes) -> CertificateRevocationList:
371
"""Load CRL from PEM format"""
372
373
def load_der_x509_crl(data: bytes) -> CertificateRevocationList:
374
"""Load CRL from DER format"""
375
376
class CertificateRevocationList:
377
@property
378
def issuer(self) -> Name:
379
"""CRL issuer"""
380
381
@property
382
def last_update(self) -> datetime:
383
"""CRL last update time"""
384
385
@property
386
def next_update(self) -> datetime:
387
"""CRL next update time"""
388
389
def get_revoked_certificate_by_serial_number(self, serial: int) -> RevokedCertificate:
390
"""Get revoked certificate by serial number"""
391
392
def __iter__(self):
393
"""Iterate over revoked certificates"""
394
395
class RevokedCertificate:
396
@property
397
def serial_number(self) -> int:
398
"""Serial number of revoked certificate"""
399
400
@property
401
def revocation_date(self) -> datetime:
402
"""Date certificate was revoked"""
403
404
@property
405
def extensions(self) -> Extensions:
406
"""Revocation entry extensions"""
407
```
408
409
## Usage Examples
410
411
### Loading and Inspecting Certificates
412
413
```python
414
from cryptography import x509
415
from cryptography.x509.oid import NameOID
416
417
# Load certificate from PEM file
418
with open('certificate.pem', 'rb') as f:
419
cert_data = f.read()
420
421
cert = x509.load_pem_x509_certificate(cert_data)
422
423
# Inspect certificate details
424
print(f"Subject: {cert.subject.rfc4514_string()}")
425
print(f"Issuer: {cert.issuer.rfc4514_string()}")
426
print(f"Serial: {cert.serial_number}")
427
print(f"Valid from: {cert.not_valid_before}")
428
print(f"Valid to: {cert.not_valid_after}")
429
430
# Extract common name
431
cn_attributes = cert.subject.get_attributes_for_oid(NameOID.COMMON_NAME)
432
if cn_attributes:
433
print(f"Common Name: {cn_attributes[0].value}")
434
435
# Check extensions
436
try:
437
san_ext = cert.extensions.get_extension_for_oid(ExtensionOID.SUBJECT_ALTERNATIVE_NAME)
438
san = san_ext.value
439
dns_names = san.get_values_for_type(x509.DNSName)
440
print(f"DNS SANs: {[name.value for name in dns_names]}")
441
except x509.ExtensionNotFound:
442
print("No SAN extension found")
443
```
444
445
### Creating a Self-Signed Certificate
446
447
```python
448
from cryptography import x509
449
from cryptography.x509.oid import NameOID
450
from cryptography.hazmat.primitives import hashes
451
from cryptography.hazmat.primitives.asymmetric import rsa
452
from cryptography.hazmat.primitives.serialization import Encoding, PrivateFormat, NoEncryption
453
import datetime
454
455
# Generate private key
456
private_key = rsa.generate_private_key(
457
public_exponent=65537,
458
key_size=2048
459
)
460
461
# Create certificate
462
subject = issuer = x509.Name([
463
x509.NameAttribute(NameOID.COUNTRY_NAME, "US"),
464
x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, "California"),
465
x509.NameAttribute(NameOID.LOCALITY_NAME, "San Francisco"),
466
x509.NameAttribute(NameOID.ORGANIZATION_NAME, "My Company"),
467
x509.NameAttribute(NameOID.COMMON_NAME, "mysite.com"),
468
])
469
470
cert = x509.CertificateBuilder().subject_name(
471
subject
472
).issuer_name(
473
issuer
474
).public_key(
475
private_key.public_key()
476
).serial_number(
477
x509.random_serial_number()
478
).not_valid_before(
479
datetime.datetime.utcnow()
480
).not_valid_after(
481
datetime.datetime.utcnow() + datetime.timedelta(days=365)
482
).add_extension(
483
x509.SubjectAlternativeName([
484
x509.DNSName("mysite.com"),
485
x509.DNSName("www.mysite.com"),
486
]),
487
critical=False,
488
).add_extension(
489
x509.BasicConstraints(ca=False, path_length=None),
490
critical=True,
491
).sign(private_key, hashes.SHA256())
492
493
# Save certificate and key
494
cert_pem = cert.public_bytes(Encoding.PEM)
495
key_pem = private_key.private_bytes(
496
encoding=Encoding.PEM,
497
format=PrivateFormat.PKCS8,
498
encryption_algorithm=NoEncryption()
499
)
500
501
with open('certificate.pem', 'wb') as f:
502
f.write(cert_pem)
503
504
with open('private_key.pem', 'wb') as f:
505
f.write(key_pem)
506
```
507
508
### Creating a Certificate Signing Request
509
510
```python
511
from cryptography import x509
512
from cryptography.x509.oid import NameOID
513
from cryptography.hazmat.primitives import hashes
514
from cryptography.hazmat.primitives.asymmetric import rsa
515
516
# Generate private key
517
private_key = rsa.generate_private_key(
518
public_exponent=65537,
519
key_size=2048
520
)
521
522
# Create CSR
523
subject = x509.Name([
524
x509.NameAttribute(NameOID.COUNTRY_NAME, "US"),
525
x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, "California"),
526
x509.NameAttribute(NameOID.LOCALITY_NAME, "San Francisco"),
527
x509.NameAttribute(NameOID.ORGANIZATION_NAME, "My Company"),
528
x509.NameAttribute(NameOID.COMMON_NAME, "example.com"),
529
])
530
531
csr = x509.CertificateSigningRequestBuilder().subject_name(
532
subject
533
).add_extension(
534
x509.SubjectAlternativeName([
535
x509.DNSName("example.com"),
536
x509.DNSName("www.example.com"),
537
]),
538
critical=False,
539
).sign(private_key, hashes.SHA256())
540
541
# Save CSR
542
csr_pem = csr.public_bytes(Encoding.PEM)
543
with open('csr.pem', 'wb') as f:
544
f.write(csr_pem)
545
546
# Verify CSR signature
547
print(f"CSR signature valid: {csr.is_signature_valid()}")
548
```
549
550
### Certificate Chain Validation
551
552
```python
553
from cryptography import x509
554
from cryptography.hazmat.primitives import hashes
555
from cryptography.hazmat.primitives.asymmetric import padding
556
557
def verify_certificate_chain(cert_chain):
558
"""Verify a certificate chain"""
559
for i in range(len(cert_chain) - 1):
560
cert = cert_chain[i]
561
issuer_cert = cert_chain[i + 1]
562
563
# Verify issuer name matches subject
564
if cert.issuer != issuer_cert.subject:
565
return False, f"Issuer name mismatch at position {i}"
566
567
# Verify signature
568
try:
569
issuer_public_key = issuer_cert.public_key()
570
issuer_public_key.verify(
571
cert.signature,
572
cert.tbs_certificate_bytes,
573
padding.PKCS1v15(),
574
cert.signature_hash_algorithm
575
)
576
except Exception as e:
577
return False, f"Signature verification failed at position {i}: {e}"
578
579
return True, "Chain verification successful"
580
581
# Usage
582
with open('cert_chain.pem', 'rb') as f:
583
chain_data = f.read()
584
585
cert_chain = x509.load_pem_x509_certificates(chain_data)
586
valid, message = verify_certificate_chain(cert_chain)
587
print(f"Chain valid: {valid}, Message: {message}")
588
```
589
590
## OID Constants
591
592
Common object identifiers for names and extensions:
593
594
```python { .api }
595
class NameOID:
596
COMMON_NAME: ObjectIdentifier
597
COUNTRY_NAME: ObjectIdentifier
598
LOCALITY_NAME: ObjectIdentifier
599
STATE_OR_PROVINCE_NAME: ObjectIdentifier
600
ORGANIZATION_NAME: ObjectIdentifier
601
ORGANIZATIONAL_UNIT_NAME: ObjectIdentifier
602
EMAIL_ADDRESS: ObjectIdentifier
603
604
class ExtensionOID:
605
BASIC_CONSTRAINTS: ObjectIdentifier
606
KEY_USAGE: ObjectIdentifier
607
EXTENDED_KEY_USAGE: ObjectIdentifier
608
SUBJECT_ALTERNATIVE_NAME: ObjectIdentifier
609
ISSUER_ALTERNATIVE_NAME: ObjectIdentifier
610
SUBJECT_KEY_IDENTIFIER: ObjectIdentifier
611
AUTHORITY_KEY_IDENTIFIER: ObjectIdentifier
612
CRL_DISTRIBUTION_POINTS: ObjectIdentifier
613
AUTHORITY_INFORMATION_ACCESS: ObjectIdentifier
614
CERTIFICATE_POLICIES: ObjectIdentifier
615
616
class SignatureAlgorithmOID:
617
RSA_WITH_SHA256: ObjectIdentifier
618
RSA_WITH_SHA384: ObjectIdentifier
619
RSA_WITH_SHA512: ObjectIdentifier
620
ECDSA_WITH_SHA256: ObjectIdentifier
621
ECDSA_WITH_SHA384: ObjectIdentifier
622
ECDSA_WITH_SHA512: ObjectIdentifier
623
```
624
625
## Exception Handling
626
627
```python { .api }
628
class InvalidVersion(ValueError):
629
"""Invalid certificate version"""
630
631
class ExtensionNotFound(ValueError):
632
"""Certificate extension not found"""
633
634
class DuplicateExtension(ValueError):
635
"""Duplicate extension in certificate"""
636
637
class UnsupportedGeneralNameType(ValueError):
638
"""Unsupported general name type"""
639
```