2013-08-06 5 views
6

Ich benutze Jetty mit HTTPS und ein gültiges Zertifikat, und ich bin nicht sicher, es richtig zu machen, weil Cipher-Suite erscheint SSL_NULL_WITH_NULL_NULL in Server-Logs. Client-Protokolle sehen jedoch gut aus.SSL_NULL_WITH_NULL_NULL Cipher-Suite in in Jetty-Logs

Die lange Geschichte: Ich füge ein Java-Beispiel an, das Jetty-7.6.10 und zwei Skripte erwartet, um Keystore und Truststore zu erstellen.

JettyHttpsForStackOverflow Läuft Client und Server zusammen oder separat, um die Protokolle zu de-installieren.

Das Skript create-chains.sh erstellt den Keystore und den Truststore. Der Schlüsselspeicher enthält eine Kette, die von einer Stammzertifizierungsstelle abgeschlossen wird, die von einem vorübergehenden Schlüsselspeicher generiert wird. Es repliziert einen realen Fall mit einer Zertifizierungsstelle und Zwischenzertifikaten.

Das Skript create-single-autosigned.sh erstellt auch den Keystore und den Truststore, jedoch mit einem selbstsignierten Zertifikat.

Bitte beachten Sie, dass SSL_NULL_WITH_NULL_NULL als Cipher-Suite des Servers mit beiden Zertifikatsketten angezeigt wird.

Ich denke, es gibt kein Problem mit dem Server-Domänennamen. Ich bekomme das gleiche Problem mit einem Server, der auf einem Rechner mit einem Domänennamen läuft, der dem definierten Namen in einem ordnungsgemäß signierten Zertifikat entspricht. SSLLab hat bestätigt, dass SSL auf meinem Server funktioniert (Klasse B), und Google Chrome verbindet glücklich.

Ich denke, es gibt kein Problem mit Jetty-Client. Wie ich es benutze, ruft es nur die SSLContextFactory Ich bin eingerichtet, um eine SSLSocket zu erstellen. Erstaunlicherweise scheint TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA in den Jetty-Client-Protokollen die verwendete Cipher-Suite zu sein.

Ist es normal, SSL_NULL_WITH_NULL_NULL in Jetty Server-Logs zu bekommen? Wenn nicht, wie bekomme ich das Richtige?

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") ; 


} 

Antwort

12

Es stellt sich heraus, dass SslConnection in Jetty-7.6.10.v20130312 Protokolle falsch, während Chiffrierung geschieht, wie es sollte.

Lange Geschichte: wenn SslConnectionSslConnection erstellt wird, extrahiert das ursprüngliche SSLSession Objekt aus der SSLEngine und protokolliert weiterhin damit.Initial SSLSession hat eine SSL_NULL_WITH_NULL_NULL Verschlüsselung und das ist normal, weil SSL-Handshake noch nicht passiert ist. Aktivieren -Djavax.net.debug=all zeigt, dass Handshake wirklich auftritt, und interaktives Debugging zeigt, dass SSLEngine Upgrades auf eine SSLSession mit einer echten Chiffre. Das Problem ist nur Jettys SslConnection, die immer noch mit dem ursprünglichen Objekt SSLSession loggt. (Es verwendet auch Wert von den anfänglichen SSLSession Puffer zuweisen, aber das ist ein anderes Problem.)

Patchen SslConnection für die Anmeldung mit _engine.getSession() ergibt erwartetes Ergebnis.

Epilog: Jetty 9 schreibt seine SslConnection komplett um.

+3

Gute Ermittlungsarbeit. Dummer Bug. – EJP

+0

Ich bekomme das gleiche mit Jetty 9.3.6 ..... – user2818782

1

Sie finden diese unter sehen zumindest die folgenden Umstände:

  1. Sie die enabledCipherSuites geändert haben alle unterstützten Chiffriersätze aufzunehmen. (Do not!)

  2. Der SSL-Handshake ist noch nicht abgeschlossen.

+2

Im obigen Beispiel habe ich nicht die verfügbaren Cipher Suites berührt, aber ich für den Produktionsserver. Dies dient nur dazu, die Liste zu beschränken, da SSL LABS viele davon als unsicher kennzeichnet. Ich ließ das Beispiel ruhig laufen. Es erzeugt eine 404 mit einer verschlüsselten Nachricht, also denke ich, dass SSL-Handshake aufgetreten ist. Was ist seltsam ist, dass ich einen sehr einfachen Fall bekomme, aber googlen herum Ich sehe keine Leute das gleiche Problem zu treffen. –