从Java客户端(Eclipse Paho)到mosquitto broker的SSL连接:“unknown_ca”

时间:2012-10-21 11:51:49

标签: java ssl mqtt

我们正在研究针对Android的推送通知平台(Google C2DM的故障转移) 我正在使用Eclipse Paho Java客户端连接到mosquitto broker(1.0.3)。代理安装在Ubuntu 12.04(AWS EC2实例)上 我使用非加密TCP连接成功将客户端连接到服务器。 顺便说一句,在调整内核参数之后,我能够在中型EC2机器上向一个代理实例打开100K并发客户端。好工作,mosquitto!

现在我正在尝试使用SSL建立安全连接。我想使用客户端证书对客户端进行身份验证。 我按照mosquito_tls page中的说明进行了操作,并为服务器和客户端生成了密钥和自签名证书。 配置服务器以使用SSL。

对于客户端部分,我查看了mosquitto_tls_set的签名,并注意到它需要CA证书,客户端密钥和证书文件。 我认为CA证书用于客户端验证服务器,而客户端密钥和证书用于服务器验证客户端。 我是对的吗?

所以我这就是我在Java方面所做的:

  1. 使用充气城堡加载上述三个文件。
  2. 将CA证书放入密钥库并用它来创建TrustManagerFactory。
  3. 将客户端密钥和证书放在另一个密钥库中并用它来创建 的KeyManagerFactory。
  4. 创建了一个SSLContext,用两个工厂初始化它。
  5. 从SSLContext创建一个SSLSocketFactory并将其传递给Paho的MqttConnectOptions
  6. 当我进行连接时,我从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
    

1 个答案:

答案 0 :(得分:9)

好吧,在得到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();
}