阿卡如何在https请求中设置pem证书

时间:2018-12-11 15:36:21

标签: scala https akka

我正在使用Akka(版本2.5.18)通过https将JSON字符串发送到特定服务器。我使用了 poolRouter (具有10个实例的平衡池)来创建参与者池,这些参与者将把JSON(从不同客户生成的)发送到单个服务器:

  val router: ActorRef = system.actorOf(
    FromConfig.props(Props(new SenderActor(configuration.getString("https://server.com"), this.self))),
    "poolRouter"
  )

项目规范说,还可以使用curl发送请求:

curl -X PUT --cert certificate.pem --key private.key -H 'Content-Type: application / json' -H 'cache-control: no-cache' -d '[{"id" : "test"}] 'https://server.com'

其中“ certificate.pem”是客户的tls证书,而“ private.key”是用于生成客户的CSR的私钥。

我使用的是平衡池,因为我将拥有大量的证书(每个客户一个),并且我需要同时发送请求。

我的方法是创建一个“ SenderActor”类,该类将由平衡池创建。每个参与者在收到带有“ customerId”的消息以及该客户生成的JSON数据后,都会发送一个https请求:

  override def receive: Receive = {
    case Data(customerId, jsonData) =>
      send(customerId(cid, jsonData))

每个SenderActor将基于使用customerId的路径读取证书(和私钥)。例如,customerId:“ cust1”将其证书和密钥存储在“ / home / test / cust1”中。这样,可以将相同的actor类用于所有客户。

根据documentation,我需要创建一个HttpsConnectionContext才能发送不同的请求:

def send(customerId: String, dataToSend): Future[HttpResponse] = {

    // Create the request
    val req = HttpRequest(
      PUT,
      uri = "https://server.com",
      entity = HttpEntity(`application/x-www-form-urlencoded` withCharset `UTF-8`, dataToSend),
      protocol = `HTTP/1.0`)

    val ctx: SSLContext = SSLContext.getInstance("TLS")

    val permissiveTrustManager: TrustManager = new X509TrustManager() {
      override def checkClientTrusted(chain: Array[X509Certificate], authType: String): Unit = {}
      override def checkServerTrusted(chain: Array[X509Certificate], authType: String): Unit = {}
      override def getAcceptedIssuers(): Array[X509Certificate] = Array.empty
    }


    ctx.init(Array.empty, Array(permissiveTrustManager), new SecureRandom())
    val httpsConnContext: HttpsConnectionContext = ConnectionContext.https(ctx)

    // Send the request
    Http(system).singleRequest(req, httpsConnContext)
}

我的问题是我对如何在请求中“设置证书和密钥”一无所知,以便服务器接受它们。

例如,我可以使用以下代码读取证书:

import java.util.Base64

val certificate: String => String = (customer: String) => IO {
Source.fromInputStream(getClass.getClassLoader
  .getResourceAsStream("/home/test/".concat(customer).concat("_cert.pem")))
  .getLines().mkString
}.unsafeRunSync()

val decodedCertificate = Base64.getDecoder.decode(certificate(customerId)
  .replaceAll(X509Factory.BEGIN_CERT, "").replaceAll(X509Factory.END_CERT, ""))
val cert: Certificate = CertificateFactory.getInstance("X.509")
  .generateCertificate(new ByteArrayInputStream(decodedCertificate))

但是我不知道如何在请求中“设置”此证书和私钥(受密码保护),以便服务器接受。

任何提示或帮助将不胜感激。

1 个答案:

答案 0 :(得分:1)

以下允许发出https请求并使用x.509证书中的私钥来标识自己。

以下库用于管理ssl配置和进行https调用:

将您的pem证书转换为here定义的pks12格式

openssl pkcs12 -export -out certificate.pfx -inkey privateKey.key -in certificate.crt

在您的application.conf中定义密钥存储。它仅支持pkcs12并且由于 步骤1是必需的。

ssl-config {
  keyManager {
    stores = [
      {
        type = "pkcs12"
        path = "/path/to/pkcs12/cetificate"
        password = changeme //the password is set when using openssl
      }
    ]
  }
}

使用特殊的akka​​特性DefaultSSLContextCreation

加载ssl配置
import akka.actor.ActorSystem
import akka.actor.ExtendedActorSystem
import akka.http.scaladsl.DefaultSSLContextCreation
import com.typesafe.sslconfig.akka.AkkaSSLConfig
import com.typesafe.sslconfig.ssl.SSLConfigFactory

class TlsProvider(val actorSystem: ActorSystem) extends DefaultSSLContextCreation {

  override protected def sslConfig: AkkaSSLConfig =
    throw new RuntimeException("Unsupported behaviour when creating new sslConfig")

  def httpsConnectionContext() = {
    val akkaSslConfig =
      new AkkaSSLConfig(system.asInstanceOf[ExtendedActorSystem], SSLConfigFactory.parse(system.settings.config))
    createClientHttpsContext(akkaSslConfig)
  }
}

创建一个https上下文并在http连接池中使用。

 Http(actorSystem).cachedHostConnectionPoolHttps[RequestContext](
            host = host,
            port = portValue,
            connectionContext = new TlsProvider(actorSystem).httpsConnectionContext()
          )

或将连接上下文设置为Http(actorSystem).singleRequest方法。

总而言之,我使用ssl-config库来管理证书,而不是自己以编程方式进行。通过在keyManager中定义ssl-config,在自定义httpsConnectionContext的帮助下完成的任何HTTP请求都将使用证书来标识呼叫者/客户端。

我专注于描述如何使用客户端证书建立https连接。省略了用于管理多个证书的任何动态行为。但我希望这段代码能够使您理解如何进行。