0
# Asymmetric Cryptography
1
2
Public-key cryptography including RSA, DSA, ECDSA, EdDSA, and key exchange algorithms. Supports key generation, digital signatures, encryption, and key agreement protocols.
3
4
## Core Imports
5
6
```python
7
from cryptography.hazmat.primitives.asymmetric import rsa, dsa, ec, ed25519, ed448, x25519, x448, dh, padding
8
from cryptography.hazmat.primitives import hashes, serialization
9
```
10
11
## Capabilities
12
13
### RSA (Rivest-Shamir-Adleman)
14
15
RSA public-key cryptosystem supporting both encryption and digital signatures.
16
17
```python { .api }
18
def generate_private_key(public_exponent: int, key_size: int, backend=None) -> RSAPrivateKey:
19
"""
20
Generate RSA private key.
21
22
Args:
23
public_exponent (int): Public exponent (typically 65537)
24
key_size (int): Key size in bits (2048, 3072, or 4096 recommended)
25
backend: Cryptographic backend (usually None)
26
27
Returns:
28
RSAPrivateKey: Generated private key
29
"""
30
31
class RSAPrivateKey:
32
def public_key(self) -> RSAPublicKey:
33
"""Get corresponding public key"""
34
35
def sign(self, data: bytes, padding_algo, algorithm) -> bytes:
36
"""
37
Sign data with private key.
38
39
Args:
40
data (bytes): Data to sign
41
padding_algo: Padding algorithm (PSS() or PKCS1v15())
42
algorithm: Hash algorithm (e.g., hashes.SHA256())
43
44
Returns:
45
bytes: Digital signature
46
"""
47
48
def decrypt(self, ciphertext: bytes, padding_algo) -> bytes:
49
"""
50
Decrypt data with private key.
51
52
Args:
53
ciphertext (bytes): Encrypted data
54
padding_algo: Padding algorithm (OAEP() or PKCS1v15())
55
56
Returns:
57
bytes: Decrypted plaintext
58
"""
59
60
def private_bytes(self, encoding, format, encryption_algorithm) -> bytes:
61
"""Serialize private key to bytes"""
62
63
@property
64
def key_size(self) -> int:
65
"""Key size in bits"""
66
67
class RSAPublicKey:
68
def verify(self, signature: bytes, data: bytes, padding_algo, algorithm) -> None:
69
"""
70
Verify signature.
71
72
Args:
73
signature (bytes): Signature to verify
74
data (bytes): Original data that was signed
75
padding_algo: Same padding used for signing
76
algorithm: Same hash algorithm used for signing
77
78
Raises:
79
InvalidSignature: If signature verification fails
80
"""
81
82
def encrypt(self, plaintext: bytes, padding_algo) -> bytes:
83
"""
84
Encrypt data with public key.
85
86
Args:
87
plaintext (bytes): Data to encrypt
88
padding_algo: Padding algorithm (OAEP() recommended)
89
90
Returns:
91
bytes: Encrypted ciphertext
92
"""
93
94
def public_bytes(self, encoding, format) -> bytes:
95
"""Serialize public key to bytes"""
96
97
@property
98
def key_size(self) -> int:
99
"""Key size in bits"""
100
```
101
102
### Elliptic Curve Cryptography (ECC)
103
104
ECDSA for digital signatures and ECDH for key exchange.
105
106
```python { .api }
107
def generate_private_key(curve, backend=None) -> EllipticCurvePrivateKey:
108
"""
109
Generate EC private key.
110
111
Args:
112
curve: Elliptic curve (SECP256R1(), SECP384R1(), SECP521R1(), etc.)
113
backend: Cryptographic backend
114
115
Returns:
116
EllipticCurvePrivateKey: Generated private key
117
"""
118
119
class EllipticCurvePrivateKey:
120
def public_key(self) -> EllipticCurvePublicKey:
121
"""Get corresponding public key"""
122
123
def sign(self, data: bytes, algorithm) -> bytes:
124
"""
125
Sign data with ECDSA.
126
127
Args:
128
data (bytes): Data to sign
129
algorithm: Hash algorithm (e.g., hashes.SHA256())
130
131
Returns:
132
bytes: DER-encoded ECDSA signature
133
"""
134
135
def exchange(self, peer_public_key: EllipticCurvePublicKey) -> bytes:
136
"""
137
Perform ECDH key exchange.
138
139
Args:
140
peer_public_key: Other party's public key
141
142
Returns:
143
bytes: Shared secret
144
"""
145
146
def private_bytes(self, encoding, format, encryption_algorithm) -> bytes:
147
"""Serialize private key"""
148
149
class EllipticCurvePublicKey:
150
def verify(self, signature: bytes, data: bytes, algorithm) -> None:
151
"""
152
Verify ECDSA signature.
153
154
Raises:
155
InvalidSignature: If verification fails
156
"""
157
158
def public_bytes(self, encoding, format) -> bytes:
159
"""Serialize public key"""
160
161
# Common elliptic curves
162
class SECP256R1:
163
"""NIST P-256 curve (256-bit)"""
164
name = "secp256r1"
165
166
class SECP384R1:
167
"""NIST P-384 curve (384-bit)"""
168
name = "secp384r1"
169
170
class SECP521R1:
171
"""NIST P-521 curve (521-bit)"""
172
name = "secp521r1"
173
174
class SECP256K1:
175
"""Bitcoin curve (256-bit)"""
176
name = "secp256k1"
177
```
178
179
### EdDSA (Edwards-curve Digital Signature Algorithm)
180
181
Modern signature algorithms using Edwards curves.
182
183
```python { .api }
184
class Ed25519PrivateKey:
185
@classmethod
186
def generate(cls) -> 'Ed25519PrivateKey':
187
"""Generate Ed25519 private key"""
188
189
@classmethod
190
def from_private_bytes(cls, data: bytes) -> 'Ed25519PrivateKey':
191
"""Load from 32-byte private key"""
192
193
def public_key(self) -> Ed25519PublicKey:
194
"""Get corresponding public key"""
195
196
def sign(self, data: bytes) -> bytes:
197
"""
198
Sign data with Ed25519.
199
200
Args:
201
data (bytes): Data to sign (no hashing needed)
202
203
Returns:
204
bytes: 64-byte signature
205
"""
206
207
def private_bytes(self, encoding, format, encryption_algorithm) -> bytes:
208
"""Serialize private key"""
209
210
class Ed25519PublicKey:
211
@classmethod
212
def from_public_bytes(cls, data: bytes) -> 'Ed25519PublicKey':
213
"""Load from 32-byte public key"""
214
215
def verify(self, signature: bytes, data: bytes) -> None:
216
"""
217
Verify Ed25519 signature.
218
219
Args:
220
signature (bytes): 64-byte signature
221
data (bytes): Original data
222
223
Raises:
224
InvalidSignature: If verification fails
225
"""
226
227
def public_bytes(self, encoding, format) -> bytes:
228
"""Serialize public key"""
229
230
class Ed448PrivateKey:
231
@classmethod
232
def generate(cls) -> 'Ed448PrivateKey':
233
"""Generate Ed448 private key"""
234
235
def public_key(self) -> Ed448PublicKey:
236
"""Get corresponding public key"""
237
238
def sign(self, data: bytes) -> bytes:
239
"""Sign with Ed448 (114-byte signature)"""
240
241
class Ed448PublicKey:
242
def verify(self, signature: bytes, data: bytes) -> None:
243
"""Verify Ed448 signature"""
244
```
245
246
### X25519/X448 Key Exchange
247
248
Elliptic Curve Diffie-Hellman using Curve25519 and Curve448.
249
250
```python { .api }
251
class X25519PrivateKey:
252
@classmethod
253
def generate(cls) -> 'X25519PrivateKey':
254
"""Generate X25519 private key"""
255
256
@classmethod
257
def from_private_bytes(cls, data: bytes) -> 'X25519PrivateKey':
258
"""Load from 32-byte private key"""
259
260
def public_key(self) -> X25519PublicKey:
261
"""Get corresponding public key"""
262
263
def exchange(self, peer_public_key: X25519PublicKey) -> bytes:
264
"""
265
Perform X25519 key exchange.
266
267
Args:
268
peer_public_key: Other party's public key
269
270
Returns:
271
bytes: 32-byte shared secret
272
"""
273
274
class X25519PublicKey:
275
@classmethod
276
def from_public_bytes(cls, data: bytes) -> 'X25519PublicKey':
277
"""Load from 32-byte public key"""
278
279
class X448PrivateKey:
280
@classmethod
281
def generate(cls) -> 'X448PrivateKey':
282
"""Generate X448 private key"""
283
284
def exchange(self, peer_public_key: X448PublicKey) -> bytes:
285
"""Perform X448 key exchange (56-byte shared secret)"""
286
287
class X448PublicKey:
288
@classmethod
289
def from_public_bytes(cls, data: bytes) -> 'X448PublicKey':
290
"""Load from 56-byte public key"""
291
```
292
293
### RSA Padding Schemes
294
295
```python { .api }
296
class PKCS1v15:
297
"""PKCS#1 v1.5 padding (legacy, use OAEP for encryption)"""
298
299
class PSS:
300
def __init__(self, mgf, salt_length: int):
301
"""
302
PSS padding for RSA signatures.
303
304
Args:
305
mgf: Mask generation function (usually MGF1())
306
salt_length: Salt length (PSS.MAX_LENGTH for maximum)
307
"""
308
309
MAX_LENGTH: int = -1 # Use maximum salt length
310
311
class OAEP:
312
def __init__(self, mgf, algorithm, label: bytes = None):
313
"""
314
OAEP padding for RSA encryption.
315
316
Args:
317
mgf: Mask generation function (MGF1())
318
algorithm: Hash algorithm
319
label: Optional label (usually None)
320
"""
321
322
class MGF1:
323
def __init__(self, algorithm):
324
"""
325
MGF1 mask generation function.
326
327
Args:
328
algorithm: Hash algorithm (e.g., hashes.SHA256())
329
"""
330
```
331
332
## Usage Examples
333
334
### RSA Key Generation and Digital Signatures
335
336
```python
337
from cryptography.hazmat.primitives.asymmetric import rsa, padding
338
from cryptography.hazmat.primitives import hashes, serialization
339
from cryptography.exceptions import InvalidSignature
340
341
# Generate RSA key pair
342
private_key = rsa.generate_private_key(
343
public_exponent=65537,
344
key_size=2048 # Use 3072 or 4096 for higher security
345
)
346
public_key = private_key.public_key()
347
348
# Sign data
349
message = b"Important document content"
350
signature = private_key.sign(
351
message,
352
padding.PSS(
353
mgf=padding.MGF1(hashes.SHA256()),
354
salt_length=padding.PSS.MAX_LENGTH
355
),
356
hashes.SHA256()
357
)
358
359
print(f"Signature: {signature.hex()}")
360
361
# Verify signature
362
try:
363
public_key.verify(
364
signature,
365
message,
366
padding.PSS(
367
mgf=padding.MGF1(hashes.SHA256()),
368
salt_length=padding.PSS.MAX_LENGTH
369
),
370
hashes.SHA256()
371
)
372
print("Signature valid")
373
except InvalidSignature:
374
print("Signature invalid")
375
376
# Save keys
377
private_pem = private_key.private_bytes(
378
encoding=serialization.Encoding.PEM,
379
format=serialization.PrivateFormat.PKCS8,
380
encryption_algorithm=serialization.NoEncryption()
381
)
382
383
public_pem = public_key.public_bytes(
384
encoding=serialization.Encoding.PEM,
385
format=serialization.PublicFormat.SubjectPublicKeyInfo
386
)
387
388
with open('private_key.pem', 'wb') as f:
389
f.write(private_pem)
390
with open('public_key.pem', 'wb') as f:
391
f.write(public_pem)
392
```
393
394
### RSA Encryption and Decryption
395
396
```python
397
from cryptography.hazmat.primitives.asymmetric import rsa, padding
398
from cryptography.hazmat.primitives import hashes
399
400
# Generate keys
401
private_key = rsa.generate_private_key(65537, 2048)
402
public_key = private_key.public_key()
403
404
# Encrypt small message with public key
405
message = b"Secret message to encrypt"
406
ciphertext = public_key.encrypt(
407
message,
408
padding.OAEP(
409
mgf=padding.MGF1(algorithm=hashes.SHA256()),
410
algorithm=hashes.SHA256(),
411
label=None
412
)
413
)
414
415
print(f"Encrypted: {ciphertext.hex()}")
416
417
# Decrypt with private key
418
plaintext = private_key.decrypt(
419
ciphertext,
420
padding.OAEP(
421
mgf=padding.MGF1(algorithm=hashes.SHA256()),
422
algorithm=hashes.SHA256(),
423
label=None
424
)
425
)
426
427
print(f"Decrypted: {plaintext}")
428
```
429
430
### ECDSA with P-256
431
432
```python
433
from cryptography.hazmat.primitives.asymmetric import ec
434
from cryptography.hazmat.primitives import hashes
435
from cryptography.exceptions import InvalidSignature
436
437
# Generate ECDSA key pair
438
private_key = ec.generate_private_key(ec.SECP256R1())
439
public_key = private_key.public_key()
440
441
# Sign data
442
data = b"Data to sign with ECDSA"
443
signature = private_key.sign(data, hashes.SHA256())
444
445
print(f"ECDSA signature: {signature.hex()}")
446
447
# Verify signature
448
try:
449
public_key.verify(signature, data, hashes.SHA256())
450
print("ECDSA signature valid")
451
except InvalidSignature:
452
print("ECDSA signature invalid")
453
```
454
455
### Ed25519 Digital Signatures
456
457
```python
458
from cryptography.hazmat.primitives.asymmetric import ed25519
459
from cryptography.exceptions import InvalidSignature
460
461
# Generate Ed25519 key pair
462
private_key = ed25519.Ed25519PrivateKey.generate()
463
public_key = private_key.public_key()
464
465
# Sign data (no hash algorithm needed)
466
message = b"Message to sign with Ed25519"
467
signature = private_key.sign(message)
468
469
print(f"Ed25519 signature: {signature.hex()}")
470
print(f"Signature length: {len(signature)} bytes")
471
472
# Verify signature
473
try:
474
public_key.verify(signature, message)
475
print("Ed25519 signature valid")
476
except InvalidSignature:
477
print("Ed25519 signature invalid")
478
479
# Serialize keys (raw bytes)
480
private_bytes = private_key.private_bytes(
481
encoding=serialization.Encoding.Raw,
482
format=serialization.PrivateFormat.Raw,
483
encryption_algorithm=serialization.NoEncryption()
484
)
485
486
public_bytes = public_key.public_bytes(
487
encoding=serialization.Encoding.Raw,
488
format=serialization.PublicFormat.Raw
489
)
490
491
print(f"Private key: {private_bytes.hex()} ({len(private_bytes)} bytes)")
492
print(f"Public key: {public_bytes.hex()} ({len(public_bytes)} bytes)")
493
```
494
495
### X25519 Key Exchange
496
497
```python
498
from cryptography.hazmat.primitives.asymmetric import x25519
499
from cryptography.hazmat.primitives import hashes
500
from cryptography.hazmat.primitives.kdf.hkdf import HKDF
501
502
# Alice generates her key pair
503
alice_private = x25519.X25519PrivateKey.generate()
504
alice_public = alice_private.public_key()
505
506
# Bob generates his key pair
507
bob_private = x25519.X25519PrivateKey.generate()
508
bob_public = bob_private.public_key()
509
510
# Alice performs key exchange
511
alice_shared_secret = alice_private.exchange(bob_public)
512
513
# Bob performs key exchange
514
bob_shared_secret = bob_private.exchange(alice_public)
515
516
# Verify both parties have same shared secret
517
assert alice_shared_secret == bob_shared_secret
518
print(f"Shared secret: {alice_shared_secret.hex()}")
519
520
# Derive encryption key from shared secret
521
derived_key = HKDF(
522
algorithm=hashes.SHA256(),
523
length=32,
524
salt=None,
525
info=b'key exchange session',
526
).derive(alice_shared_secret)
527
528
print(f"Derived key: {derived_key.hex()}")
529
```
530
531
### Hybrid Encryption (RSA + AES)
532
533
```python
534
from cryptography.hazmat.primitives.asymmetric import rsa, padding
535
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
536
from cryptography.hazmat.primitives import hashes
537
import os
538
539
class HybridEncryption:
540
def __init__(self, rsa_public_key):
541
self.rsa_public_key = rsa_public_key
542
543
def encrypt(self, data: bytes) -> dict:
544
"""Encrypt large data using hybrid encryption"""
545
# Generate random AES key
546
aes_key = AESGCM.generate_key(256)
547
aesgcm = AESGCM(aes_key)
548
549
# Encrypt data with AES
550
nonce = os.urandom(12)
551
ciphertext = aesgcm.encrypt(nonce, data, None)
552
553
# Encrypt AES key with RSA
554
encrypted_key = self.rsa_public_key.encrypt(
555
aes_key,
556
padding.OAEP(
557
mgf=padding.MGF1(algorithm=hashes.SHA256()),
558
algorithm=hashes.SHA256(),
559
label=None
560
)
561
)
562
563
return {
564
'encrypted_key': encrypted_key,
565
'nonce': nonce,
566
'ciphertext': ciphertext
567
}
568
569
def decrypt(self, encrypted_data: dict, rsa_private_key):
570
"""Decrypt hybrid encrypted data"""
571
# Decrypt AES key with RSA
572
aes_key = rsa_private_key.decrypt(
573
encrypted_data['encrypted_key'],
574
padding.OAEP(
575
mgf=padding.MGF1(algorithm=hashes.SHA256()),
576
algorithm=hashes.SHA256(),
577
label=None
578
)
579
)
580
581
# Decrypt data with AES
582
aesgcm = AESGCM(aes_key)
583
plaintext = aesgcm.decrypt(
584
encrypted_data['nonce'],
585
encrypted_data['ciphertext'],
586
None
587
)
588
589
return plaintext
590
591
# Usage
592
# Generate RSA key pair
593
rsa_private = rsa.generate_private_key(65537, 2048)
594
rsa_public = rsa_private.public_key()
595
596
# Create hybrid encryption instance
597
hybrid = HybridEncryption(rsa_public)
598
599
# Encrypt large data
600
large_data = b"This is a large document that would be too big for RSA encryption alone. " * 100
601
encrypted = hybrid.encrypt(large_data)
602
603
print(f"Encrypted key size: {len(encrypted['encrypted_key'])} bytes")
604
print(f"Ciphertext size: {len(encrypted['ciphertext'])} bytes")
605
606
# Decrypt
607
decrypted = hybrid.decrypt(encrypted, rsa_private)
608
print(f"Decryption successful: {decrypted == large_data}")
609
```
610
611
### Digital Certificate Creation with Asymmetric Keys
612
613
```python
614
from cryptography.hazmat.primitives.asymmetric import rsa
615
from cryptography import x509
616
from cryptography.x509.oid import NameOID
617
from cryptography.hazmat.primitives import hashes, serialization
618
import datetime
619
620
# Generate key pair for certificate
621
private_key = rsa.generate_private_key(65537, 2048)
622
public_key = private_key.public_key()
623
624
# Create self-signed certificate
625
subject = issuer = x509.Name([
626
x509.NameAttribute(NameOID.COUNTRY_NAME, "US"),
627
x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, "California"),
628
x509.NameAttribute(NameOID.LOCALITY_NAME, "San Francisco"),
629
x509.NameAttribute(NameOID.ORGANIZATION_NAME, "My Organization"),
630
x509.NameAttribute(NameOID.COMMON_NAME, "example.com"),
631
])
632
633
cert = x509.CertificateBuilder().subject_name(
634
subject
635
).issuer_name(
636
issuer
637
).public_key(
638
public_key
639
).serial_number(
640
x509.random_serial_number()
641
).not_valid_before(
642
datetime.datetime.utcnow()
643
).not_valid_after(
644
datetime.datetime.utcnow() + datetime.timedelta(days=365)
645
).add_extension(
646
x509.BasicConstraints(ca=False, path_length=None),
647
critical=True,
648
).sign(private_key, hashes.SHA256())
649
650
# Save certificate
651
cert_pem = cert.public_bytes(serialization.Encoding.PEM)
652
with open('self_signed_cert.pem', 'wb') as f:
653
f.write(cert_pem)
654
655
print("Self-signed certificate created")
656
```
657
658
## Security Considerations
659
660
- **Key Sizes**: Use RSA 2048+ bits, EC 256+ bits for new applications
661
- **Algorithm Selection**: Prefer Ed25519/Ed448 for signatures, X25519/X448 for key exchange
662
- **Padding**: Always use OAEP for RSA encryption, PSS for RSA signatures
663
- **Randomness**: Use cryptographically secure random number generators
664
- **Key Management**: Store private keys securely, never hardcode them
665
- **Hybrid Encryption**: Use asymmetric crypto for key exchange, symmetric for data
666
- **Signature Verification**: Always verify signatures before trusting data
667
- **Forward Secrecy**: Use ephemeral keys for key exchange when possible