or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

asymmetric.mdbackend.mdindex.mdkdf.mdkeys.mdsymmetric.mdtls.mdtrust-store.mdutility.md

keys.mddocs/

0

# Key Management

1

2

Parsing and handling of cryptographic keys and certificates from various formats including DER, PEM, PKCS#8, and PKCS#12. These functions provide format detection and conversion capabilities for interoperability with different cryptographic systems.

3

4

## Capabilities

5

6

### Certificate Parsing

7

8

Parse X.509 certificates from DER or PEM format data.

9

10

```python { .api }

11

def parse_certificate(data: bytes) -> Certificate:

12

"""

13

Parse an X.509 certificate from DER or PEM data.

14

15

Parameters:

16

- data: bytes - Certificate data in DER or PEM format

17

18

Returns:

19

Certificate object with parsed certificate information

20

21

Raises:

22

ValueError if data is not a valid certificate

23

"""

24

```

25

26

### Private Key Parsing

27

28

Parse private keys from various formats including PKCS#8, PKCS#1, and OpenSSL formats.

29

30

```python { .api }

31

def parse_private(data: bytes) -> PrivateKey:

32

"""

33

Parse a private key from DER or PEM encoded data.

34

35

Parameters:

36

- data: bytes - Private key data in supported formats:

37

- PKCS#8 (encrypted or unencrypted)

38

- PKCS#1 RSA private key

39

- OpenSSL traditional format

40

- SEC1 EC private key

41

42

Returns:

43

PrivateKey object for the parsed key

44

45

Raises:

46

ValueError if data is not a valid private key

47

AsymmetricKeyError if key format is unsupported

48

"""

49

```

50

51

### Public Key Parsing

52

53

Parse public keys from DER or PEM format data.

54

55

```python { .api }

56

def parse_public(data: bytes) -> PublicKey:

57

"""

58

Parse a public key from DER or PEM encoded data.

59

60

Parameters:

61

- data: bytes - Public key data in supported formats:

62

- X.509 SubjectPublicKeyInfo

63

- PKCS#1 RSA public key

64

- RFC 3279 formats

65

66

Returns:

67

PublicKey object for the parsed key

68

69

Raises:

70

ValueError if data is not a valid public key

71

AsymmetricKeyError if key format is unsupported

72

"""

73

```

74

75

### PKCS#12 Bundle Parsing

76

77

Parse PKCS#12 bundles containing certificates, private keys, and certificate chains.

78

79

```python { .api }

80

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

81

"""

82

Parse a PKCS#12 bundle containing certificate and private key.

83

84

Parameters:

85

- data: bytes - PKCS#12 bundle data

86

- password: bytes - Password for encrypted bundle (None for unencrypted)

87

88

Returns:

89

Tuple of (end_entity_certificate, private_key, intermediate_certificates)

90

91

Raises:

92

ValueError if data is not a valid PKCS#12 bundle

93

AsymmetricKeyError if password is incorrect or bundle is corrupted

94

"""

95

```

96

97

## Usage Examples

98

99

### Certificate File Parsing

100

101

```python

102

from oscrypto.keys import parse_certificate

103

import os

104

105

def load_certificate_file(file_path: str):

106

"""Load and parse a certificate file."""

107

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

108

cert_data = f.read()

109

110

try:

111

certificate = parse_certificate(cert_data)

112

113

print(f"Certificate loaded from: {file_path}")

114

print(f"Subject: {certificate.subject}")

115

print(f"Issuer: {certificate.issuer}")

116

print(f"Serial Number: {certificate.serial_number}")

117

print(f"Valid From: {certificate.not_valid_before}")

118

print(f"Valid Until: {certificate.not_valid_after}")

119

print(f"Key Algorithm: {certificate.public_key.algorithm}")

120

121

if hasattr(certificate.public_key, 'bit_size'):

122

print(f"Key Size: {certificate.public_key.bit_size} bits")

123

124

return certificate

125

126

except ValueError as e:

127

print(f"Error parsing certificate: {e}")

128

return None

129

130

# Example usage

131

cert_files = [

132

'server.crt',

133

'ca-certificate.pem',

134

'client.der'

135

]

136

137

for cert_file in cert_files:

138

if os.path.exists(cert_file):

139

load_certificate_file(cert_file)

140

print("-" * 50)

141

```

142

143

### Private Key File Parsing

144

145

```python

146

from oscrypto.keys import parse_private

147

from oscrypto.errors import AsymmetricKeyError

148

import getpass

149

150

def load_private_key_file(file_path: str, password: str = None):

151

"""Load and parse a private key file with optional password."""

152

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

153

key_data = f.read()

154

155

# Convert password to bytes if provided

156

password_bytes = password.encode('utf-8') if password else None

157

158

try:

159

# First try without password

160

private_key = parse_private(key_data)

161

print(f"Private key loaded from: {file_path} (unencrypted)")

162

163

except (ValueError, AsymmetricKeyError) as e:

164

if "password" in str(e).lower() or "encrypted" in str(e).lower():

165

# Key is encrypted, prompt for password if not provided

166

if not password_bytes:

167

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

168

password_bytes = password.encode('utf-8')

169

170

try:

171

from oscrypto.asymmetric import load_private_key

172

private_key = load_private_key(key_data, password_bytes)

173

print(f"Private key loaded from: {file_path} (encrypted)")

174

except Exception as e2:

175

print(f"Error loading encrypted private key: {e2}")

176

return None

177

else:

178

print(f"Error parsing private key: {e}")

179

return None

180

181

# Display key information

182

print(f"Algorithm: {private_key.algorithm}")

183

if hasattr(private_key, 'bit_size'):

184

print(f"Key Size: {private_key.bit_size} bits")

185

if hasattr(private_key, 'curve'):

186

print(f"Curve: {private_key.curve}")

187

188

return private_key

189

190

# Example usage

191

key_files = [

192

'private_key.pem',

193

'encrypted_key.p8',

194

'rsa_key.der'

195

]

196

197

for key_file in key_files:

198

if os.path.exists(key_file):

199

load_private_key_file(key_file)

200

print("-" * 50)

201

```

202

203

### PKCS#12 Bundle Processing

204

205

```python

206

from oscrypto.keys import parse_pkcs12

207

from oscrypto.errors import AsymmetricKeyError

208

import getpass

209

210

def process_pkcs12_bundle(file_path: str, password: str = None):

211

"""Process a PKCS#12 bundle file."""

212

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

213

p12_data = f.read()

214

215

# Prompt for password if not provided

216

if not password:

217

password = getpass.getpass(f"Enter password for {file_path}: ")

218

219

password_bytes = password.encode('utf-8') if password else None

220

221

try:

222

certificate, private_key, intermediates = parse_pkcs12(p12_data, password_bytes)

223

224

print(f"PKCS#12 bundle processed: {file_path}")

225

print(f"End-entity certificate: {certificate.subject}")

226

print(f"Private key algorithm: {private_key.algorithm}")

227

print(f"Intermediate certificates: {len(intermediates)}")

228

229

# Display certificate chain

230

print("\nCertificate Chain:")

231

print(f"1. {certificate.subject} (end-entity)")

232

233

for i, intermediate in enumerate(intermediates, 2):

234

print(f"{i}. {intermediate.subject} (intermediate)")

235

236

# Verify private key matches certificate

237

cert_public_key = certificate.public_key

238

private_public_key = private_key.public_key

239

240

# Simple check - compare key algorithms and sizes

241

keys_match = (cert_public_key.algorithm == private_public_key.algorithm)

242

if hasattr(cert_public_key, 'bit_size') and hasattr(private_public_key, 'bit_size'):

243

keys_match = keys_match and (cert_public_key.bit_size == private_public_key.bit_size)

244

245

print(f"\nPrivate key matches certificate: {keys_match}")

246

247

return certificate, private_key, intermediates

248

249

except AsymmetricKeyError as e:

250

print(f"Error processing PKCS#12 bundle: {e}")

251

return None, None, []

252

253

# Example usage

254

p12_files = [

255

'client.p12',

256

'server.pfx',

257

'identity.p12'

258

]

259

260

for p12_file in p12_files:

261

if os.path.exists(p12_file):

262

process_pkcs12_bundle(p12_file)

263

print("=" * 60)

264

```

265

266

### Multi-Format Key Detection

267

268

```python

269

from oscrypto.keys import parse_certificate, parse_private, parse_public, parse_pkcs12

270

from oscrypto.errors import AsymmetricKeyError

271

272

def identify_crypto_file(file_path: str):

273

"""Identify the type and contents of a cryptographic file."""

274

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

275

data = f.read()

276

277

print(f"Analyzing file: {file_path}")

278

print(f"File size: {len(data)} bytes")

279

280

# Check if it's text (PEM) or binary (DER/P12)

281

is_text = all(byte < 128 for byte in data[:100])

282

print(f"Format: {'PEM/Text' if is_text else 'Binary (DER/P12)'}")

283

284

if is_text:

285

# Look for PEM markers

286

data_str = data.decode('utf-8', errors='ignore')

287

pem_types = []

288

289

if '-----BEGIN CERTIFICATE-----' in data_str:

290

pem_types.append('Certificate')

291

if '-----BEGIN PRIVATE KEY-----' in data_str:

292

pem_types.append('PKCS#8 Private Key')

293

if '-----BEGIN RSA PRIVATE KEY-----' in data_str:

294

pem_types.append('PKCS#1 RSA Private Key')

295

if '-----BEGIN EC PRIVATE KEY-----' in data_str:

296

pem_types.append('SEC1 EC Private Key')

297

if '-----BEGIN PUBLIC KEY-----' in data_str:

298

pem_types.append('Public Key')

299

300

if pem_types:

301

print(f"PEM content types: {', '.join(pem_types)}")

302

303

# Try parsing as different types

304

results = {}

305

306

# Try certificate

307

try:

308

cert = parse_certificate(data)

309

results['certificate'] = f"X.509 Certificate - Subject: {cert.subject}"

310

except:

311

pass

312

313

# Try private key

314

try:

315

key = parse_private(data)

316

results['private_key'] = f"Private Key - Algorithm: {key.algorithm}"

317

if hasattr(key, 'bit_size'):

318

results['private_key'] += f", {key.bit_size} bits"

319

except:

320

pass

321

322

# Try public key

323

try:

324

pub_key = parse_public(data)

325

results['public_key'] = f"Public Key - Algorithm: {pub_key.algorithm}"

326

if hasattr(pub_key, 'bit_size'):

327

results['public_key'] += f", {pub_key.bit_size} bits"

328

except:

329

pass

330

331

# Try PKCS#12 (typically requires password, so this might fail)

332

try:

333

cert, key, intermediates = parse_pkcs12(data, None)

334

results['pkcs12'] = f"PKCS#12 Bundle - Cert: {cert.subject}, Key: {key.algorithm}, Intermediates: {len(intermediates)}"

335

except:

336

# Try with empty password

337

try:

338

cert, key, intermediates = parse_pkcs12(data, b'')

339

results['pkcs12'] = f"PKCS#12 Bundle (empty password) - Cert: {cert.subject}, Key: {key.algorithm}, Intermediates: {len(intermediates)}"

340

except:

341

pass

342

343

if results:

344

print("Successful parses:")

345

for parse_type, description in results.items():

346

print(f" {parse_type}: {description}")

347

else:

348

print("Could not parse as any recognized cryptographic format")

349

350

print()

351

352

# Example usage - analyze all crypto files in directory

353

import glob

354

355

crypto_extensions = ['*.pem', '*.crt', '*.cer', '*.der', '*.key', '*.p8', '*.p12', '*.pfx']

356

crypto_files = []

357

358

for extension in crypto_extensions:

359

crypto_files.extend(glob.glob(extension))

360

361

for crypto_file in crypto_files:

362

identify_crypto_file(crypto_file)

363

```

364

365

### Key Conversion Utility

366

367

```python

368

from oscrypto.keys import parse_private, parse_certificate

369

from oscrypto.asymmetric import dump_private_key, dump_certificate, dump_public_key

370

import base64

371

372

def convert_key_format(input_file: str, output_file: str, output_format: str = 'pem'):

373

"""Convert cryptographic files between formats."""

374

375

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

376

input_data = f.read()

377

378

try:

379

# Try parsing as private key

380

private_key = parse_private(input_data)

381

382

# Export in requested format

383

if output_format.lower() == 'der':

384

output_data = dump_private_key(private_key)

385

else: # PEM

386

der_data = dump_private_key(private_key)

387

pem_data = base64.b64encode(der_data).decode('ascii')

388

# Add PEM wrapper

389

output_data = (

390

"-----BEGIN PRIVATE KEY-----\n" +

391

'\n'.join(pem_data[i:i+64] for i in range(0, len(pem_data), 64)) +

392

"\n-----END PRIVATE KEY-----\n"

393

).encode('ascii')

394

395

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

396

f.write(output_data)

397

398

print(f"Converted private key from {input_file} to {output_file} ({output_format.upper()})")

399

return

400

401

except:

402

pass

403

404

try:

405

# Try parsing as certificate

406

certificate = parse_certificate(input_data)

407

408

# Export in requested format

409

if output_format.lower() == 'der':

410

output_data = dump_certificate(certificate)

411

else: # PEM

412

der_data = dump_certificate(certificate)

413

pem_data = base64.b64encode(der_data).decode('ascii')

414

# Add PEM wrapper

415

output_data = (

416

"-----BEGIN CERTIFICATE-----\n" +

417

'\n'.join(pem_data[i:i+64] for i in range(0, len(pem_data), 64)) +

418

"\n-----END CERTIFICATE-----\n"

419

).encode('ascii')

420

421

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

422

f.write(output_data)

423

424

print(f"Converted certificate from {input_file} to {output_file} ({output_format.upper()})")

425

return

426

427

except Exception as e:

428

print(f"Error converting {input_file}: {e}")

429

430

# Example usage

431

conversions = [

432

('server.crt', 'server.der', 'der'),

433

('client.der', 'client.pem', 'pem'),

434

('private.p8', 'private.pem', 'pem')

435

]

436

437

for input_file, output_file, format_type in conversions:

438

if os.path.exists(input_file):

439

convert_key_format(input_file, output_file, format_type)

440

```