2013-08-06 14 views
6

Korzystam z Jetty z HTTPS i ważnego certyfikatu i nie jestem pewien, czy jest to poprawne, ponieważ zestaw szyfrów wydaje się być SSL_NULL_WITH_NULL_NULL w logach serwera. Jednak logi klientów wyglądają dobrze.SSL_NULL_WITH_NULL_NULL zestaw algorytmów szyfrowania w dziennikach Jetty

Długa historia: dołączam próbkę Java oczekującą Jetty-7.6.10 i dwa skrypty do tworzenia magazynu kluczy i magazynu zaufanych certyfikatów.

JettyHttpsForStackOverflow uruchamia klienta i serwer, razem lub oddzielnie, aby anulować dowiązanie dzienników.

Skrypt create-chains.sh tworzy magazyn kluczy i magazyn zaufanych certyfikatów. Magazyn kluczy zawiera łańcuch kończący się głównym urzędem certyfikacji wygenerowanym z tymczasowego magazynu kluczy. Replikuje on rzeczywistą sprawę z urzędem certyfikacji i certyfikatami pośrednimi.

Skrypt create-single-autosigned.sh tworzy również magazyn kluczy i magazyn zaufanych certyfikatów, ale z samopodpisanym certyfikatem.

Należy pamiętać, że SSL_NULL_WITH_NULL_NULL pojawia się jako zestaw szyfrów serwera dla obu łańcuchów certyfikatów.

Myślę, że nie ma problemu z nazwą domeny serwera. Ten sam problem występuje w przypadku serwera działającego na komputerze z nazwą domeny pasującą do nazwy wyróżniającej w prawidłowo podpisanym certyfikacie. SSLLab potwierdził, że SSL na moim serwerze działa dobrze (klasa B), a Google Chrome łączy się szczęśliwie.

Myślę, że nie ma problemu z klientem Jetty. Kiedy go używam, po prostu dzwoni SSLContextFactory konfiguruję, aby utworzyć SSLSocket. O dziwo, w dziennikach klienta Jetty wygląda na to, że używany pakiet algorytmu to TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA.

Czy to normalne, aby uzyskać SSL_NULL_WITH_NULL_NULL w dziennikach serwera Jetty? Jeśli nie, jak to naprawić?

create-single-autosigned.sh

#!/bin/bash 

rm their-keystore.jks 2> /dev/null 
rm my-keystore.jks 2> /dev/null 
rm my-truststore.jks 2> /dev/null 

echo "====================================================" 
echo "Creating fake third-party chain ca2 -> ca1 -> ca ..." 
echo "====================================================" 

keytool -genkeypair -alias ca -dname cn=ca       \ 
    -validity 10000 -keyalg RSA -keysize 2048       \ 
    -ext BasicConstraints:critical=ca:true,pathlen:10000    \ 
    -keystore their-keystore.jks -keypass Keypass -storepass Storepass 

keytool -genkeypair -alias ca1 -dname cn=ca1       \ 
    -validity 10000 -keyalg RSA -keysize 2048       \ 
    -keystore their-keystore.jks -keypass Keypass -storepass Storepass 

keytool -genkeypair -alias ca2 -dname cn=ca2       \ 
    -validity 10000 -keyalg RSA -keysize 2048       \ 
    -keystore their-keystore.jks -keypass Keypass -storepass Storepass 


    keytool -certreq -alias ca1           \ 
    -keystore their-keystore.jks -keypass Keypass -storepass Storepass \ 
| keytool -gencert -alias ca            \ 
    -ext KeyUsage:critical=keyCertSign         \ 
    -ext SubjectAlternativeName=dns:ca1         \ 
    -keystore their-keystore.jks -keypass Keypass -storepass Storepass \ 
| keytool -importcert -alias ca1           \ 
    -keystore their-keystore.jks -keypass Keypass -storepass Storepass 

#echo "Debug exit" ; exit 0 

    keytool -certreq -alias ca2           \ 
    -keystore their-keystore.jks -keypass Keypass -storepass Storepass \ 
| keytool -gencert -alias ca1           \ 
    -ext KeyUsage:critical=keyCertSign         \ 
    -ext SubjectAlternativeName=dns:ca2         \ 
    -keystore their-keystore.jks -keypass Keypass -storepass Storepass \ 
| keytool -importcert -alias ca2          \ 
    -keystore their-keystore.jks -keypass Keypass -storepass Storepass 

keytool -list -v -storepass Storepass -keystore their-keystore.jks 


echo "====================================================================" 
echo "Fake third-party chain generated. Now generating my-keystore.jks ..." 
echo "====================================================================" 
read -p "Press a key to continue." 

# Import authority's certificate chain 

    keytool -exportcert -alias ca           \ 
    -keystore their-keystore.jks -keypass Keypass -storepass Storepass \ 
| keytool -importcert -trustcacerts -noprompt -alias ca     \ 
    -keystore my-keystore.jks -keypass Keypass -storepass Storepass 

    keytool -exportcert -alias ca1          \ 
    -keystore their-keystore.jks -keypass Keypass -storepass Storepass \ 
| keytool -importcert -noprompt -alias ca1        \ 
    -keystore my-keystore.jks -keypass Keypass -storepass Storepass 

    keytool -exportcert -alias ca2          \ 
    -keystore their-keystore.jks -keypass Keypass -storepass Storepass \ 
| keytool -importcert -noprompt -alias ca2        \ 
    -keystore my-keystore.jks -keypass Keypass -storepass Storepass 

# Create our own certificate, the authority signs it. 

keytool -genkeypair -alias e1 -dname cn=e1      \ 
    -validity 10000 -keyalg RSA -keysize 2048      \ 
    -keystore my-keystore.jks -keypass Keypass -storepass Storepass 

    keytool -certreq -alias e1           \ 
    -keystore my-keystore.jks -keypass Keypass -storepass Storepass  \ 
| keytool -gencert -alias ca2           \ 
    -ext SubjectAlternativeName=dns:localhost,ip:127.0.0.1    \ 
    -ext KeyUsage:critical=keyEncipherment,digitalSignature    \ 
    -ext ExtendedKeyUsage=serverAuth,clientAuth       \ 
    -keystore their-keystore.jks -keypass Keypass -storepass Storepass \ 
| keytool -importcert -alias e1           \ 
    -keystore my-keystore.jks -keypass Keypass -storepass Storepass 

keytool -list -v -storepass Storepass -keystore my-keystore.jks 

echo "=================================================" 
echo "Keystore generated. Now generating truststore ..." 
echo "=================================================" 
read -p "Press a key to continue." 

    keytool -exportcert -alias ca          \ 
    -keystore my-keystore.jks -keypass Keypass -storepass Storepass \ 
| keytool -importcert -trustcacerts -noprompt -alias ca    \ 
    -keystore my-truststore.jks -keypass Keypass -storepass Storepass 

    keytool -exportcert -alias ca1          \ 
    -keystore my-keystore.jks -keypass Keypass -storepass Storepass \ 
| keytool -importcert -noprompt -alias ca1        \ 
    -keystore my-truststore.jks -keypass Keypass -storepass Storepass 

    keytool -exportcert -alias ca2          \ 
    -keystore my-keystore.jks -keypass Keypass -storepass Storepass \ 
| keytool -importcert -noprompt -alias ca2        \ 
    -keystore my-truststore.jks -keypass Keypass -storepass Storepass 

    keytool -exportcert -alias e1          \ 
    -keystore my-keystore.jks -keypass Keypass -storepass Storepass \ 
| keytool -importcert -noprompt -alias e1        \ 
    -keystore my-truststore.jks -keypass Keypass -storepass Storepass 

keytool -list -v -storepass Storepass -keystore my-truststore.jks 

rm their-keystore.jks 2> /dev/null 

create-single-autosigned.sh

#!/bin/bash 

rm my-keystore.jks 2> /dev/null 
rm my-truststore.jks 2> /dev/null 

keytool -genkeypair -alias e1 -dname cn=e1      \ 
    -validity 10000 -keyalg RSA -keysize 2048      \ 
    -keystore my-keystore.jks -keypass Keypass -storepass Storepass 



keytool -list -v -storepass Storepass -keystore my-keystore.jks 

echo "=================================================" 
echo "Keystore generated. Now generating truststore ..." 
echo "=================================================" 
read -p "Press a key to continue." 

    keytool -exportcert -alias e1          \ 
    -keystore my-keystore.jks -keypass Keypass -storepass Storepass \ 
| keytool -importcert -noprompt -alias e1        \ 
    -keystore my-truststore.jks -keypass Keypass -storepass Storepass 

keytool -list -v -storepass Storepass -keystore my-truststore.jks 

JettyHttpsForStackOverflow.java

import java.io.IOException; 
import java.io.InputStream; 
import java.net.URL; 
import java.security.KeyManagementException; 
import java.security.KeyStore; 
import java.security.KeyStoreException; 
import java.security.NoSuchAlgorithmException; 
import java.security.SecureRandom; 
import java.security.UnrecoverableKeyException; 
import java.security.cert.Certificate; 
import java.security.cert.CertificateException; 
import java.security.cert.X509Certificate; 
import javax.net.ssl.KeyManager; 
import javax.net.ssl.KeyManagerFactory; 
import javax.net.ssl.SSLContext; 
import javax.net.ssl.SSLEngine; 
import javax.net.ssl.TrustManager; 
import javax.net.ssl.TrustManagerFactory; 
import javax.net.ssl.X509TrustManager; 

import org.eclipse.jetty.client.ContentExchange; 
import org.eclipse.jetty.client.HttpClient; 
import org.eclipse.jetty.server.Server; 
import org.eclipse.jetty.server.ssl.SslSelectChannelConnector; 
import org.eclipse.jetty.util.ssl.SslContextFactory; 


/** 
* Code sample for Jetty {@link HttpClient} with HTTPS, in a completely standalone fashion. 
* Use create-chains.sh and create-empty.sh to generate completely standalone certificates. 
*/ 
public class JettyHttpsForStackOverflow { 

    public static void main(final String... arguments) throws Exception { 
    System.setProperty("javax.net.debug", "all") ; 

    try { 
     if(arguments.length == 0 || "server".equals(arguments[ 0 ])) { 
     runServer() ; 
     } 
     if(arguments.length == 0 || "client".equals(arguments[ 0 ])) { 
     runClient() ; 
     } 
    } catch(Exception e) { 
     e.printStackTrace() ; 
     System.exit(1) ; // Avoids keeping the port open. 
    } 

    } 

    private static void runServer() throws Exception { 
    final KeyStore keyStore = loadKeystore() ; 
    final SSLContext sslContext = createSslContext(
     keyStore, 
     KEYPASS, 
     newTrustManagers(keyStore, CERTIFICATE_ALIAS) 
    ) ; 

    final SslContextFactory sslContextFactory = new SslContextFactory() { 
     @Override 
     public SSLEngine newSslEngine() { 
     return sslContext.createSSLEngine() ; 
     } 
     @Override 
     public SSLEngine newSslEngine(final String host, final int port) { 
     return sslContext.createSSLEngine(host, port) ; 
     } 
    } ; 
    sslContextFactory.setAllowRenegotiate(true) ; 
    sslContextFactory.setNeedClientAuth(false) ; 
    sslContextFactory.setWantClientAuth(false) ; 
    sslContextFactory.setKeyStorePath(keyStore.toString()) ; // Better logging. 
    sslContextFactory.setKeyStore(keyStore) ; 
    sslContextFactory.setCertAlias(CERTIFICATE_ALIAS) ; 
    sslContextFactory.setKeyManagerPassword(KEYPASS) ; 

    final SslSelectChannelConnector sslConnector = 
     new SslSelectChannelConnector(sslContextFactory) ; 
    sslConnector.setPort(PORT) ; 
    sslConnector.open() ; 

    final Server jettyServer = new Server() ; 
    jettyServer.addConnector(sslConnector) ; 

    jettyServer.start() ; 
    } 

    public static void runClient() throws Exception { 
    final KeyStore keyStore = loadTruststore() ; 

    final HttpClient httpClient = new HttpClient() ; 
    httpClient.getSslContextFactory().setKeyStore(keyStore) ; // Better logging. 
    httpClient.getSslContextFactory().setKeyStorePassword("storepwd") ; 
    httpClient.getSslContextFactory().setKeyManagerPassword(KEYPASS) ; 
    httpClient.setConnectorType(HttpClient.CONNECTOR_SELECT_CHANNEL) ; 
    httpClient.setConnectorType(HttpClient.CONNECTOR_SOCKET); 


    // Don't need that because shipping our own certificate in the truststore. 
    // Anyways, it blows when set to true. 
// httpClient.getSslContextFactory().setValidateCerts(false) ; 

    httpClient.start() ; 

    final ContentExchange contentExchange = new ContentExchange() ; 
    contentExchange.setURI(new URL("https://localhost:" + PORT).toURI()) ; 
    contentExchange.setTimeout(36_000_000) ; // Leave time for debugging. 
    httpClient.send(contentExchange) ; 
    contentExchange.waitForDone() ; 
    assert(contentExchange.getStatus() == ContentExchange.STATUS_COMPLETED) ; 
    } 

    private static SSLContext createSslContext(
     final KeyStore keyStore, 
     final String keypass, 
     final TrustManager[] trustManagers 
) { 
    try { 
     final KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509") ; 
     keyManagerFactory.init(keyStore, keypass == null ? null : keypass.toCharArray()) ; 
     final KeyManager[] keyManagers = keyManagerFactory.getKeyManagers() ; 
     final SecureRandom secureRandom = new SecureRandom() ; 

     final SSLContext sslContext = SSLContext.getInstance("TLS") ; 
     sslContext.init(
      keyManagers, 
      trustManagers, 
      secureRandom 
    ) ; 
     return sslContext ; 
    } catch(NoSuchAlgorithmException | UnrecoverableKeyException | KeyStoreException 
     | KeyManagementException e 
    ) { 
     throw new RuntimeException(e) ; 
    } 
    } 



    private static TrustManager[] newTrustManagers(
     final KeyStore keyStore, 
     final String certificateAlias 
) { 
    try { 
     final TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("SunX509") ; 
     trustManagerFactory.init(keyStore) ; 
     final TrustManager[] trustManagers ; 
     if(certificateAlias == null) { 
     trustManagers = trustManagerFactory.getTrustManagers() ; 
     } else { 
     final Certificate certificate = keyStore.getCertificate(certificateAlias) ; 
     final X509Certificate[] x509Certificates ; 
     if(certificate == null) { 
      x509Certificates = new X509Certificate[ 0 ] ; 
     } else { 
      x509Certificates = new X509Certificate[] { (X509Certificate) certificate } ; 
     } 
     trustManagers = new TrustManager[] { newX509TrustManager(x509Certificates) } ; 

     } 
     return trustManagers ; 
    } catch(KeyStoreException | NoSuchAlgorithmException e) { 
     throw new RuntimeException(e); 
    } 

    } 

    private static final TrustManager newX509TrustManager(final X509Certificate[] certificates) { 
    return new X509TrustManager() { 

     public X509Certificate[] getAcceptedIssuers() { 
     return certificates ; 
     } 

     public void checkClientTrusted(
      final X509Certificate[] certs, 
      final String authType 
    ) { ; } 

     public void checkServerTrusted(
      final X509Certificate[] certs, 
      final String authType 
    ) { ; } 
    } ; 
    } 


    public static KeyStore loadKeystore() 
     throws CertificateException, NoSuchAlgorithmException, KeyStoreException, IOException 
    { 
    return loadKeystore(KEYSTORE_RESOURCE_URL) ; 
    } 

    public static KeyStore loadTruststore() 
     throws CertificateException, NoSuchAlgorithmException, KeyStoreException, IOException 
    { 
    return loadKeystore(TRUSTSTORE_RESOURCE_URL) ; 
    } 

    public static KeyStore loadKeystore(final URL keystoreResourceUrl) 
     throws IOException, KeyStoreException, CertificateException, NoSuchAlgorithmException 
    { 
    try(final InputStream inputStream = keystoreResourceUrl.openStream()) { 
     final KeyStore keyStore = KeyStore.getInstance("JKS") ; 
     // We don't need the storepass for just reading one password-protected certificate 
     // of our own, or a trusted entry. 
     keyStore.load(inputStream, null) ; 
     return keyStore ; 
    } 
    } 


    private static final int PORT = 8443 ; 

    private static final String CERTIFICATE_ALIAS = "e1"; 

    private static final String KEYPASS = "Keypass"; 

    private static final URL KEYSTORE_RESOURCE_URL 
     = JettyHttpsForStackOverflow.class.getResource("my-keystore.jks") ; 

    private static final URL TRUSTSTORE_RESOURCE_URL 
     = JettyHttpsForStackOverflow.class.getResource("my-truststore.jks") ; 


} 

Odpowiedz

12

Okazuje się, że SslConnection w dziennikach Jetty-7.6.10.v20130312 nieprawidłowo, natomiast szyfrowanie dzieje się tak jak powinno.

Długa historia: po utworzeniu, SslConnection wyodrębnia początkowy obiekt SSLSession z SSLEngine i kontynuuje rejestrowanie za jego pomocą.Początkowa SSLSession ma szyfr SSL_NULL_WITH_NULL_NULL i jest to normalne, ponieważ handshake SSL jeszcze się nie wydarzył. Aktywacja -Djavax.net.debug=all pokazuje, że handshake naprawdę występuje, a interaktywne debugowanie pokazuje, że SSLEngine uaktualnia się do SSLSession z prawdziwym szyfrem. Problem polega tylko na tym, że Jetty jest SslConnection, który nadal loguje się z początkowym obiektem SSLSession. (Używa również wartości z początkowego SSLSession do alokowania buforów, ale to kolejny problem.)

Łata SslConnection do logowania z _engine.getSession() podaje oczekiwany wynik.

Epilog: Jetty 9 całkowicie zmienia jego numer SslConnection.

+3

Dobra praca dochodzeniowa. Głupi błąd. – EJP

+0

Otrzymuję to samo z Jetty 9.3.6 ..... – user2818782

1

Zobaczysz to pod co najmniej następujących okolicznościach:

  1. Zmieniłeś enabledCipherSuites obejmować wszystkie obsługiwane szyfrów. (Nie!)

  2. Uzgadnianie SSL nie zostało jeszcze zakończone.

+2

W powyższym przykładzie nie dotykam dostępnych zestawów szyfrów, ale robię to dla serwera produkcyjnego. Jest to tylko w celu ograniczenia listy, ponieważ SSL LABS sygnalizuje wiele z nich jako niepewne. Pozwoliłem, aby ten przykład działał cicho. Tworzy 404 z zaszyfrowaną wiadomością, więc myślę, że wystąpił handshake SSL. Co jest dziwne, że dostaję bardzo podstawowy przypadek, ale szukam go, nie widzę ludzi, którzy uderzają w ten sam problem. –