or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

certificate-management.mdhandshake-configuration.mdindex.mdpem-utilities.md

pem-utilities.mddocs/

0

# PEM Utilities

1

2

Utility functions for encoding and decoding X.509 certificates in PEM format, enabling easy certificate persistence, exchange, and interoperability with other TLS tools and systems.

3

4

## Capabilities

5

6

### Certificate PEM Decoding

7

8

Parse X.509 certificates from PEM-encoded strings.

9

10

```kotlin { .api }

11

/**

12

* Extension function to decode a PEM-encoded certificate string into an X509Certificate

13

* @receiver String containing PEM-encoded certificate

14

* @return X509Certificate parsed from the PEM data

15

* @throws IllegalArgumentException if parsing fails or certificate is invalid

16

*/

17

fun String.decodeCertificatePem(): X509Certificate

18

```

19

20

**Usage Example:**

21

22

```kotlin

23

val pemCertificate = """

24

-----BEGIN CERTIFICATE-----

25

MIIBYTCCAQegAwIBAgIBKjAKBggqhkjOPQQDAjApMRQwEgYDVQQLEwtlbmdpbmVl

26

cmluZzERMA8GA1UEAxMIY2FzaC5hcHAwHhcNNzAwMTAxMDAwMDA1WhcNNzAwMTAx

27

MDAwMDEwWjApMRQwEgYDVQQLEwtlbmdpbmVlcmluZzERMA8GA1UEAxMIY2FzaC5h

28

cHAwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASda8ChkQXxGELnrV/oBnIAx3dD

29

ocUOJfdz4pOJTP6dVQB9U3UBiW5uSX/MoOD0LL5zG3bVyL3Y6pDwKuYvfLNhoyAw

30

HjAcBgNVHREBAf8EEjAQhwQBAQEBgghjYXNoLmFwcDAKBggqhkjOPQQDAgNIADBF

31

AiAyHHg1N6YDDQiY920+cnI5XSZwEGhAtb9PYWO8bLmkcQIhAI2CfEZf3V/obmdT

32

yyaoEufLKVXhrTQhRfodTeigi4RX

33

-----END CERTIFICATE-----

34

""".trimIndent()

35

36

val certificate = pemCertificate.decodeCertificatePem()

37

println("Subject: ${certificate.subjectX500Principal.name}")

38

println("Issuer: ${certificate.issuerX500Principal.name}")

39

```

40

41

### Certificate PEM Encoding

42

43

Encode X.509 certificates to PEM format strings.

44

45

```kotlin { .api }

46

/**

47

* Extension function to encode an X509Certificate as a PEM-formatted string

48

* @receiver X509Certificate to encode

49

* @return String containing the certificate in PEM format with proper headers

50

*/

51

fun X509Certificate.certificatePem(): String

52

```

53

54

**Usage Example:**

55

56

```kotlin

57

// Create or load a certificate

58

val heldCert = HeldCertificate.Builder()

59

.commonName("example.com")

60

.build()

61

62

// Export to PEM format

63

val pemString = heldCert.certificate.certificatePem()

64

println(pemString)

65

66

// Output format:

67

// -----BEGIN CERTIFICATE-----

68

// MIIBSjCB8aADAgECAgEBMAoGCCqGSM49BAMCMC8xLTArBgNVBAMTJDJiYWY3NzVl

69

// LWE4MzUtNDM5ZS1hYWE2LTgzNmNiNDlmMGM3MTAeFw0xODA3MTMxMjA0MzJaFw0x

70

// ODA3MTQxMjA0MzJaMC8xLTArBgNVBAMTJDJiYWY3NzVlLWE4MzUtNDM5ZS1hYWE2

71

// LTgzNmNiNDlmMGM3MTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABDmlOiZ3dxA2

72

// zw1KwqGNsKVUZbkUVj5cxV1jDbSTvTlOjSj6LR0Ovys9RFdrjcbbMLWvSvMQgHch

73

// k8Q50c6Kb34wCgYIKoZIzj0EAwIDSAAwRQIhAJkXiCbIR3zxuH5SQR5PEAPJ+ntg

74

// msOSMaAKwAePESf+AiBlxbEu6YpHt1ZJoAhMAv6raYnwSp/A94eJGlJynQ0igQ==

75

// -----END CERTIFICATE-----

76

77

// Save to file

78

File("certificate.pem").writeText(pemString)

79

```

80

81

## Common Usage Patterns

82

83

### Certificate Chain Export

84

85

Export a complete certificate chain in PEM format.

86

87

```kotlin

88

// Create certificate chain

89

val rootCa = HeldCertificate.Builder()

90

.certificateAuthority(1)

91

.commonName("Root CA")

92

.build()

93

94

val intermediateCa = HeldCertificate.Builder()

95

.certificateAuthority(0)

96

.commonName("Intermediate CA")

97

.signedBy(rootCa)

98

.build()

99

100

val serverCert = HeldCertificate.Builder()

101

.commonName("server.example.com")

102

.signedBy(intermediateCa)

103

.build()

104

105

// Export chain as concatenated PEM

106

val chainPem = buildString {

107

append(serverCert.certificate.certificatePem())

108

append("\n")

109

append(intermediateCa.certificate.certificatePem())

110

append("\n")

111

append(rootCa.certificate.certificatePem())

112

}

113

114

// Save certificate chain

115

File("cert-chain.pem").writeText(chainPem)

116

```

117

118

### Certificate and Key Bundle

119

120

Export both certificate and private key together.

121

122

```kotlin

123

val heldCert = HeldCertificate.Builder()

124

.addSubjectAlternativeName("api.example.com")

125

.build()

126

127

// Create combined PEM bundle

128

val bundlePem = buildString {

129

append(heldCert.certificatePem())

130

append("\n")

131

append(heldCert.privateKeyPkcs8Pem())

132

}

133

134

// This bundle can later be loaded with HeldCertificate.decode()

135

val reloaded = HeldCertificate.decode(bundlePem)

136

```

137

138

### Certificate Validation

139

140

Load and validate certificates from PEM files.

141

142

```kotlin

143

// Load certificate from file

144

val pemContent = File("server.pem").readText()

145

val certificate = pemContent.decodeCertificatePem()

146

147

// Validate certificate properties

148

println("Subject: ${certificate.subjectX500Principal.name}")

149

println("Valid from: ${certificate.notBefore}")

150

println("Valid until: ${certificate.notAfter}")

151

println("Serial number: ${certificate.serialNumber}")

152

153

// Check if certificate is still valid

154

try {

155

certificate.checkValidity()

156

println("Certificate is valid")

157

} catch (e: Exception) {

158

println("Certificate is not valid: ${e.message}")

159

}

160

161

// Extract subject alternative names

162

val sanExtension = certificate.subjectAlternativeNames

163

sanExtension?.forEach { san ->

164

val type = san[0] as Int

165

val value = san[1] as String

166

when (type) {

167

2 -> println("DNS: $value")

168

7 -> println("IP: $value")

169

}

170

}

171

```

172

173

### Cross-Tool Compatibility

174

175

Export certificates for use with other TLS tools.

176

177

```kotlin

178

val serverCert = HeldCertificate.Builder()

179

.addSubjectAlternativeName("nginx.example.com")

180

.rsa2048() // Use RSA for broader compatibility

181

.duration(365, TimeUnit.DAYS)

182

.build()

183

184

// Export for nginx

185

val certPem = serverCert.certificatePem()

186

val keyPem = serverCert.privateKeyPkcs8Pem()

187

188

File("nginx.crt").writeText(certPem)

189

File("nginx.key").writeText(keyPem)

190

191

// Export for Apache (PKCS#1 format preferred)

192

val rsaKeyPem = serverCert.privateKeyPkcs1Pem()

193

File("apache.key").writeText(rsaKeyPem)

194

195

// Export for Java keystore import (requires openssl)

196

// openssl pkcs12 -export -in nginx.crt -inkey nginx.key -out keystore.p12

197

```

198

199

### Certificate Authority Bundle

200

201

Create a CA bundle file with multiple trusted roots.

202

203

```kotlin

204

val caCertificates = listOf(

205

loadCertFromResource("root-ca-1.crt"),

206

loadCertFromResource("root-ca-2.crt"),

207

loadCertFromResource("custom-ca.crt")

208

)

209

210

// Create CA bundle

211

val caBundlePem = buildString {

212

caCertificates.forEach { cert ->

213

append(cert.certificatePem())

214

append("\n")

215

}

216

}

217

218

File("ca-bundle.pem").writeText(caBundlePem)

219

220

// Use bundle with HandshakeCertificates

221

val trustedCerts = caBundlePem.split("-----END CERTIFICATE-----")

222

.filter { it.contains("-----BEGIN CERTIFICATE-----") }

223

.map { "$it-----END CERTIFICATE-----" }

224

.map { it.decodeCertificatePem() }

225

226

val handshake = HandshakeCertificates.Builder()

227

.apply {

228

trustedCerts.forEach { addTrustedCertificate(it) }

229

}

230

.build()

231

```

232

233

## Error Handling

234

235

### Common Parsing Errors

236

237

Handle common certificate parsing issues.

238

239

```kotlin

240

fun safeParseCertificate(pemData: String): X509Certificate? {

241

return try {

242

pemData.decodeCertificatePem()

243

} catch (e: IllegalArgumentException) {

244

when {

245

"failed to decode certificate" in e.message.orEmpty() -> {

246

println("Invalid PEM format or corrupted certificate data")

247

null

248

}

249

"string does not include a certificate" in e.message.orEmpty() -> {

250

println("No certificate found in PEM data")

251

null

252

}

253

else -> {

254

println("Certificate parsing error: ${e.message}")

255

null

256

}

257

}

258

}

259

}

260

261

// Usage

262

val certificate = safeParseCertificate(pemData)

263

if (certificate != null) {

264

println("Successfully parsed certificate: ${certificate.subjectX500Principal.name}")

265

} else {

266

println("Failed to parse certificate")

267

}

268

```

269

270

### Certificate Validation

271

272

Validate certificate chains and expiration.

273

274

```kotlin

275

fun validateCertificateChain(certificates: List<X509Certificate>): Boolean {

276

if (certificates.isEmpty()) return false

277

278

return try {

279

// Check each certificate's validity period

280

certificates.forEach { cert ->

281

cert.checkValidity()

282

}

283

284

// Verify certificate chain (simplified)

285

for (i in 0 until certificates.size - 1) {

286

val cert = certificates[i]

287

val issuer = certificates[i + 1]

288

cert.verify(issuer.publicKey)

289

}

290

291

true

292

} catch (e: Exception) {

293

println("Certificate chain validation failed: ${e.message}")

294

false

295

}

296

}

297

```