2013-08-06 3 views
6

Я использую Jetty с HTTPS и действительным сертификатом, и я не уверен, что все правильно, потому что набор шифров выглядит SSL_NULL_WITH_NULL_NULL в журналах сервера. Однако журналы клиентов выглядят неплохо.SSL_NULL_WITH_NULL_NULL набор шифров в журналах Jetty

Длинная история. Я прикрепляю образец Java, ожидающий Jetty-7.6.10, и два сценария для создания хранилища ключей и доверия.

JettyHttpsForStackOverflow запускает клиент и сервер вместе или отдельно для дезактивации журналов.

Сценарий create-chains.sh создает хранилище ключей и доверительный магазин. Хранилище ключей содержит цепочку, завершающуюся корневым центром сертификации, созданным из временного хранилища ключей. Он реплицирует реальный случай с сертификационным центром и промежуточными сертификатами.

Сценарий create-single-autosigned.sh создает хранилище ключей и доверительное хранилище тоже, но с самозаверяющим сертификатом.

Обратите внимание, что SSL_NULL_WITH_NULL_NULL отображается как набор шифров сервера с обеих цепочками сертификатов.

Я думаю, что нет проблем с доменным именем сервера. Я получаю ту же проблему с сервером, запущенным на машине с доменным именем, соответствующим различающемуся имени, в правильно подписанном сертификате. SSLLab подтвердил, что SSL на моем сервере отлично работает (класс B), и Google Chrome подключается к счастью.

Я думаю, что нет проблем с клиентом Jetty. Поскольку я использую его, он просто вызывает SSLContextFactory, который я настраиваю для создания SSLSocket. Удивительно, что в журналах клиентов Jetty TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA представляется используемым набором шифров.

Нормально ли получить SSL_NULL_WITH_NULL_NULL в журналах сервера Jetty? Если нет, как правильно это сделать?

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


} 

ответ

12

Оказывается, SslConnection в журналах Jetty-7.6.10.v20130312 неправильно, в то время как шифрации происходит, как это должно быть.

Длинная история: при создании SslConnection извлекает исходный объект SSLSession из SSLEngine и продолжает вести журнал.Начальный SSLSession имеет шифр SSL_NULL_WITH_NULL_NULL, и это нормально, потому что SSL-квитирование еще не произошло. Активация -Djavax.net.debug=all показывает, что рукопожатие действительно происходит, а интерактивная отладка показывает, что SSLEngine обновляется до SSLSession с реальным шифром. Проблема заключается только в том, что Jetty SslConnection, который все еще регистрируется с начальным SSLSession объектом. (Он также использует значения от начального SSLSession для выделения буферов, но это еще одна проблема.)

Патч SslConnection для регистрации с _engine.getSession() дает ожидаемый результат.

Эпилог: Jetty 9 полностью переписывает свой SslConnection.

+3

Хорошая исследовательская работа. Глупая ошибка. – EJP

+0

Я получаю то же самое с Jetty 9.3.6 ..... – user2818782

1

Вы увидите это при по меньшей мере, следующие обстоятельства:

  1. Вы изменили enabledCipherSuites включить все поддерживаемые шифры. (Не надо!)

  2. Подтверждение SSL еще не завершено.

+2

В приведенном выше примере я не касаюсь доступных наборов шифров, но я делаю это для производственного сервера. Это только для ограничения списка, поскольку SSL LABS флагами многих из них является небезопасным. Я пропустил пример спокойно. Он производит 404 с зашифрованным сообщением, поэтому я _think_, что SSL-квитирование действительно произошло. Странно, что я получаю очень простой случай, но прихожу к нему, я вижу, что люди не сталкиваются с одной и той же проблемой. –

Смежные вопросы