如何使用.crt、.key和.ca-bundle为Akka HTTP服务器配置HTTPS
没问题,我来帮你搞定这个Akka HTTP的HTTPS配置问题!你手里的.crt、.key和.ca-bundle这些PEM格式文件完全能用,不用纠结官方示例的PKCS12,有两种实用方案,我给你一步步讲清楚:
方案一:将PEM证书转换为PKCS12格式(适配Akka官方示例)
这是最直接的方式,用OpenSSL把现有证书打包成Akka文档里常用的PKCS12格式,操作很简单:
1. 用OpenSSL转换证书
打开终端,执行以下命令(替换成你的实际文件名):
openssl pkcs12 -export -in your-cert.crt -inkey your-private.key -certfile your-ca-bundle.crt -out keystore.p12 -name "my-akka-cert"
- 执行后会要求你设置一个密码,一定要记好,后续配置要用到
-name参数是证书别名,随便起个好记的就行
2. 修改Akka HTTP代码配置HTTPS
修改你的initializeWebServer方法,添加SSL上下文的创建逻辑,然后绑定HTTPS连接:
import java.io.FileInputStream import java.security.KeyStore import javax.net.ssl.{KeyManagerFactory, SSLContext, TrustManagerFactory} import akka.http.scaladsl.ConnectionContext def initializeWebServer(interface: String, port: Int) = { // 新增:创建SSL上下文 def createSslContext(): SSLContext = { val keystorePassword = "你刚才设置的密码".toCharArray val keyStore = KeyStore.getInstance("PKCS12") keyStore.load(new FileInputStream("keystore.p12"), keystorePassword) val keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm) keyManagerFactory.init(keyStore, keystorePassword) val trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm) trustManagerFactory.init(keyStore) val sslContext = SSLContext.getInstance("TLS") sslContext.init(keyManagerFactory.getKeyManagers, trustManagerFactory.getTrustManagers, null) sslContext } val route : Route = pathPrefix("secured") { authenticateOAuth2(realm = "secure site", checkAuthentication){ token => concat( get{ path("hello"){ complete("hello world") } } ) } } // 新增:创建HTTPS连接上下文 val httpsContext = ConnectionContext.https(createSslContext()) // 绑定的时候指定HTTPS上下文 val bindingFuture = Http().bindAndHandle(route, interface, port.toInt, connectionContext = httpsContext) CoordinatedShutdown(system).addJvmShutdownHook({ bindingFuture .flatMap(_.unbind()) }) } // 你的其他方法保持不变 def myUserPassAuthenticator(credentials: Credentials): Option[String] = credentials match { case p@Credentials.Provided(id) if p.verify("secret") => Some(id) case _ => None } def checkAuthentication(credentials: Credentials): Option[String] = credentials match { case p @ Credentials.Provided(token) if p.verify("secret") => Some(token) case _ => None }
方案二:直接加载PEM格式证书(无需转换)
如果你不想转换格式,可以直接加载PEM文件,不过需要引入BouncyCastle库来解析PEM内容:
1. 添加BouncyCastle依赖
在你的build.sbt里添加:
libraryDependencies += "org.bouncycastle" % "bcpkix-jdk15on" % "1.70"
2. 编写PEM加载和SSL上下文创建逻辑
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo import org.bouncycastle.cert.X509CertificateHolder import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter import org.bouncycastle.openssl.PEMParser import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter import java.io.FileReader import java.security.{KeyFactory, PrivateKey, Security, X509Certificate} import java.security.spec.PKCS8EncodedKeySpec import javax.net.ssl.{KeyManagerFactory, SSLContext, TrustManagerFactory} import akka.http.scaladsl.ConnectionContext // 注册BouncyCastle安全提供者 Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()) def initializeWebServer(interface: String, port: Int) = { // 新增:从PEM加载私钥 def loadPrivateKeyFromPem(path: String): PrivateKey = { val pemParser = new PEMParser(new FileReader(path)) val keyInfo = pemParser.readObject().asInstanceOf[PrivateKeyInfo] val keySpec = new PKCS8EncodedKeySpec(keyInfo.getEncoded) val keyFactory = KeyFactory.getInstance("RSA", "BC") keyFactory.generatePrivate(keySpec) } // 新增:从PEM加载证书 def loadCertificatesFromPem(path: String): List[X509Certificate] = { val pemParser = new PEMParser(new FileReader(path)) val converter = new JcaX509CertificateConverter().setProvider("BC") Iterator.continually(pemParser.readObject()) .takeWhile(_ != null) .collect { case holder: X509CertificateHolder => converter.getCertificate(holder) } .toList } // 新增:创建SSL上下文 def createSslContextFromPem(): SSLContext = { // 替换成你的实际文件路径 val privateKey = loadPrivateKeyFromPem("your-private.key") val certs = loadCertificatesFromPem("your-cert.crt") val caCerts = loadCertificatesFromPem("your-ca-bundle.crt") // 构建KeyStore val keyStore = KeyStore.getInstance(KeyStore.getDefaultType) keyStore.load(null) certs.zipWithIndex.foreach { case (cert, idx) => keyStore.setCertificateEntry(s"cert-$idx", cert) } // 如果私钥有密码,把第三个参数换成密码数组,比如"your-key-password".toCharArray keyStore.setKeyEntry("private-key", privateKey, "".toCharArray, certs.toArray) // 构建TrustStore val trustStore = KeyStore.getInstance(KeyStore.getDefaultType) trustStore.load(null) caCerts.zipWithIndex.foreach { case (caCert, idx) => trustStore.setCertificateEntry(s"ca-$idx", caCert) } // 初始化管理器 val keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm) keyManagerFactory.init(keyStore, "".toCharArray) // 私钥密码同上 val trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm) trustManagerFactory.init(trustStore) // 创建SSL上下文 val sslContext = SSLContext.getInstance("TLS") sslContext.init(keyManagerFactory.getKeyManagers, trustManagerFactory.getTrustManagers, null) sslContext } val route : Route = pathPrefix("secured") { authenticateOAuth2(realm = "secure site", checkAuthentication){ token => concat( get{ path("hello"){ complete("hello world") } } ) } } // 绑定HTTPS val httpsContext = ConnectionContext.https(createSslContextFromPem()) val bindingFuture = Http().bindAndHandle(route, interface, port.toInt, connectionContext = httpsContext) CoordinatedShutdown(system).addJvmShutdownHook({ bindingFuture .flatMap(_.unbind()) }) } // 你的其他方法保持不变 def myUserPassAuthenticator(credentials: Credentials): Option[String] = credentials match { case p@Credentials.Provided(id) if p.verify("secret") => Some(id) case _ => None } def checkAuthentication(credentials: Credentials): Option[String] = credentials match { case p @ Credentials.Provided(token) if p.verify("secret") => Some(token) case _ => None }
注意事项
- 确保你的私钥文件权限设置为
600(仅限当前用户读取),避免安全风险 - 证书文件要确保没有过期,且CA bundle包含所有必要的中间证书
- 如果使用方案二,注意BouncyCastle的版本要和你的Java版本兼容
内容的提问来源于stack exchange,提问作者pksimba
相关产品推荐
相关产品推荐

