or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

crypto-utilities.mddisplay-ui.mderror-handling.mdindex.mdmain-config.mdplugin-development.mdutility-functions.md

crypto-utilities.mddocs/

0

# Cryptographic Utilities

1

2

Certificate and key generation, management, and validation functions for handling SSL/TLS certificates, private keys, and certificate signing requests.

3

4

## Capabilities

5

6

### Key Generation

7

8

Generate private keys for certificate signing requests and SSL/TLS certificates.

9

10

```python { .api }

11

def generate_key(key_size: int, key_dir: Optional[str], key_type: str = "rsa",

12

elliptic_curve: str = "secp256r1", keyname: str = "key-certbot.pem",

13

strict_permissions: bool = True) -> util.Key:

14

"""

15

Initialize and save a private key in PEM format.

16

17

Args:

18

key_size: Key size in bits if key type is RSA

19

key_dir: Optional directory to save key file

20

key_type: Key type ("rsa" or "ecdsa")

21

elliptic_curve: Name of elliptic curve if key type is ECDSA

22

keyname: Filename for the key (may be modified if file exists)

23

strict_permissions: If true, enforce 0700 permissions on key_dir

24

25

Returns:

26

Key object containing file path and PEM data

27

28

Raises:

29

ValueError: If unable to generate key with given parameters

30

"""

31

32

def make_key(bits: int = 2048, key_type: str = "rsa",

33

elliptic_curve: Optional[str] = None) -> bytes:

34

"""

35

Generate a private key and return PEM-encoded bytes.

36

37

Args:

38

bits: Key size in bits for RSA keys (minimum 2048)

39

key_type: Type of key to generate ("rsa" or "ecdsa")

40

elliptic_curve: Elliptic curve name for ECDSA keys

41

42

Returns:

43

PEM-encoded private key as bytes

44

45

Raises:

46

ValueError: If key generation parameters are invalid

47

"""

48

49

def valid_privkey(privkey: Union[str, bytes]) -> bool:

50

"""

51

Check if private key is valid and loadable.

52

53

Args:

54

privkey: Private key file contents in PEM format

55

56

Returns:

57

True if private key is valid and can be loaded

58

"""

59

```

60

61

Usage examples:

62

63

```python

64

from certbot import crypto_util

65

66

# Generate RSA key

67

rsa_key = crypto_util.generate_key(

68

key_size=2048,

69

key_dir='/etc/letsencrypt/keys',

70

key_type='rsa'

71

)

72

73

# Generate ECDSA key

74

ecdsa_key = crypto_util.generate_key(

75

key_size=256,

76

key_dir='/etc/letsencrypt/keys',

77

key_type='ecdsa',

78

elliptic_curve='secp256r1'

79

)

80

81

# Generate key without saving to file

82

key_pem = crypto_util.make_key(bits=2048, key_type='rsa')

83

```

84

85

### Certificate Signing Request Generation

86

87

Create certificate signing requests (CSRs) for obtaining certificates from ACME CAs.

88

89

```python { .api }

90

def generate_csr(privkey: util.Key, names: Union[list[str], set[str]],

91

path: Optional[str], must_staple: bool = False,

92

strict_permissions: bool = True) -> util.CSR:

93

94

def make_csr(private_key_pem: bytes, domains: list[str],

95

must_staple: bool = False) -> bytes:

96

"""

97

Generate a CSR and return DER-encoded bytes.

98

99

Args:

100

private_key_pem: PEM-encoded private key

101

domains: List of domain names for the certificate

102

must_staple: Whether to include OCSP Must-Staple extension

103

104

Returns:

105

DER-encoded CSR as bytes

106

"""

107

108

def valid_csr(csr: bytes) -> bool:

109

"""

110

Validate CSR with correct self-signed signature.

111

112

Args:

113

csr: CSR in PEM format

114

115

Returns:

116

True if CSR is valid with correct signature

117

"""

118

119

def csr_matches_pubkey(csr: bytes, privkey: bytes) -> bool:

120

"""

121

Check if private key corresponds to the CSR's public key.

122

123

Args:

124

csr: CSR in PEM format

125

privkey: Private key file contents in PEM format

126

127

Returns:

128

True if private key matches CSR public key

129

"""

130

131

def import_csr_file(csrfile: str, data: bytes) -> tuple[acme_crypto_util.Format, util.CSR, list[str]]:

132

"""

133

Import a CSR file which can be either PEM or DER format.

134

135

Args:

136

csrfile: CSR filename

137

data: Contents of the CSR file

138

139

Returns:

140

Tuple of (Format.PEM, CSR object, list of domains requested)

141

142

Raises:

143

errors.Error: If CSR file cannot be parsed

144

"""

145

```

146

147

The generate_csr function creates certificate signing requests for ACME certificate requests:

148

149

```python { .api }

150

def generate_csr(privkey: util.Key, names: Union[list[str], set[str]],

151

path: Optional[str], must_staple: bool = False,

152

strict_permissions: bool = True) -> util.CSR:

153

"""

154

Generate a certificate signing request.

155

156

Args:

157

privkey: Private key to sign the CSR

158

names: Domain names to include in the CSR (first is CN, rest are SANs)

159

path: Optional path to save CSR file

160

must_staple: Whether to include OCSP Must-Staple extension

161

strict_permissions: If true, enforce 0755 permissions on path directory

162

163

Returns:

164

CSR object containing file path, data, and format

165

166

Raises:

167

ValueError: If names list is empty or invalid

168

"""

169

170

def make_csr(private_key_pem: bytes, domains: list[str],

171

must_staple: bool = False) -> bytes:

172

"""

173

Generate a CSR and return DER-encoded bytes.

174

175

Args:

176

private_key_pem: PEM-encoded private key

177

domains: List of domain names for the certificate

178

must_staple: Whether to include OCSP Must-Staple extension

179

180

Returns:

181

DER-encoded CSR as bytes

182

"""

183

```

184

185

Usage examples:

186

187

```python

188

from certbot import crypto_util, util

189

190

# Generate key first

191

key = crypto_util.generate_key(2048, '/etc/letsencrypt/keys')

192

193

# Generate CSR for multiple domains

194

domains = ['example.com', 'www.example.com', 'api.example.com']

195

csr = crypto_util.generate_csr(

196

privkey=key,

197

names=domains,

198

path='/etc/letsencrypt/csr/example.csr',

199

must_staple=True

200

)

201

202

# Generate CSR without saving to file

203

csr_data = crypto_util.make_csr(key.pem, domains, must_staple=False)

204

```

205

206

### Certificate Information

207

208

Extract information and validate certificates.

209

210

```python { .api }

211

def notAfter(cert_path: str) -> datetime:

212

"""

213

Get certificate expiration date.

214

215

Args:

216

cert_path: Path to certificate file

217

218

Returns:

219

Certificate expiration datetime

220

221

Raises:

222

errors.Error: If certificate cannot be read

223

"""

224

225

def notBefore(cert_path: str) -> datetime:

226

"""

227

Get certificate valid from date.

228

229

Args:

230

cert_path: Path to certificate file

231

232

Returns:

233

Certificate valid from datetime

234

235

Raises:

236

errors.Error: If certificate cannot be read

237

"""

238

239

def cert_from_chain(chain_path: str) -> x509.Certificate:

240

"""

241

Extract the first certificate from a certificate chain file.

242

243

Args:

244

chain_path: Path to certificate chain file

245

246

Returns:

247

X.509 certificate object

248

249

Raises:

250

errors.Error: If chain file cannot be read or parsed

251

"""

252

253

def get_sans_from_cert(cert_path: str, typ: type = x509.DNSName) -> list[str]:

254

"""

255

Get Subject Alternative Names from certificate.

256

257

Args:

258

cert_path: Path to certificate file

259

typ: Type of SAN to extract (default: DNS names)

260

261

Returns:

262

List of SAN values

263

"""

264

```

265

266

Usage examples:

267

268

```python

269

from certbot import crypto_util

270

from datetime import datetime, timezone

271

272

# Check certificate expiration

273

cert_path = '/etc/letsencrypt/live/example.com/cert.pem'

274

expiry = crypto_util.notAfter(cert_path)

275

if expiry < datetime.now(timezone.utc):

276

print("Certificate has expired")

277

278

# Get certificate validity period

279

valid_from = crypto_util.notBefore(cert_path)

280

valid_until = crypto_util.notAfter(cert_path)

281

282

# Extract certificate from chain

283

chain_path = '/etc/letsencrypt/live/example.com/chain.pem'

284

cert = crypto_util.cert_from_chain(chain_path)

285

286

# Get domain names from certificate

287

domains = crypto_util.get_sans_from_cert(cert_path)

288

print(f"Certificate covers domains: {domains}")

289

```

290

291

### Certificate Validation

292

293

Validate certificates and certificate chains.

294

295

```python { .api }

296

def valid_privkey(privkey_path: str) -> bool:

297

"""

298

Check if private key is valid and loadable.

299

300

Args:

301

privkey_path: Path to private key file

302

303

Returns:

304

True if private key is valid

305

"""

306

307

def verify_cert_matches_priv_key(cert_path: str, key_path: str) -> bool:

308

"""

309

Verify that certificate matches private key.

310

311

Args:

312

cert_path: Path to certificate file

313

key_path: Path to private key file

314

315

Returns:

316

True if certificate and key match

317

318

Raises:

319

errors.Error: If files cannot be read

320

"""

321

322

def cert_chain_matches(cert_path: str, chain_path: str) -> bool:

323

"""

324

Verify that certificate chain is valid for certificate.

325

326

Args:

327

cert_path: Path to certificate file

328

chain_path: Path to certificate chain file

329

330

Returns:

331

True if chain is valid for certificate

332

"""

333

334

def verify_renewable_cert(renewable_cert: interfaces.RenewableCert) -> None:

335

"""

336

Comprehensive verification of a renewable certificate.

337

338

Checks signature verification, fullchain integrity, and key matching.

339

340

Args:

341

renewable_cert: Certificate to verify

342

343

Raises:

344

errors.Error: If verification fails

345

"""

346

347

def verify_renewable_cert_sig(renewable_cert: interfaces.RenewableCert) -> None:

348

"""

349

Verify the signature of a RenewableCert object.

350

351

Args:

352

renewable_cert: Certificate to verify

353

354

Raises:

355

errors.Error: If signature verification fails

356

"""

357

358

def verify_fullchain(renewable_cert: interfaces.RenewableCert) -> None:

359

"""

360

Verify that fullchain is cert concatenated with chain.

361

362

Args:

363

renewable_cert: Certificate to verify

364

365

Raises:

366

errors.Error: If cert and chain do not combine to fullchain

367

"""

368

369

def get_names_from_cert(cert: bytes, typ: Union[acme_crypto_util.Format, int] = acme_crypto_util.Format.PEM) -> list[str]:

370

"""

371

Get all domain names from a certificate including CN.

372

373

Args:

374

cert: Certificate in encoded format

375

typ: Format of the cert bytes (PEM or DER)

376

377

Returns:

378

List of domain names from certificate

379

"""

380

381

def get_names_from_req(csr: bytes, typ: Union[acme_crypto_util.Format, int] = acme_crypto_util.Format.PEM) -> list[str]:

382

"""

383

Get domain names from a CSR including CN.

384

385

Args:

386

csr: CSR in encoded format

387

typ: Format of the CSR bytes (PEM or DER)

388

389

Returns:

390

List of domain names from CSR

391

"""

392

393

def sha256sum(filename: str) -> str:

394

"""

395

Compute SHA256 hash of a file.

396

397

Args:

398

filename: Path to file to hash

399

400

Returns:

401

SHA256 digest in hexadecimal format

402

"""

403

404

def cert_and_chain_from_fullchain(fullchain_pem: str) -> tuple[str, str]:

405

"""

406

Split fullchain PEM into separate cert and chain PEMs.

407

408

Args:

409

fullchain_pem: Concatenated certificate + chain

410

411

Returns:

412

Tuple of (cert_pem, chain_pem)

413

414

Raises:

415

errors.Error: If less than 2 certificates in chain

416

"""

417

418

def get_serial_from_cert(cert_path: str) -> int:

419

"""

420

Get certificate serial number.

421

422

Args:

423

cert_path: Path to certificate file

424

425

Returns:

426

Certificate serial number

427

"""

428

429

def find_chain_with_issuer(fullchains: list[str], issuer_cn: str,

430

warn_on_no_match: bool = False) -> str:

431

"""

432

Find certificate chain with matching issuer common name.

433

434

Args:

435

fullchains: List of fullchains in PEM format

436

issuer_cn: Exact Subject Common Name to match

437

warn_on_no_match: Whether to warn if no chain matches

438

439

Returns:

440

Best-matching fullchain or first if none match

441

"""

442

```

443

444

Usage examples:

445

446

```python

447

from certbot import crypto_util

448

449

cert_path = '/etc/letsencrypt/live/example.com/cert.pem'

450

key_path = '/etc/letsencrypt/live/example.com/privkey.pem'

451

chain_path = '/etc/letsencrypt/live/example.com/chain.pem'

452

453

# Validate private key

454

if not crypto_util.valid_privkey(key_path):

455

print("Private key is invalid")

456

457

# Verify certificate matches private key

458

if crypto_util.verify_cert_matches_priv_key(cert_path, key_path):

459

print("Certificate and private key match")

460

461

# Verify certificate chain

462

if crypto_util.cert_chain_matches(cert_path, chain_path):

463

print("Certificate chain is valid")

464

```

465

466

### OCSP Support

467

468

Check certificate revocation status using OCSP.

469

470

```python { .api }

471

class RevocationChecker:

472

"""OCSP revocation checking functionality."""

473

474

def ocsp_revoked(self, cert: RenewableCert) -> bool:

475

"""

476

Get revocation status for a certificate.

477

478

Args:

479

cert: Certificate object to check

480

481

Returns:

482

True if revoked; False if valid or check failed

483

"""

484

485

def ocsp_revoked_by_paths(self, cert_path: str, chain_path: str,

486

timeout: int = 10) -> bool:

487

"""

488

Check revocation status using file paths.

489

490

Args:

491

cert_path: Path to certificate file

492

chain_path: Path to certificate chain file

493

timeout: Timeout in seconds for OCSP query

494

495

Returns:

496

True if revoked; False if valid or check failed

497

"""

498

```

499

500

Usage example:

501

502

```python

503

from certbot import ocsp

504

505

# Create revocation checker

506

checker = ocsp.RevocationChecker()

507

508

# Check revocation by file paths

509

cert_path = '/etc/letsencrypt/live/example.com/cert.pem'

510

chain_path = '/etc/letsencrypt/live/example.com/chain.pem'

511

512

if checker.ocsp_revoked_by_paths(cert_path, chain_path):

513

print("Certificate has been revoked")

514

else:

515

print("Certificate is valid (not revoked)")

516

```

517

518

## Types

519

520

```python { .api }

521

class Key(NamedTuple):

522

"""Container for private key data."""

523

file: Optional[str] # Path to key file (None if not saved)

524

pem: bytes # PEM-encoded key data

525

526

class CSR(NamedTuple):

527

"""Container for certificate signing request data."""

528

file: Optional[str] # Path to CSR file (None if not saved)

529

data: bytes # CSR data (PEM or DER encoded)

530

form: str # Format of data ("pem" or "der")

531

```