6

Android用のプッシュ通知プラットフォーム(GoogleのC2DMのフェイルオーバー)を調査しています。EclipsePahoJavaクライアントを使用してmosquittoブローカー(1.0.3)に接続しています。ブローカーはUbuntu12.04(AWS EC2インスタンス)にインストールされています暗号化されていないTCP接続を使用してクライアントをサーバーに正常に接続しました。ちなみに、カーネルパラメータを調整した後、中規模のEC2マシン上の1つのブローカーインスタンスに対して100Kの同時クライアントを開くことができました。お疲れ様でした、モスキート!

現在、SSLを使用してセキュリティで保護された接続を設定しようとしています。クライアント証明書を使用してクライアントを認証したい。mosquito_tlsページの説明に従い、サーバーとクライアントのキーと自己署名証明書を生成しました。SSLを使用するようにサーバーを構成しました。

クライアント部分については、mosquitto_tls_setの署名を調べ、CA証明書、クライアントキー、および証明書ファイルが必要であることに注意しました。クライアントがサーバーを認証するためにCA証明書が使用され、サーバーがクライアントを認証するためにクライアントキーと証明書が使用されることがわかりました。私は正しいですか?

だから私はここに私がJava側でしたことです:

  1. 弾力がある城を使用して、上記の3つのファイルをロードします。
  2. CA証明書をキーストアに入れ、それを使用してTrustManagerFactoryを作成しました。
  3. クライアントキーと証明書を別のキーストアに配置し、それを使用してKeyManagerFactoryを作成しました。
  4. SSLContextを作成し、2つのファクトリで初期化しました。
  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
4

1 に答える 1

9

わかりました。モスキート開発者(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();
}
于 2012-10-24T16:37:53.660 に答える