2012-03-05 16 views
9

Próbuję teraz skonfigurować Netty z 2-drożnym handshake SSL, w którym zarówno klient, jak i serwer przedstawiają i weryfikują certyfikaty.Konfigurowanie Netty za pomocą 2-way SSL Handsake (certyfikat klienta i serwera)

Wydaje się, że nie jest to zaimplementowane w SslHandler. Czy ktoś to robi? Przypuszczam, że przejdzie on w operacji SslHandler.handshake i zostanie delegowany do javax.net.ssl.SSLEngine?

Jakieś wskazówki/wskazówki/wcześniejsze wdrożenia?

Dzięki!


ODPOWIEDŹ (stackoverflow nie pozwoli mi opublikować go w normalny sposób) stwierdziliśmy, że jeśli ustawić flagę needClientAuth na obiekcie SSLEngine przed skonfigurowaniu SslHandler, że dba o problemie!

Odpowiedz

10

Oto rozwiązanie, oparte na przykładzie serwera HttpSnoop z projektu netty.

Podczas konfigurowania rurociągu po stronie klienta, silnik SSL musi być ustawiony w następujący sposób:

public ChannelPipeline getPipeline() throws Exception { 
    // Create a default pipeline implementation. 
    ChannelPipeline pipeline = pipeline(); 

    // Uncomment the following line if you want HTTPS 
    SSLEngine engine = SecureChatSslContextFactory.getServerContext().createSSLEngine(); 
    engine.setUseClientMode(false); 
    engine.setNeedClientAuth(true); 
    pipeline.addLast("ssl", new SslHandler(engine)); 

    pipeline.addLast("decoder", new HttpRequestDecoder()); 
    pipeline.addLast("logger", new RequestAuditLogger()); 
    // Uncomment the following line if you don't want to handle HttpChunks. 
    pipeline.addLast("aggregator", new HttpChunkAggregator(1048576)); 
    pipeline.addLast("outputLogger", new ResponseAuditLogger()); 
    pipeline.addLast("encoder", new HttpResponseEncoder()); 
    // Remove the following line if you don't want automatic content compression. 
    pipeline.addLast("deflater", new HttpContentCompressor()); 
    pipeline.addLast("handler", new HttpSnoopServerHandler()); 
    return pipeline; 
} 
} 

Wtedy twój SSLContext należy zmodyfikować w następujący sposób założyć sklep zaufania oprócz kluczy (SecureChatSslContextFactory) :

public final class SecureChatSslContextFactory { 


private static Logger logger = LoggerFactory.getLogger(SecureChatSslContextFactory.class); 

private static final String PROTOCOL = "TLS"; 
private static final SSLContext SERVER_CONTEXT; 
private static final SSLContext CLIENT_CONTEXT; 

static { 

    SSLContext serverContext = null; 
    SSLContext clientContext = null; 

     // get keystore and trustore locations and passwords 
    String keyStoreLocation = System.getProperty("javax.net.ssl.keyStore"); 
    String keyStorePassword = System.getProperty("javax.net.ssl.keyStorePassword"); 
    String trustStoreLocation = System.getProperty("javax.net.ssl.trustStore"); 
    String trustStorePassword = System.getProperty("javax.net.ssl.trustStorePassword"); 
    try { 

     KeyStore ks = KeyStore.getInstance("JKS"); 
     ks.load(KeyStoreStreamManager.asInputStream(keyStoreLocation), 
       keyStorePassword.toCharArray()); 

     // Set up key manager factory to use our key store 
     KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); 
     kmf.init(ks, keyStorePassword.toCharArray()); 

      // truststore 
     KeyStore ts = KeyStore.getInstance("JKS"); 
     ts.load(KeyStoreStreamManager.asInputStream(trustStoreLocation), 
       trustStorePassword.toCharArray()); 

     // set up trust manager factory to use our trust store 
     TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); 
     tmf.init(ts); 

     // Initialize the SSLContext to work with our key managers. 
     serverContext = SSLContext.getInstance(PROTOCOL); 
     serverContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); 

    } catch (Exception e) { 
     throw new Error(
       "Failed to initialize the server-side SSLContext", e); 
    } 

    try { 
     clientContext = SSLContext.getInstance(PROTOCOL); 
     clientContext.init(null, SecureChatTrustManagerFactory.getTrustManagers(), null); 
    } catch (Exception e) { 
     throw new Error(
       "Failed to initialize the client-side SSLContext", e); 
    } 

    SERVER_CONTEXT = serverContext; 
    CLIENT_CONTEXT = clientContext; 
} 

public static SSLContext getServerContext() { 
    return SERVER_CONTEXT; 
} 

public static SSLContext getClientContext() { 
    return CLIENT_CONTEXT; 
} 

private SecureChatSslContextFactory() { 
    // Unused 
} 
} 
+1

Chciałbym skomentować odpowiedź CStepnitz. Z dokumentacji SslEngine: Konfiguruje silnik tak, aby wymagał uwierzytelniania klienta. Ta opcja jest przydatna tylko w przypadku silników w trybie serwera. Nie po stronie klienta, jak wskazał. – user1792307

+0

@CStepnitz: Czy wiesz, jakie certyfikaty akceptuje TrustManager? Mam bardzo podobną architekturę, ale klient wysyła certyfikat ECC (a uzgadnianie kończy się niepowodzeniem, ponieważ krzywa na certyfikacie nie jest rozpoznawana), ale certyfikaty RSA są akceptowane. – favicon

4

uwierzytelniania wzajemnego jest teraz obsługiwana przez SslContext (obecnie tylko dla dostawcy JDK, ale OpenSSL zapewnić wsparcie wkrótce). Zobacz newClientContext i newServerContext, które teraz obsługują przyjmowanie TrustManagerFactory i KeyManagerFactory. Te statyczne metody fabryczne wspierają również bezpośrednie pobieranie plików certyfikatów, kluczy i certyfikatów, aby zbudować TrustManagerFactory i KeyManagerFactory dla ciebie.

Zobacz przykład jak wymagać uwierzytelnienia klienta (dla dostawcy JDK).

+0

Silnik OpenSSL obsługuje teraz wzajemne uwierzytelnianie. Silnik OpenSSL ma w zasadzie parzystość funkcji z silnikiem SSL JDK.Zobacz [SSLEngineTest] (https://github.com/netty/netty/blob/4.1/handler/src/test/java/io/netty/SSler/ssl/SSLEngineTest.java) i jeśli brakuje funkcji, proszę [file problem] (https://github.com/netty/netty/issues). –

6

Zamiast ustawienia SSLEngine użyj nettys SslContext, aby utworzyć nowy SslHandler. Zasadniczo można utworzyć nową SslContext przekazując KeyManagerFactory następująco

SslContext sslContext = SslContextBuilder.forServer (keyManagerFactory) .build();

Następnie użyj utworzonego SslContext, aby uzyskać obsługę dla ChannelPipeline.

ChannelPipeline.addLast ("ssl", sslContext.newHandler (socketChannel.alloc()));