我的服务器正在使用TLSv1.2,并且需要客户端证书才能进行连接。我可以使用CURL将请求发送到服务器(此请求工作正常):
curl --data "SAMPLETEXT" https://myserver.com/webservice --insecure --key privkey.pem --cert certificate.cert
(是的,服务器具有自签名证书,并且需要
--insecure
标志;不,我无法解决此问题)。现在,我想创建客户端以从Scala代码发送请求。
MyClient
是包含所需密码和路径的对象。为此,我创建SSLContext
: private val keyStore = {
//Setting up BouncyCastle provider for message signing
Security.addProvider(new BouncyCastleProvider())
//Loading keystore from specified file
val clientStore = KeyStore.getInstance("JKS")
val inputStream = new FileInputStream(MyClient.keystore)
clientStore.load(inputStream, MyClient.keystorePassword.toCharArray)
inputStream.close()
clientStore
}
//Retrieving certificate and key
private val cert = keyStore.getCertificate(MyClient.keyAlias).asInstanceOf[X509Certificate]
private val key = keyStore.getKey(MyClient.keyAlias, MyClient.keystorePassword.toCharArray).asInstanceOf[PrivateKey]
//Creating SSL context
private val sslContext = {
val context = SSLContext.getInstance("TLS")
val tmf: TrustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm)
val kmf: KeyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm)
kmf.init(keyStore, MyClient.keystorePassword.toCharArray)
tmf.init(keyStore)
context.init(kmf.getKeyManagers, tmf.getTrustManagers, null)
context
}
然后使用它来构建客户端:
private val httpClient =
richHttpBuilder(HttpEndpoint(baseUri))
.hostConnectionLimit(1)
.tlsWithoutValidation()
.tls(sslContext, Some(MyClient.host))
.build()
但我仍然收到错误:
未来返回了一个类型异常:
com.twitter.finagle.ChannelWriteException,并显示以下消息:
com.twitter.finagle.SslHandshakeException:一般SSLEngine问题
在远程地址:
我做错了什么?
最佳答案
我花了一周的时间才意识到自己做错了什么。
选项.tlsWithoutValidation()
和.tls(sslContext, Some(MyClient.host))
不能同时使用,因为它们配置了构建器的相同属性(Transport.TLSClientEngine
)。
有三种解决方案。
使用正确的服务器证书。不幸的是,这是不适用的。
将服务器证书添加到密钥库。它将被标记为受信任,并且客户端将很高兴在没有tlsWithoutValidation
的情况下工作。
使用不验证任何内容的无知信任管理器:
private[this] class IgnorantTrustManager extends X509TrustManager {
def getAcceptedIssuers(): Array[X509Certificate] = new Array[X509Certificate](0)
def checkClientTrusted(certs: Array[X509Certificate], authType: String) {
}
def checkServerTrusted(certs: Array[X509Certificate], authType: String) {
}
}
然后将其用作信任管理器:
context.init(kmf.getKeyManagers, new IgnorantTrustManager(), null)
tlsWithoutValidation
选项必须删除: richHttpBuilder(HttpEndpoint(baseUri))
.hostConnectionLimit(1)
.tls(sslContext, Some(YandexClient.host))
.build()
此解决方案消除了证书的全部目的,因此应仅用于测试。