我正在使用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))
但是我不知道如何在请求中“设置”此证书和私钥(受密码保护),以便服务器接受。
任何提示或帮助将不胜感激。
答案 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
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连接。省略了用于管理多个证书的任何动态行为。但我希望这段代码能够使您理解如何进行。