2015-06-09 146 views
3

我的服务器正在使用TLSv1.2并需要连接的客户端证书。我可以使用curl(这要求正常工作)发送请求到服务器:使用客户端证书与twitter finagle

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() 

,但我仍然得到错误:

The future returned an exception of type: com.twitter.finagle.ChannelWriteException, with message: com.twitter.finagle.SslHandshakeException: General SSLEngine problem at remote address:

我做错了吗?

回答

2

我花了一个星期才明白我做错了什么。

选项.tlsWithoutValidation().tls(sslContext, Some(MyClient.host))不能同时使用,因为它们配置了构建器的相同属性(Transport.TLSClientEngine)。

有三种解决方案。

  1. 使用正确的服务器证书。不幸的是,这个不适用。

  2. 将服务器证书添加到密钥库。它将被标记为可信,并且客户将愉快地在没有tlsWithoutValidation的情况下工作。

  3. 不使用无知信任管理器验证什么:

    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() 
    

    这个解决方案消除了证书的全部用途,所以它就是d仅用于测试。