2

I have a .pem certificate and private key which I require for SSL. Doing so is trivial in Python:

import httpx
import ssl

sslcontext = ssl.create_default_context()
sslcontext.load_cert_chain(
    "./public-cert.pem",
    "./private-key.pem",
)

response = httpx.post(
    url,
    verify=sslcontext,
)

But I'd like do the same in Kotlin / Java. I've been going through the following article but I'm having issues handling the private key: https://developer.android.com/training/articles/security-ssl

I believe I must read the private key through a KeyFactory like such:

fun loadPrivateKey(path: Path): PrivateKey {
    val keyText = InputStreamReader(FileInputStream(path.toString())).use {
        it.readText()
            .replace("-----BEGIN PRIVATE KEY-----\n", "")
            .replace("-----END PRIVATE KEY-----", "")
    }
    val encoded = Base64.getDecoder().decode(keyText)
    return KeyFactory.getInstance("RSA")
        .generatePrivate(PKCS8EncodedKeySpec(encoded))
}

And pass it into the key store. However, decoding results in an error:

Illegal base64 character a

I would like to avoid handling it like this if possible: https://stackoverflow.com/a/8224863/13669284 I'm a little lost at this point and any guidance on this matter would be much appreciated.

kaega
  • 75
  • 6
  • 2
    FWIW note `CertificateFactory.getInstance("X.509")` handles either PEM or 'DER' (binary) (as a Stream) so you don't need to read and pre-decode the cert, unlike `KeyFactory` which only handles DER that is already read and decoded. – dave_thompson_085 Oct 22 '20 at 19:15

1 Answers1

2

For basic encoding no line feed character allowed: Using java.util.Base64 Basic – Uses “The Base64 Alphabet” as specified in Table 1 of RFC 4648 and RFC 2045 for encoding and decoding operation. – Encoding: not add any line feed (line separator) character. – Decoding: not contain characters outside the Base64 Alphabet.

So you can replace all the occurrences of "\n" in the key:

fun loadPrivateKey(path: Path): PrivateKey {
    val keyText = InputStreamReader(FileInputStream(path.toString())).use {
        it.readText()
            .replace("\n", "")
            .replace("-----BEGIN PRIVATE KEY-----", "")
            .replace("-----END PRIVATE KEY-----", "")
    }
    val encoded = Base64.getDecoder().decode(keyText)
    return KeyFactory.getInstance("RSA")
        .generatePrivate(PKCS8EncodedKeySpec(encoded))
}
maximus
  • 716
  • 7
  • 18