簡體   English   中英

從Java客戶端(Eclipse Paho)到mosquitto broker的SSL連接:“unknown_ca”

[英]SSL connection from Java client (Eclipse Paho) to mosquitto broker: “unknown_ca”

我們正在研究Android的推送通知平台(谷歌C2DM的故障轉移)我正在使用Eclipse Paho Java客戶端連接到mosquitto broker(1.0.3)。 代理安裝在Ubuntu 12.04(AWS EC2實例)上我使用非加密TCP連接成功將客戶端連接到服務器。 順便說一句,在調整內核參數之后,我能夠在中型EC2機器上向一個代理實例打開100K並發客戶端。 好工作,蚊子!

現在我正在嘗試使用SSL建立安全連接。 我想使用客戶端證書對客戶端進行身份驗證。 我按照mosquito_tls頁面中的說明進行了操作,並為服務器和客戶端生成了密鑰和自簽名證書。 配置服務器以使用SSL。

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

所以我在這里是我在Java方面所做的:

  1. 使用充氣城堡加載上述三個文件。
  2. 將CA證書放在密鑰庫中並使用它來創建TrustManagerFactory。
  3. 將客戶端密鑰和證書放在另一個密鑰庫中,並使用它來創建KeyManagerFactory。
  4. 創建了一個SSLContext,用兩個工廠初始化它。
  5. 從SSLContext創建了一個SSLSocketFactory並將其傳遞給Paho的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

好吧,經過mosquitto開發人員(thx,Roger Light)的一些支持后,我們發現了問題所在。 生成證書(公司,組織單位,公用名)時提供的詳細信息在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();
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM