Reputation: 61
My server is using TLSv1.2 and requires client certificate for connections. I can send requests to server using CURL (This request works fine):
curl --data "SAMPLETEXT" https://myserver.com/webservice --insecure --key privkey.pem --cert certificate.cert
(yes, server has self-signed certificate and requires --insecure
flag; no, I cannot fix this).
Now, I want to create client to send requests from Scala code. MyClient
is object containing needed passwords and paths. To do so I create 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
}
and later use it to build client:
private val httpClient =
richHttpBuilder(HttpEndpoint(baseUri))
.hostConnectionLimit(1)
.tlsWithoutValidation()
.tls(sslContext, Some(MyClient.host))
.build()
but I still get error:
The future returned an exception of type: com.twitter.finagle.ChannelWriteException, with message: com.twitter.finagle.SslHandshakeException: General SSLEngine problem at remote address:
What I'm doing wrong?
Upvotes: 3
Views: 2757
Reputation: 61
It took me week to realize what I was doing wrong.
Options .tlsWithoutValidation()
and .tls(sslContext, Some(MyClient.host))
cannot be used at the same time because they configure same property (Transport.TLSClientEngine
) of the builder.
There are three solutions.
Use proper server certificate. This one is unapplicable, unfortunately.
Add server certificate to keystore. It will be marked as trusted, and client will happily work without tlsWithoutValidation
.
Use ignorant trust manager that doesn't validates anything:
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) {
}
}
Then use it as trust manager:
context.init(kmf.getKeyManagers, new IgnorantTrustManager(), null)
tlsWithoutValidation
option must be removed:
richHttpBuilder(HttpEndpoint(baseUri))
.hostConnectionLimit(1)
.tls(sslContext, Some(YandexClient.host))
.build()
This solution eliminates the whole purpose of certificates, so it should be used for tests only.
Upvotes: 3