CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-com-squareup-okhttp3--okhttp-tls

OkHttp Transport Layer Security (TLS) library providing approachable APIs for using TLS, including certificate handling, certificate authorities, and client authentication

Pending
Overview
Eval results
Files

pem-utilities.mddocs/

PEM Utilities

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.

Capabilities

Certificate PEM Decoding

Parse X.509 certificates from PEM-encoded strings.

/**
 * Extension function to decode a PEM-encoded certificate string into an X509Certificate
 * @receiver String containing PEM-encoded certificate
 * @return X509Certificate parsed from the PEM data
 * @throws IllegalArgumentException if parsing fails or certificate is invalid
 */
fun String.decodeCertificatePem(): X509Certificate

Usage Example:

val pemCertificate = """
    -----BEGIN CERTIFICATE-----
    MIIBYTCCAQegAwIBAgIBKjAKBggqhkjOPQQDAjApMRQwEgYDVQQLEwtlbmdpbmVl
    cmluZzERMA8GA1UEAxMIY2FzaC5hcHAwHhcNNzAwMTAxMDAwMDA1WhcNNzAwMTAx
    MDAwMDEwWjApMRQwEgYDVQQLEwtlbmdpbmVlcmluZzERMA8GA1UEAxMIY2FzaC5h
    cHAwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASda8ChkQXxGELnrV/oBnIAx3dD
    ocUOJfdz4pOJTP6dVQB9U3UBiW5uSX/MoOD0LL5zG3bVyL3Y6pDwKuYvfLNhoyAw
    HjAcBgNVHREBAf8EEjAQhwQBAQEBgghjYXNoLmFwcDAKBggqhkjOPQQDAgNIADBF
    AiAyHHg1N6YDDQiY920+cnI5XSZwEGhAtb9PYWO8bLmkcQIhAI2CfEZf3V/obmdT
    yyaoEufLKVXhrTQhRfodTeigi4RX
    -----END CERTIFICATE-----
""".trimIndent()

val certificate = pemCertificate.decodeCertificatePem()
println("Subject: ${certificate.subjectX500Principal.name}")
println("Issuer: ${certificate.issuerX500Principal.name}")

Certificate PEM Encoding

Encode X.509 certificates to PEM format strings.

/**
 * Extension function to encode an X509Certificate as a PEM-formatted string
 * @receiver X509Certificate to encode
 * @return String containing the certificate in PEM format with proper headers
 */
fun X509Certificate.certificatePem(): String

Usage Example:

// Create or load a certificate
val heldCert = HeldCertificate.Builder()
    .commonName("example.com")
    .build()

// Export to PEM format
val pemString = heldCert.certificate.certificatePem()
println(pemString)

// Output format:
// -----BEGIN CERTIFICATE-----
// MIIBSjCB8aADAgECAgEBMAoGCCqGSM49BAMCMC8xLTArBgNVBAMTJDJiYWY3NzVl
// LWE4MzUtNDM5ZS1hYWE2LTgzNmNiNDlmMGM3MTAeFw0xODA3MTMxMjA0MzJaFw0x
// ODA3MTQxMjA0MzJaMC8xLTArBgNVBAMTJDJiYWY3NzVlLWE4MzUtNDM5ZS1hYWE2
// LTgzNmNiNDlmMGM3MTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABDmlOiZ3dxA2
// zw1KwqGNsKVUZbkUVj5cxV1jDbSTvTlOjSj6LR0Ovys9RFdrjcbbMLWvSvMQgHch
// k8Q50c6Kb34wCgYIKoZIzj0EAwIDSAAwRQIhAJkXiCbIR3zxuH5SQR5PEAPJ+ntg
// msOSMaAKwAePESf+AiBlxbEu6YpHt1ZJoAhMAv6raYnwSp/A94eJGlJynQ0igQ==
// -----END CERTIFICATE-----

// Save to file
File("certificate.pem").writeText(pemString)

Common Usage Patterns

Certificate Chain Export

Export a complete certificate chain in PEM format.

// Create certificate chain
val rootCa = HeldCertificate.Builder()
    .certificateAuthority(1)
    .commonName("Root CA")
    .build()

val intermediateCa = HeldCertificate.Builder()
    .certificateAuthority(0)
    .commonName("Intermediate CA")
    .signedBy(rootCa)
    .build()

val serverCert = HeldCertificate.Builder()
    .commonName("server.example.com")
    .signedBy(intermediateCa)
    .build()

// Export chain as concatenated PEM
val chainPem = buildString {
    append(serverCert.certificate.certificatePem())
    append("\n")
    append(intermediateCa.certificate.certificatePem())
    append("\n") 
    append(rootCa.certificate.certificatePem())
}

// Save certificate chain
File("cert-chain.pem").writeText(chainPem)

Certificate and Key Bundle

Export both certificate and private key together.

val heldCert = HeldCertificate.Builder()
    .addSubjectAlternativeName("api.example.com")
    .build()

// Create combined PEM bundle
val bundlePem = buildString {
    append(heldCert.certificatePem())
    append("\n")
    append(heldCert.privateKeyPkcs8Pem())
}

// This bundle can later be loaded with HeldCertificate.decode()
val reloaded = HeldCertificate.decode(bundlePem)

Certificate Validation

Load and validate certificates from PEM files.

// Load certificate from file
val pemContent = File("server.pem").readText()
val certificate = pemContent.decodeCertificatePem()

// Validate certificate properties
println("Subject: ${certificate.subjectX500Principal.name}")
println("Valid from: ${certificate.notBefore}")
println("Valid until: ${certificate.notAfter}")
println("Serial number: ${certificate.serialNumber}")

// Check if certificate is still valid
try {
    certificate.checkValidity()
    println("Certificate is valid")
} catch (e: Exception) {
    println("Certificate is not valid: ${e.message}")
}

// Extract subject alternative names
val sanExtension = certificate.subjectAlternativeNames
sanExtension?.forEach { san ->
    val type = san[0] as Int
    val value = san[1] as String
    when (type) {
        2 -> println("DNS: $value")
        7 -> println("IP: $value")
    }
}

Cross-Tool Compatibility

Export certificates for use with other TLS tools.

val serverCert = HeldCertificate.Builder()
    .addSubjectAlternativeName("nginx.example.com")
    .rsa2048() // Use RSA for broader compatibility
    .duration(365, TimeUnit.DAYS)
    .build()

// Export for nginx
val certPem = serverCert.certificatePem()
val keyPem = serverCert.privateKeyPkcs8Pem()

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

// Export for Apache (PKCS#1 format preferred)
val rsaKeyPem = serverCert.privateKeyPkcs1Pem()
File("apache.key").writeText(rsaKeyPem)

// Export for Java keystore import (requires openssl)
// openssl pkcs12 -export -in nginx.crt -inkey nginx.key -out keystore.p12

Certificate Authority Bundle

Create a CA bundle file with multiple trusted roots.

val caCertificates = listOf(
    loadCertFromResource("root-ca-1.crt"),
    loadCertFromResource("root-ca-2.crt"),
    loadCertFromResource("custom-ca.crt")
)

// Create CA bundle
val caBundlePem = buildString {
    caCertificates.forEach { cert ->
        append(cert.certificatePem())
        append("\n")
    }
}

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

// Use bundle with HandshakeCertificates
val trustedCerts = caBundlePem.split("-----END CERTIFICATE-----")
    .filter { it.contains("-----BEGIN CERTIFICATE-----") }
    .map { "$it-----END CERTIFICATE-----" }
    .map { it.decodeCertificatePem() }

val handshake = HandshakeCertificates.Builder()
    .apply {
        trustedCerts.forEach { addTrustedCertificate(it) }
    }
    .build()

Error Handling

Common Parsing Errors

Handle common certificate parsing issues.

fun safeParseCertificate(pemData: String): X509Certificate? {
    return try {
        pemData.decodeCertificatePem()
    } catch (e: IllegalArgumentException) {
        when {
            "failed to decode certificate" in e.message.orEmpty() -> {
                println("Invalid PEM format or corrupted certificate data")
                null
            }
            "string does not include a certificate" in e.message.orEmpty() -> {
                println("No certificate found in PEM data")
                null
            }
            else -> {
                println("Certificate parsing error: ${e.message}")
                null
            }
        }
    }
}

// Usage
val certificate = safeParseCertificate(pemData)
if (certificate != null) {
    println("Successfully parsed certificate: ${certificate.subjectX500Principal.name}")
} else {
    println("Failed to parse certificate")
}

Certificate Validation

Validate certificate chains and expiration.

fun validateCertificateChain(certificates: List<X509Certificate>): Boolean {
    if (certificates.isEmpty()) return false
    
    return try {
        // Check each certificate's validity period
        certificates.forEach { cert ->
            cert.checkValidity()
        }
        
        // Verify certificate chain (simplified)
        for (i in 0 until certificates.size - 1) {
            val cert = certificates[i]
            val issuer = certificates[i + 1]
            cert.verify(issuer.publicKey)
        }
        
        true
    } catch (e: Exception) {
        println("Certificate chain validation failed: ${e.message}")
        false
    }
}

Install with Tessl CLI

npx tessl i tessl/maven-com-squareup-okhttp3--okhttp-tls

docs

certificate-management.md

handshake-configuration.md

index.md

pem-utilities.md

tile.json