2015-06-09 143 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僅用於測試。