Using client certificates with twitter finagle

My server is using TLSv1.2 and requires a client certificate for connections. I can send requests to the server using CURL (this request works fine):

curl --data "SAMPLETEXT" https://myserver.com/webservice --insecure --key privkey.pem --cert certificate.cert

      

(yes, the server has a self-signed certificate and a flag is required --insecure

; no, I can't fix that). Now I want to create a client to send requests from Scala code. MyClient

is an object containing the required passwords and paths. For this 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 then use it to create a client:

  private val httpClient =
  richHttpBuilder(HttpEndpoint(baseUri))
    .hostConnectionLimit(1)
    .tlsWithoutValidation()
    .tls(sslContext, Some(MyClient.host))
    .build()

      

but i still get the error:

In the future, an exception of type: com.twitter.finagle.ChannelWriteException is returned, with the message: com.twitter.finagle.SslHandshakeException: common SSLEngine problem at remote address:

What am I doing wrong?

+3


source to share


1 answer


It took me a week to figure out what I was doing wrong.

The parameters .tlsWithoutValidation()

and .tls(sslContext, Some(MyClient.host))

cannot be used at the same time because they set the same property ( Transport.TLSClientEngine

) of the constructor.



There are three solutions.

  • Use the correct server certificate. Unfortunately, this is not applicable.

  • Add the server certificate to the keystore. It will be marked as trusted and the client will happily work without tlsWithoutValidation

    .

  • Use an ignorant trust manager that checks nothing:

      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 a trusted manager:

    context.init(kmf.getKeyManagers, new IgnorantTrustManager(), null)
    
          

    tlsWithoutValidation

    you need to remove:

      richHttpBuilder(HttpEndpoint(baseUri))
        .hostConnectionLimit(1)
        .tls(sslContext, Some(YandexClient.host))
        .build()
    
          

    This solution removes the whole purpose of certificates, so it should only be used for tests.

+2


source







All Articles