or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

aead-ciphers.mdasymmetric-cryptography.mdhash-functions.mdindex.mdkey-derivation.mdkey-serialization.mdmessage-authentication.mdsymmetric-ciphers.mdsymmetric-encryption.mdtwo-factor-auth.mdutilities.mdx509-certificates.md

asymmetric-cryptography.mddocs/

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