You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用.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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 09:13:01