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

key-serialization.mddocs/

0

# Key Serialization

1

2

Serialization and deserialization of cryptographic keys in various formats including PEM, DER, and SSH formats. Essential for key storage, transmission, and interoperability.

3

4

## Core Imports

5

6

```python

7

from cryptography.hazmat.primitives import serialization

8

from cryptography.hazmat.primitives.serialization import ssh, pkcs7, pkcs12

9

```

10

11

## Capabilities

12

13

### Key Loading Functions

14

15

```python { .api }

16

def load_pem_private_key(data: bytes, password: bytes = None, backend=None) -> PrivateKey:

17

"""Load private key from PEM format"""

18

19

def load_der_private_key(data: bytes, password: bytes = None, backend=None) -> PrivateKey:

20

"""Load private key from DER format"""

21

22

def load_pem_public_key(data: bytes, backend=None) -> PublicKey:

23

"""Load public key from PEM format"""

24

25

def load_der_public_key(data: bytes, backend=None) -> PublicKey:

26

"""Load public key from DER format"""

27

28

def load_ssh_private_key(data: bytes, password: bytes = None, backend=None) -> PrivateKey:

29

"""Load SSH private key"""

30

31

def load_ssh_public_key(data: bytes, backend=None) -> PublicKey:

32

"""Load SSH public key"""

33

```

34

35

### Encoding Formats

36

37

```python { .api }

38

class Encoding:

39

PEM: Encoding # Base64 with headers

40

DER: Encoding # Binary ASN.1

41

OpenSSH: Encoding # SSH format

42

Raw: Encoding # Raw bytes

43

X962: Encoding # X9.62 format for EC keys

44

45

class PrivateFormat:

46

PKCS8: PrivateFormat # PKCS#8 (preferred)

47

TraditionalOpenSSL: PrivateFormat # Traditional format

48

Raw: PrivateFormat # Raw key bytes

49

OpenSSH: PrivateFormat # SSH private key format

50

51

class PublicFormat:

52

SubjectPublicKeyInfo: PublicFormat # X.509 SubjectPublicKeyInfo (preferred)

53

PKCS1: PublicFormat # PKCS#1 RSA format

54

OpenSSH: PublicFormat # SSH public key format

55

Raw: PublicFormat # Raw key bytes

56

CompressedPoint: PublicFormat # Compressed EC point

57

UncompressedPoint: PublicFormat # Uncompressed EC point

58

```

59

60

### Encryption Algorithms

61

62

```python { .api }

63

class KeySerializationEncryption:

64

"""Abstract base for key encryption"""

65

66

class NoEncryption(KeySerializationEncryption):

67

"""No encryption for private keys"""

68

69

class BestAvailableEncryption(KeySerializationEncryption):

70

def __init__(self, password: bytes):

71

"""

72

Best available encryption for private keys.

73

74

Args:

75

password (bytes): Password for key encryption

76

"""

77

```

78

79

### SSH Key Support

80

81

```python { .api }

82

def ssh_key_fingerprint(public_key, algorithm=hashes.SHA256()) -> bytes:

83

"""Generate SSH key fingerprint"""

84

85

class SSHCertificate:

86

"""SSH certificate representation"""

87

88

@property

89

def public_key(self):

90

"""Certificate public key"""

91

92

@property

93

def serial(self) -> int:

94

"""Certificate serial number"""

95

```

96

97

## Usage Examples

98

99

### Save and Load RSA Keys

100

101

```python

102

from cryptography.hazmat.primitives.asymmetric import rsa

103

from cryptography.hazmat.primitives import serialization

104

105

# Generate RSA key

106

private_key = rsa.generate_private_key(65537, 2048)

107

public_key = private_key.public_key()

108

109

# Save private key (encrypted)

110

encrypted_private = private_key.private_bytes(

111

encoding=serialization.Encoding.PEM,

112

format=serialization.PrivateFormat.PKCS8,

113

encryption_algorithm=serialization.BestAvailableEncryption(b'mypassword')

114

)

115

116

# Save private key (unencrypted - less secure)

117

unencrypted_private = private_key.private_bytes(

118

encoding=serialization.Encoding.PEM,

119

format=serialization.PrivateFormat.PKCS8,

120

encryption_algorithm=serialization.NoEncryption()

121

)

122

123

# Save public key

124

public_pem = public_key.public_bytes(

125

encoding=serialization.Encoding.PEM,

126

format=serialization.PublicFormat.SubjectPublicKeyInfo

127

)

128

129

# Write to files

130

with open('private_key_encrypted.pem', 'wb') as f:

131

f.write(encrypted_private)

132

133

with open('public_key.pem', 'wb') as f:

134

f.write(public_pem)

135

136

# Load keys back

137

with open('private_key_encrypted.pem', 'rb') as f:

138

loaded_private = serialization.load_pem_private_key(

139

f.read(),

140

password=b'mypassword'

141

)

142

143

with open('public_key.pem', 'rb') as f:

144

loaded_public = serialization.load_pem_public_key(f.read())

145

146

print("Keys loaded successfully")

147

```

148

149

### SSH Key Format

150

151

```python

152

from cryptography.hazmat.primitives.asymmetric import rsa

153

from cryptography.hazmat.primitives import serialization, hashes

154

155

# Generate key

156

private_key = rsa.generate_private_key(65537, 2048)

157

public_key = private_key.public_key()

158

159

# SSH public key format

160

ssh_public = public_key.public_bytes(

161

encoding=serialization.Encoding.OpenSSH,

162

format=serialization.PublicFormat.OpenSSH

163

)

164

165

# SSH private key format

166

ssh_private = private_key.private_bytes(

167

encoding=serialization.Encoding.PEM,

168

format=serialization.PrivateFormat.OpenSSH,

169

encryption_algorithm=serialization.NoEncryption()

170

)

171

172

print("SSH public key:")

173

print(ssh_public.decode())

174

175

# Generate SSH key fingerprint

176

fingerprint = serialization.ssh_key_fingerprint(public_key, hashes.SHA256())

177

print(f"SSH fingerprint: {fingerprint.hex()}")

178

179

# Save SSH keys

180

with open('id_rsa', 'wb') as f:

181

f.write(ssh_private)

182

183

with open('id_rsa.pub', 'wb') as f:

184

f.write(ssh_public)

185

```

186

187

### Ed25519 Key Serialization

188

189

```python

190

from cryptography.hazmat.primitives.asymmetric import ed25519

191

from cryptography.hazmat.primitives import serialization

192

193

# Generate Ed25519 key

194

private_key = ed25519.Ed25519PrivateKey.generate()

195

public_key = private_key.public_key()

196

197

# Raw format (32 bytes each)

198

private_raw = private_key.private_bytes(

199

encoding=serialization.Encoding.Raw,

200

format=serialization.PrivateFormat.Raw,

201

encryption_algorithm=serialization.NoEncryption()

202

)

203

204

public_raw = public_key.public_bytes(

205

encoding=serialization.Encoding.Raw,

206

format=serialization.PublicFormat.Raw

207

)

208

209

print(f"Ed25519 private key (raw): {private_raw.hex()} ({len(private_raw)} bytes)")

210

print(f"Ed25519 public key (raw): {public_raw.hex()} ({len(public_raw)} bytes)")

211

212

# PEM format

213

private_pem = private_key.private_bytes(

214

encoding=serialization.Encoding.PEM,

215

format=serialization.PrivateFormat.PKCS8,

216

encryption_algorithm=serialization.NoEncryption()

217

)

218

219

public_pem = public_key.public_bytes(

220

encoding=serialization.Encoding.PEM,

221

format=serialization.PublicFormat.SubjectPublicKeyInfo

222

)

223

224

print("Ed25519 PEM private key:")

225

print(private_pem.decode())

226

```

227

228

### Key Format Conversion

229

230

```python

231

from cryptography.hazmat.primitives import serialization

232

from cryptography.hazmat.primitives.asymmetric import rsa

233

234

# Load existing key (any format)

235

with open('existing_key.pem', 'rb') as f:

236

private_key = serialization.load_pem_private_key(f.read(), password=None)

237

238

# Convert to different formats

239

formats = {

240

'pkcs8_pem': (serialization.Encoding.PEM, serialization.PrivateFormat.PKCS8),

241

'pkcs8_der': (serialization.Encoding.DER, serialization.PrivateFormat.PKCS8),

242

'traditional_pem': (serialization.Encoding.PEM, serialization.PrivateFormat.TraditionalOpenSSL),

243

'ssh': (serialization.Encoding.PEM, serialization.PrivateFormat.OpenSSH),

244

}

245

246

for name, (encoding, format_type) in formats.items():

247

try:

248

key_bytes = private_key.private_bytes(

249

encoding=encoding,

250

format=format_type,

251

encryption_algorithm=serialization.NoEncryption()

252

)

253

254

with open(f'key_{name}', 'wb') as f:

255

f.write(key_bytes)

256

257

print(f"Converted to {name}")

258

except Exception as e:

259

print(f"Failed to convert to {name}: {e}")

260

```

261

262

### Secure Key Storage Class

263

264

```python

265

from cryptography.hazmat.primitives import serialization

266

from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC

267

from cryptography.hazmat.primitives import hashes

268

import os

269

import getpass

270

271

class SecureKeyStorage:

272

def save_private_key(self, private_key, filepath: str, password: str = None):

273

"""Save private key with optional password protection"""

274

if password is None:

275

password = getpass.getpass("Enter password for private key: ")

276

277

if password:

278

encryption = serialization.BestAvailableEncryption(password.encode())

279

else:

280

encryption = serialization.NoEncryption()

281

282

key_bytes = private_key.private_bytes(

283

encoding=serialization.Encoding.PEM,

284

format=serialization.PrivateFormat.PKCS8,

285

encryption_algorithm=encryption

286

)

287

288

with open(filepath, 'wb') as f:

289

f.write(key_bytes)

290

291

print(f"Private key saved to {filepath}")

292

293

def load_private_key(self, filepath: str, password: str = None):

294

"""Load private key with password prompt if needed"""

295

with open(filepath, 'rb') as f:

296

key_data = f.read()

297

298

# Try without password first

299

try:

300

return serialization.load_pem_private_key(key_data, password=None)

301

except TypeError:

302

# Key is encrypted, need password

303

if password is None:

304

password = getpass.getpass("Enter password for private key: ")

305

306

return serialization.load_pem_private_key(key_data, password=password.encode())

307

308

def save_public_key(self, public_key, filepath: str, format_type: str = 'pem'):

309

"""Save public key in specified format"""

310

if format_type.lower() == 'ssh':

311

key_bytes = public_key.public_bytes(

312

encoding=serialization.Encoding.OpenSSH,

313

format=serialization.PublicFormat.OpenSSH

314

)

315

else: # PEM

316

key_bytes = public_key.public_bytes(

317

encoding=serialization.Encoding.PEM,

318

format=serialization.PublicFormat.SubjectPublicKeyInfo

319

)

320

321

with open(filepath, 'wb') as f:

322

f.write(key_bytes)

323

324

print(f"Public key saved to {filepath}")

325

326

# Usage

327

from cryptography.hazmat.primitives.asymmetric import rsa

328

329

key_storage = SecureKeyStorage()

330

331

# Generate and save key pair

332

private_key = rsa.generate_private_key(65537, 2048)

333

public_key = private_key.public_key()

334

335

key_storage.save_private_key(private_key, 'my_private_key.pem', 'secure_password')

336

key_storage.save_public_key(public_key, 'my_public_key.pem')

337

key_storage.save_public_key(public_key, 'my_public_key.ssh', format_type='ssh')

338

339

# Load key back

340

loaded_key = key_storage.load_private_key('my_private_key.pem', 'secure_password')

341

print("Key loaded successfully")

342

```

343

344

## Security Considerations

345

346

- **Password Protection**: Always encrypt private keys when storing

347

- **Secure Deletion**: Securely wipe unencrypted key data from memory

348

- **Format Selection**: Use PKCS#8 for interoperability

349

- **Key Validation**: Validate loaded keys before use

350

- **Access Control**: Restrict file permissions on key files (600/400)

351

- **Key Rotation**: Regularly rotate and re-encrypt stored keys

352

353

### PKCS#7/CMS Support

354

355

PKCS#7 (Cryptographic Message Syntax) for packaging and signing certificates and data.

356

357

```python { .api }

358

def load_pem_pkcs7_certificates(data: bytes) -> List[Certificate]:

359

"""

360

Load certificates from PKCS#7 PEM data.

361

362

Args:

363

data (bytes): PEM-encoded PKCS#7 data

364

365

Returns:

366

List[Certificate]: List of certificates from PKCS#7 structure

367

"""

368

369

def load_der_pkcs7_certificates(data: bytes) -> List[Certificate]:

370

"""

371

Load certificates from PKCS#7 DER data.

372

373

Args:

374

data (bytes): DER-encoded PKCS#7 data

375

376

Returns:

377

List[Certificate]: List of certificates from PKCS#7 structure

378

"""

379

380

def serialize_certificates(certificates: List[Certificate], encoding: Encoding) -> bytes:

381

"""

382

Serialize certificates to PKCS#7 format.

383

384

Args:

385

certificates: List of certificates to serialize

386

encoding: Encoding format (PEM or DER)

387

388

Returns:

389

bytes: PKCS#7 encoded certificate data

390

"""

391

392

class PKCS7Options:

393

Text: PKCS7Options # Add text/plain MIME type

394

Binary: PKCS7Options # Don't translate to canonical MIME

395

DetachedSignature: PKCS7Options # Don't embed data in PKCS#7

396

```

397

398

### PKCS#12 Support

399

400

PKCS#12 format for packaging private keys with certificates, commonly used for key/certificate bundles.

401

402

```python { .api }

403

def load_key_and_certificates(data: bytes, password: bytes = None) -> Tuple[PrivateKey, Certificate, List[Certificate]]:

404

"""

405

Load private key and certificates from PKCS#12 data.

406

407

Args:

408

data (bytes): PKCS#12 data

409

password (bytes): Password for PKCS#12 (None if unencrypted)

410

411

Returns:

412

Tuple containing:

413

- PrivateKey: The private key (or None)

414

- Certificate: The certificate (or None)

415

- List[Certificate]: Additional certificates

416

"""

417

418

def load_pkcs12(data: bytes, password: bytes = None) -> PKCS12KeyAndCertificates:

419

"""

420

Load PKCS#12 data into structured object.

421

422

Args:

423

data (bytes): PKCS#12 data

424

password (bytes): Password for PKCS#12

425

426

Returns:

427

PKCS12KeyAndCertificates: Structured PKCS#12 data

428

"""

429

430

def serialize_key_and_certificates(name: bytes, key: PrivateKey, cert: Certificate, cas: List[Certificate] = None, encryption_algorithm=None) -> bytes:

431

"""

432

Serialize key and certificates to PKCS#12 format.

433

434

Args:

435

name (bytes): Friendly name for the key/cert

436

key: Private key to include

437

cert: Certificate to include

438

cas: Additional certificates to include

439

encryption_algorithm: Encryption for the PKCS#12 data

440

441

Returns:

442

bytes: PKCS#12 encoded data

443

"""

444

445

class PKCS12KeyAndCertificates:

446

"""Container for PKCS#12 key and certificate data"""

447

448

@property

449

def key(self) -> PrivateKey:

450

"""Private key from PKCS#12"""

451

452

@property

453

def cert(self) -> PKCS12Certificate:

454

"""Main certificate from PKCS#12"""

455

456

@property

457

def additional_certificates(self) -> List[PKCS12Certificate]:

458

"""Additional certificates from PKCS#12"""

459

460

class PKCS12Certificate:

461

"""Certificate from PKCS#12 with additional metadata"""

462

463

@property

464

def certificate(self) -> Certificate:

465

"""The X.509 certificate"""

466

467

@property

468

def friendly_name(self) -> bytes:

469

"""Friendly name for the certificate"""

470

```

471

472

#### PKCS#7 Usage Example

473

474

```python

475

from cryptography.hazmat.primitives.serialization import pkcs7

476

from cryptography import x509

477

from cryptography.hazmat.primitives import serialization

478

479

# Load certificates from PKCS#7

480

with open('certificates.p7b', 'rb') as f:

481

p7b_data = f.read()

482

483

certificates = pkcs7.load_pem_pkcs7_certificates(p7b_data)

484

print(f"Loaded {len(certificates)} certificates from PKCS#7")

485

486

# Serialize certificates back to PKCS#7

487

pkcs7_data = pkcs7.serialize_certificates(

488

certificates,

489

encoding=serialization.Encoding.PEM

490

)

491

492

with open('output.p7b', 'wb') as f:

493

f.write(pkcs7_data)

494

```

495

496

#### PKCS#12 Usage Example

497

498

```python

499

from cryptography.hazmat.primitives.serialization import pkcs12

500

from cryptography.hazmat.primitives.asymmetric import rsa

501

from cryptography import x509

502

from cryptography.hazmat.primitives import hashes, serialization

503

import datetime

504

505

# Create a key and self-signed certificate

506

private_key = rsa.generate_private_key(65537, 2048)

507

public_key = private_key.public_key()

508

509

# Build certificate

510

builder = x509.CertificateBuilder()

511

builder = builder.subject_name(x509.Name([

512

x509.NameAttribute(x509.NameOID.COMMON_NAME, "Test Certificate")

513

]))

514

builder = builder.issuer_name(x509.Name([

515

x509.NameAttribute(x509.NameOID.COMMON_NAME, "Test Certificate")

516

]))

517

builder = builder.public_key(public_key)

518

builder = builder.serial_number(1)

519

builder = builder.not_valid_before(datetime.datetime.utcnow())

520

builder = builder.not_valid_after(datetime.datetime.utcnow() + datetime.timedelta(days=365))

521

522

certificate = builder.sign(private_key, hashes.SHA256())

523

524

# Create PKCS#12 bundle

525

p12_data = pkcs12.serialize_key_and_certificates(

526

name=b"My Certificate",

527

key=private_key,

528

cert=certificate,

529

cas=None, # No additional certificates

530

encryption_algorithm=serialization.BestAvailableEncryption(b"password123")

531

)

532

533

# Save PKCS#12 file

534

with open('bundle.p12', 'wb') as f:

535

f.write(p12_data)

536

537

# Load PKCS#12 bundle back

538

with open('bundle.p12', 'rb') as f:

539

p12_data = f.read()

540

541

loaded_key, loaded_cert, additional_certs = pkcs12.load_key_and_certificates(

542

p12_data,

543

password=b"password123"

544

)

545

546

print(f"Loaded private key: {loaded_key}")

547

print(f"Loaded certificate: {loaded_cert.subject}")

548

print(f"Additional certificates: {len(additional_certs)}")

549

```