1import java.security.KeyFactory
2import java.security.KeyStore
3import java.security.PrivateKey
4import java.security.cert.Certificate
5import java.security.cert.CertificateFactory
6import java.security.cert.X509Certificate
7import java.security.spec.PKCS8EncodedKeySpec
8import java.util.Base64
9import kotlin.io.path.Path
10import kotlin.io.path.inputStream
11import kotlin.io.path.outputStream
12import kotlin.io.path.readText
13
14fun readCertificate(path: String): Certificate =
15 Path(path).inputStream().use { CertificateFactory.getInstance("X.509").generateCertificate(it) }
16
17fun readPkcs8Key(path: String): PrivateKey {
18 val der = Base64.getMimeDecoder().decode(Path(path).readText().replace(Regex("-----[^-]+-----"), ""))
19 return KeyFactory.getInstance("EC").generatePrivate(PKCS8EncodedKeySpec(der))
20}
21
22fun writeBundle(path: String, alias: String, key: PrivateKey, chain: List<Certificate>, password: CharArray) {
23 val store = KeyStore.getInstance("PKCS12").apply { load(null, null) }
24 store.setKeyEntry(alias, key, password, chain.toTypedArray()) // leaf first; alias = friendly name
25 Path(path).outputStream().use { store.store(it, password) }
26}
27
28fun readBundle(path: String, password: CharArray): KeyStore =
29 KeyStore.getInstance("PKCS12").apply { Path(path).inputStream().use { load(it, password) } }
30
31fun main() {
32 val password = System.getenv("P12_PASSWORD").toCharArray()
33 writeBundle(
34 "bundle.p12", "api.example.com", readPkcs8Key("key.pem"),
35 listOf(readCertificate("leaf.pem"), readCertificate("intermediate.pem")), password,
36 )
37
38 val store = readBundle("bundle.p12", password)
39 for (alias in store.aliases().toList()) {
40 val key = store.getKey(alias, password)
41 val chain = store.getCertificateChain(alias).map { it as X509Certificate }
42 println("alias $alias: ${key.algorithm} key, chain ${chain.map { it.subjectX500Principal.name }}")
43 }
44 println("wrong password: ${runCatching { readBundle("bundle.p12", "wrong-password".toCharArray()) }.exceptionOrNull()?.message}")
45}
46