2012-03-07 24 views
7

Sono stato incaricato di implementare un server Web Java personalizzato/autonomo in grado di elaborare messaggi SSL e non SSL sulla stessa porta.Handshaking SSL utilizzando certificati autofirmati e SSLEngine (JSSE)

Ho implementato un server NIO e funziona abbastanza bene per richieste non SSL. Sto avendo un pò di tempo con il pezzo SSL e potrei davvero usare qualche consiglio.

Ecco cosa ho fatto finora.

Per distinguere tra messaggi SSL e non SSL, controllo il primo byte della richiesta in ingresso per verificare se si tratta di un messaggio SSL/TLS. Esempio:

byte a = read(buf); 
    if (totalBytesRead==1 && (a>19 && a<25)){ 
     parseTLS(buf); 
    } 

Nelle parseTLS() metodo che istanziare uno SSLEngine simili:

java.security.KeyStore ks = java.security.KeyStore.getInstance("JKS"); 
    java.security.KeyStore ts = java.security.KeyStore.getInstance("JKS"); 

    ks.load(new java.io.FileInputStream(keyStoreFile), passphrase); 
    ts.load(new java.io.FileInputStream(trustStoreFile), passphrase); 

    KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); 
    kmf.init(ks, passphrase); 

    TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509"); 
    tmf.init(ts); 

    SSLContext sslc = SSLContext.getInstance("TLS");  
    sslc.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); 


    SSLEngine serverEngine = sslc.createSSLEngine(); 
    serverEngine.setUseClientMode(false); 
    serverEngine.setEnableSessionCreation(true); 
    serverEngine.setWantClientAuth(true); 

volta che lo SSLEngine viene istanziata, ho elaborare i dati in ingresso utilizzando i metodi di decifratura/involucro utilizzando il codice direttamente del funzionario JSSE Samples:

log("----"); 

    serverResult = serverEngine.unwrap(inNetData, inAppData); 
    log("server unwrap: ", serverResult); 
    runDelegatedTasks(serverResult, serverEngine); 

    log("----"); 

    serverResult = serverEngine.wrap(outAppData, outNetData); 
    log("server wrap: ", serverResult); 
    runDelegatedTasks(serverResult, serverEngine); 

la prima parte della stretta di mano sembra funzionare bene. Il client invia un messaggio stretta di mano e il server risponde con un messaggio con 4 record:

handshake (22) 
- server_hello (2) 
- certificate (11) 
- server_key_exchange (12) 
- certificate_request (13) 
- server_hello_done (14) 

Avanti, il client invia un messaggio con tre parti:

handshake (22) 
- certificate (11) 
- client_key_exchange (16) 

change_cipher_spec (20) 
- client_hello (1) 

handshake (22) 
*** Encrypted Message **** 

Lo SSLEngine scarta la richiesta del client e analizza i record ma il metodo wrap produce 0 byte con uno stato di handshake di OK/NEED_UNWRAP. In altre parole, non c'è niente da restituire al cliente e la stretta di mano arriva a una brusca frenata.

Questo è il punto in cui sono bloccato.

Nel debugger, posso vedere che SSLEngine, in particolare ServerHandshaker, non trova alcun peer certs. Questo è piuttosto ovvio quando guardo il record del certificato dal client che è lungo 0 byte. Ma perché?

Posso solo supporre che ci sia qualcosa di sbagliato nella risposta di HelloServer ma non riesco a metterci il dito sopra. Il server sembra inviare un certificato valido ma il client non sta inviando nulla indietro. C'è un problema con il mio keystore? O è il truststore? O ha qualcosa a che fare con il modo in cui sto istanziando SSLEngine? Sono perplesso.

paio di altri punti:

  • L'archivio chiavi e truststore si fa riferimento nel codice snippit sopra sono stati creati utilizzando il seguente tutorial: http://www.techbrainwave.com/?p=953
  • sto usando Firefox 10 e IE 9 come il client di prova il server. Stessi risultati per entrambi i client web.
  • Utilizzo Sun/Oracle JDK 6 e Java Secure Socket Extension (JSSE) fornito in dotazione.

Non vedo l'ora di avere una guida che potreste avere, ma per favore non dirmi che sono pazzo o di usare Netty o Grizzly o qualche altra soluzione esistente. Non è un'opzione in questo momento. Voglio solo capire cosa sto facendo male.

Grazie in anticipo!

+0

Solo una domanda stupida: hai generato e installato certificati client in es. Firefox quando ti connetti al tuo web-server tramite HTTPS? – Robert

+0

Sì. FF mi ha chiesto di accettare/rifiutare il certificato. Ho cliccato su "Accetto i rischi tecnici" e posso vedere il certificato in Strumenti-> Opzioni-> Avanzate-> Crittografia-> Visualizza certificati-> Server. Infatti, FF non invierà il secondo messaggio di handshake senza questo. – Peter

+0

Non è quello che stavo chiedendo. Stavo chiedendo il certificato del cliente. L'autenticazione del client SSL è facoltativa e utilizzata principalmente in ambienti aziendali, spesso in combinazione con chip-card. Se il client non ha un certificato installato (sezione "Il tuo certificato" di Firefox) non ne invierà uno. – Robert

risposta

9

Hai ottenuto NEED_UNWRAP, quindi esegui uno scocco. Questo a sua volta potrebbe darti BUFFER_UNDERFLOW, il che significa che devi fare una lettura e riprovare a scartare.

Analogamente quando si ottiene NEED_WRAP, eseguire un wrap: a sua volta potrebbe dare BUFFER_OVERFLOW, il che significa che è necessario eseguire una scrittura e riprovare l'involucro.

Questo avvolgere o scartare potrebbe a sua volta suggerire di eseguire un'altra operazione: avvolgere o scartare.

Basta fare ciò che ti dice di fare.

+0

Sì, ho provato a scartare dopo aver visto il NEED_UNWRAP ma non c'è nient'altro da leggere. Riesco a vedere la seconda stretta di mano del client che attraversa il cavo nella sua interezza e SSLEngine analizza tutto. Sfortunatamente, non produce alcun output da inviare al cliente. Il client attende un po 'e alla fine riattacca. – Peter

+1

@Peter Potrebbe non esserci nulla da leggere ma potrebbe ancora esserci qualcosa da scartare. Fallo nel modo in cui ho detto, lascia che ti dica quando leggere piuttosto che assumere che ogni unwrap ha bisogno di una lettura. In genere, la prima risposta del server nell'handshake consiste di tre messaggi separati, ad esempio, quindi otterrete 3 NEED_WRAP in una riga: al contrario il client probabilmente leggerà tutto ciò in un'unica lettura, ma otterrà tre successivi NEED_UNWRAPS. Lo stesso vale per tutta la stretta di mano. – EJP

+0

OK, fantastico. Grazie a EJP e un po 'più di debug, capisco cosa sta succedendo ora. SSLEngine sta scartando un record alla volta. Quindi nella seconda stretta di mano del cliente, ci sono in realtà 3 record: handshake, change_cipher_spec e un'altra stretta di mano. Avevo solo chiamato unwrap una volta, quindi SSLEngine ha analizzato solo il primo record. Doveva elaborare tutti i record prima di generare eventuali byte da inviare al client. Sto finalmente recuperando i dati dell'applicazione! – Peter