2012-10-21 5 views
5

Badamy platformę powiadomień push dla Androida (awaryjne przełączanie dla C2DM Google) Używam klienta Java Eclipse Paho do łączenia się z brokerem mosquitto (1.0.3). Broker jest zainstalowany na systemie Ubuntu 12.04 (instancja AWS EC2) Udało mi się połączyć klienta z serwerem przy użyciu niezaszyfrowanego połączenia TCP. Nawiasem mówiąc, po poprawieniu parametrów jądra, udało mi się otworzyć 100K klientów współbieżnych do jednej instancji brokera na średniej wielkości maszynie EC2. Dobra robota, mosquitto!Połączenie SSL od klienta Java (Eclipse Paho) ​​do brokto mosquitto: "unknown_ca"

Teraz próbuję skonfigurować bezpieczne połączenie przy użyciu protokołu SSL. Chcę uwierzytelnić klienta przy użyciu certyfikatu klienta. Poszedłem za objaśnieniami w mosquito_tls page i wygenerowałem klucze i samopodpisane certyfikaty dla serwera i klienta. Skonfigurowano serwer tak, aby korzystał z protokołu SSL.

Dla części klienta przyjrzałem się podpisowi mosquitto_tls_set i zauważyłem, że wymaga on certyfikatu CA, klucza klienta i plików certyfikatu. Uznałem, że certyfikat CA jest używany do uwierzytelniania serwera przez klienta, a klucz klienta i certyfikat są używane do uwierzytelniania klienta przez serwer. Mam rację?

Więc tutaj jest to, co zrobiłem po stronie Java:

  1. Zastosowanie nadmuchiwany zamek do załadowania trzech wymienionych plików.
  2. Umieść certyfikat CA w magazynie kluczy i użyj go do utworzenia TrustManagerFactory.
  3. Umieść klucz klienta i certyfikat w innym magazynie kluczy, a następnie użyj go do utworzenia obiektu KeyManagerFactory o numerze .
  4. Utworzono SSLContekst i zainicjalizowano go dwiema fabrykami.
  5. Utworzono SSLSocketFactory z SSLContext i przekazał ją do MqttConnectOptions w PAHO za

Kiedy ja connect, pojawia się następujący komunikat o błędzie z mosquitto

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

Edit: teraz widzę na po stronie klienta następujący wyjątek

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

Oto pełny kod

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 wygląda to

# 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 

Odpowiedz

7

ok, po pewnym wsparciu mosquitto dewelopera (thx Roger Light) zorientowaliśmy się problemu. Dane podane podczas generowania certyfikatu (firma, jednostka organizacyjna, nazwa zwyczajowa) muszą być różne w certyfikatach urzędu certyfikacji, klienta i serwera. w przeciwnym razie kod działa z niewielkimi zmianami. I tu odśwież prawidłowy kod z niektórych komentarzy dla jasności:

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

I stworzył istotę git z przykładowy kod powyżej, jak również pełnego wyjaśnienia wszystkich niezbędnych kroków, aby uczynić tę pracę: https: //gist.github. com/4104301 –

+0

"szczegóły podane podczas generowania certyfikatu ...muszą być różne w certyfikatach urzędu certyfikacji, klienta i serwera " Czy zdajesz sobie również sprawę, dlaczego jest to wymagane? – mvmn

Powiązane problemy