2012-10-21 131 views
5

我正在研究Android的推送通知平臺(Google C2DM的故障轉移) 我正在使用Eclipse Paho Java客戶端連接到mosquitto broker 1.0.3)。該代理安裝在Ubuntu 12.04(AWS EC2實例) 上,我使用非加密的TCP連接成功將客戶端連接到服務器。 順便說一下,在調整內核參數之後,我能夠在一箇中型EC2機器上爲一個代理實例打開100K個併發客戶端。好工作,蚊子!從Java客戶端(Eclipse Paho)到mosquitto broker的SSL連接:「unknown_ca」

現在我正在嘗試使用SSL設置安全連接。我想使用客戶端證書來驗證客戶端。 我遵循mosquito_tls page中的解釋併爲服務器和客戶端生成密鑰和自簽名證書。 配置服務器以使用SSL。

對於客戶端部分,我查看了mosquitto_tls_set的簽名並注意到它需要CA證書,客戶端密鑰和證書文件。 我認爲CA證書用於客戶端驗證服務器,而客戶端密鑰和證書用於服務器驗證客戶端。 我對不對?

所以我這裏是我做了Java方面:

  1. 使用充氣城堡加載上述三個文件。
  2. 將CA證書放入密鑰庫中,並用它來創建TrustManagerFactory。
  3. 將客戶端密鑰和證書放入另一個密鑰存儲中,並用它創建KeyManagerFactory的 。
  4. 創建了一個SSLContext,並使用兩個工廠對其進行了初始化。
  5. 創建從SSLContext中的SSLSocketFactory的,並將其傳遞給泛美衛生組織的MqttConnectOptions

當我做了連接,我從mosquitto收到以下錯誤

OpenSSL Error: error:140890B2:SSL routines:SSL3_GET_CLIENT_CERTIFICATE:no certificate returned 
Socket read error on client (null), disconnecting. 

編輯: 現在我看到的客戶端以下例外

javax.net.ssl.SSLHandshakeException: Received fatal alert: unknown_ca 

以下是完整的代碼

static SSLSocketFactory getSocketFactory (final String caCrtFile, final String crtFile, final String keyFile, final String password) throws Exception 
{ 
    Security.addProvider(new BouncyCastleProvider()); 

    PEMReader reader = new PEMReader(new InputStreamReader(new ByteArrayInputStream(Files.readAllBytes(Paths.get(caCrtFile))))); 
    X509Certificate caCert = (X509Certificate)reader.readObject(); 
    reader.close(); 

    reader = new PEMReader(new InputStreamReader(new ByteArrayInputStream(Files.readAllBytes(Paths.get(crtFile))))); 
    X509Certificate cert = (X509Certificate)reader.readObject(); 
    reader.close(); 

    reader = new PEMReader(
      new InputStreamReader(new ByteArrayInputStream(Files.readAllBytes(Paths.get(keyFile)))), 
      new PasswordFinder() { 
       public char[] getPassword() { 
        return password.toCharArray(); 
       } 
      } 
    ); 
    KeyPair key = (KeyPair)reader.readObject(); 
    reader.close(); 

    KeyStore caKs = KeyStore.getInstance("JKS"); 
    caKs.load(null, null); 
    caKs.setCertificateEntry("ca-certificate", caCert); 
    TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX"); 
    tmf.init(caKs); 

    KeyStore ks = KeyStore.getInstance("JKS"); 
    ks.load(null, null); 
    ks.setCertificateEntry("certificate", cert); 
    ks.setKeyEntry("private-key", key.getPrivate(), password.toCharArray(), new java.security.cert.Certificate[]{cert}); 
    //ks.setKeyEntry("public-key", key.getPublic(), password.toCharArray(), new java.security.cert.Certificate[]{cert}); 
    KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); 
    kmf.init(ks, password.toCharArray()); 

    SSLContext context = SSLContext.getInstance("SSLv3"); 
    context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); 

    return context.getSocketFactory(); 
} 

的mosquito.conf看起來像這樣

# general options 
pid_file /home/ubuntu/mosquitto.pid 

# persistence 
queue_qos0_messages false 
persistence false 

# logging 
log_dest stdout 
connection_messages true 
log_timestamp false 

# default listener 
# disable default listener (open only SSL listener) 
#port 1883 
#max_connections -1 

# SSL listener 
listener 1883 
cafile /home/ubuntu/etc/ca.crt 
certfile /home/ubuntu/etc/server.crt 
keyfile /home/ubuntu/etc/server.key 
require_certificate true 
use_identity_as_username true 
max_connections -1 

回答

7

確定,從mosquitto開發一些支持後(感謝,羅傑光),我們想通了這個問題。 您在生成證書(公司,組織單位,公用名稱)時提供的詳細信息在CA,客戶端和服務器證書中必須不同。 否則代碼會進行一些較小的更改。我在這裏轉貼正確的代碼爲清楚起見,一些意見:

import java.io.*; 
import java.nio.file.*; 
import java.security.*; 
import java.security.cert.*; 
import java.security.interfaces.*; 
import javax.net.ssl.*; 

import org.bouncycastle.jce.provider.*; 
import org.bouncycastle.openssl.*; 

static SSLSocketFactory getSocketFactory (final String caCrtFile, final String crtFile, final String keyFile, final String password) throws Exception 
{ 
    Security.addProvider(new BouncyCastleProvider()); 

    // load CA certificate 
    PEMReader reader = new PEMReader(new InputStreamReader(new ByteArrayInputStream(Files.readAllBytes(Paths.get(caCrtFile))))); 
    X509Certificate caCert = (X509Certificate)reader.readObject(); 
    reader.close(); 

    // load client certificate 
    reader = new PEMReader(new InputStreamReader(new ByteArrayInputStream(Files.readAllBytes(Paths.get(crtFile))))); 
    X509Certificate cert = (X509Certificate)reader.readObject(); 
    reader.close(); 

    // load client private key 
    reader = new PEMReader(
      new InputStreamReader(new ByteArrayInputStream(Files.readAllBytes(Paths.get(keyFile)))), 
      new PasswordFinder() { 
       public char[] getPassword() { 
        return password.toCharArray(); 
       } 
      } 
    ); 
    KeyPair key = (KeyPair)reader.readObject(); 
    reader.close(); 

    // CA certificate is used to authenticate server 
    KeyStore caKs = KeyStore.getInstance("JKS"); 
    caKs.load(null, null); 
    caKs.setCertificateEntry("ca-certificate", caCert); 
    TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX"); 
    tmf.init(caKs); 

    // client key and certificates are sent to server so it can authenticate us 
    KeyStore ks = KeyStore.getInstance("JKS"); 
    ks.load(null, null); 
    ks.setCertificateEntry("certificate", cert); 
    ks.setKeyEntry("private-key", key.getPrivate(), password.toCharArray(), new java.security.cert.Certificate[]{cert}); 
    KeyManagerFactory kmf = KeyManagerFactory.getInstance("PKIX"); 
    kmf.init(ks, password.toCharArray()); 

    // finally, create SSL socket factory 
    SSLContext context = SSLContext.getInstance("TLSv1"); 
    context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); 

    return context.getSocketFactory(); 
} 
+0

我創建了一個git的要點以上,以及所需的所有步驟,充分說明了示例代碼,使這項工作:HTTPS://gist.github。 com/4104301 –

+0

「您在生成證書時提供的詳細信息...必須在CA,客戶端和服務器證書中有所不同「 您碰巧也知道爲什麼這是必需的? – mvmn