diff --git a/GENERATING_CERT.md b/GENERATING_CERT.md index d0595e0..51c3981 100644 --- a/GENERATING_CERT.md +++ b/GENERATING_CERT.md @@ -1,4 +1,11 @@ -# Generating a certificate +# Keystore +Kmail uses a PKCS12 keystore to store certificates. Given a certificate and key (e.g, from CloudFlare), a keystore can be generated using the following command: +```bash +openssl pkcs12 -export -inkey server.key -in server.pem -out server.p12 +``` +Configure the keystore to be used in [the configuration file](kmail.toml). + +## Self-Signed (outdated) To run a secure mail server, a certificate for the domain the mail server is handling is required. For this example, we wil generate a self-signed certificate. ``` openssl req -x509 -newkey rsa:4096 -keyout private.pem -out xgophers.crt -sha256 -days 365 -nodes diff --git a/mailserver/runner/src/main/kotlin/config.kt b/mailserver/runner/src/main/kotlin/config.kt index eeaf858..258fc9d 100644 --- a/mailserver/runner/src/main/kotlin/config.kt +++ b/mailserver/runner/src/main/kotlin/config.kt @@ -110,9 +110,7 @@ data class KmailConfig( } } - data class Security(val certificates: List) { - data class CertificateAndKey(val certificate: String, val key: String) - } + data class Security(val keystore: String, val password: String) data class Smtp( val submission: Submission, val transfer: Transfer diff --git a/mailserver/runner/src/main/kotlin/launcherJvm.kt b/mailserver/runner/src/main/kotlin/launcherJvm.kt index f0cabaa..2421ee9 100644 --- a/mailserver/runner/src/main/kotlin/launcherJvm.kt +++ b/mailserver/runner/src/main/kotlin/launcherJvm.kt @@ -3,8 +3,13 @@ package dev.sitar.kmail.runner import dev.sitar.kmail.utils.connection.TlsCapableConnectionFactory import dev.sitar.kmail.utils.server.TlsCapableServerSocketFactory import kotlinx.coroutines.coroutineScope +import mu.KotlinLogging + +private val logger = KotlinLogging.logger { } suspend fun main() { + logger.info { "Using JVM version: ${Runtime.version()}" } + dns() // System.setProperty("javax.net.debug", "all") diff --git a/mailserver/runner/src/main/kotlin/sslJvm.kt b/mailserver/runner/src/main/kotlin/sslJvm.kt index 475e43e..d66a804 100644 --- a/mailserver/runner/src/main/kotlin/sslJvm.kt +++ b/mailserver/runner/src/main/kotlin/sslJvm.kt @@ -2,6 +2,7 @@ package dev.sitar.kmail.runner import mu.KotlinLogging import java.io.FileInputStream +import java.io.FileOutputStream import java.io.InputStream import java.security.KeyFactory import java.security.KeyStore @@ -10,81 +11,29 @@ import java.security.cert.CertificateException import java.security.cert.CertificateFactory import java.security.cert.X509Certificate import java.security.spec.PKCS8EncodedKeySpec -import javax.net.ssl.KeyManagerFactory -import javax.net.ssl.SSLContext -import javax.net.ssl.TrustManagerFactory -import javax.net.ssl.X509TrustManager +import javax.net.ssl.* private val logger = KotlinLogging.logger { } -private fun InputStream.load(): Array { - logger.debug { "Loading certificates from input stream." } - val factory = CertificateFactory.getInstance("X.509") - - return buildList { - while (available() > 0) { - val cert = factory.generateCertificate(this@load) - add(cert) - logger.debug { "Generated a certificate." } - logger.trace { cert } - } - }.toTypedArray() -} - fun ssl(): Pair { - val keyStore = KeyStore.getInstance(KeyStore.getDefaultType()) - keyStore.load(null) - - Config.security.certificates.forEach { (certPath, key) -> - FileInputStream(certPath).use { certStream -> - certStream.load().apply { - forEachIndexed { i, cert -> - keyStore.setCertificateEntry(certPath, cert) - } - - keyStore.setKeyEntry("private", - KeyFactory.getInstance("RSA") - .generatePrivate(PKCS8EncodedKeySpec(FileInputStream(key).readAllBytes())), - null, - this - ) - } - } - } + val keyStore = KeyStore.getInstance("PKCS12") + keyStore.load(FileInputStream(Config.security.keystore), Config.security.password.toCharArray()) logger.debug { "Generating SSL context." } val ssl = SSLContext.getInstance("TLS") val keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()) - keyManagerFactory.init(keyStore, null) + keyManagerFactory.init(keyStore, Config.security.password.toCharArray()) + + keyStore.aliases().iterator().forEach { + logger.debug { "got certificate\n${keyStore.getCertificate(it)}"} + } val trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()) trustManagerFactory.init(keyStore) - val mine = trustManagerFactory.trustManagers.filterIsInstance().first() - val default = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()).also { it.init(null as KeyStore?) }.trustManagers.filterIsInstance().first() - - val trustManager = object: X509TrustManager { - override fun checkClientTrusted(chain: Array?, authType: String?) { - default.checkClientTrusted(chain, authType) - } - - override fun checkServerTrusted(chain: Array?, authType: String?) { - try { - mine.checkServerTrusted(chain, authType) - } catch (e: CertificateException) { - default.checkServerTrusted(chain, authType) - } - } - - override fun getAcceptedIssuers(): Array { - return default.acceptedIssuers - } - - } - - ssl.init(keyManagerFactory.keyManagers, arrayOf(trustManager), null) + ssl.init(keyManagerFactory.keyManagers, trustManagerFactory.trustManagers, null) logger.debug { "Generated SSL context." }