[英]JBWEB003006: Handshake failed: java.io.IOException: JBWEB002042: SSL handshake failed, cipher suite in SSL Session is SSL_NULL_WITH_NULL_NULL
[英]SSL_NULL_WITH_NULL_NULL cipher suite in in Jetty logs
我正在使用帶有HTTPS和有效證書的Jetty,我不確定是否正確,因為密碼套件在服務器日志中似乎是SSL_NULL_WITH_NULL_NULL
。 但是,客戶端日志看起來不錯。
長話故事:我正在附加一個期望Jetty-7.6.10的Java樣本和兩個用於創建密鑰庫和信任庫的腳本。
JettyHttpsForStackOverflow
一起或單獨運行客戶端和服務器以對日志進行分離。
create-chains.sh
腳本創建密鑰庫和信任庫。 密鑰庫包含以從臨時密鑰庫生成的根證書頒發機構結束的鏈。 它使用證書頒發機構和中間證書復制真實案例。
create-single-autosigned.sh
腳本create-single-autosigned.sh
創建密鑰庫和信任庫,但使用自簽名證書。
請注意, SSL_NULL_WITH_NULL_NULL
顯示為服務器的密碼套件,同時包含兩個證書鏈。
我認為服務器域名沒有問題。 對於在正確簽名的證書中具有與專有名稱匹配的域名的計算機上運行的服務器,我遇到了同樣的問題。 SSLLab確認我服務器上的SSL工作正常(B級),谷歌Chrome連接愉快。
我認為Jetty客戶端沒有問題。 當我使用它時,它只是調用我正在設置的SSLContextFactory
來創建一個SSLSocket
。 令人驚訝的是,在Jetty客戶端日志中, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
似乎是正在使用的密碼套件。
在Jetty服務器日志中獲取SSL_NULL_WITH_NULL_NULL
是正常的嗎? 如果沒有,怎么做那件事吧?
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" ) ;
}
事實證明, SslConnection
在碼頭,7.6.10.v20130312日志錯誤,同時加密發生,因為它應該。
SslConnection
:在創建時, SslConnection
從SSLEngine
提取初始SSLSession
對象並繼續使用它進行記錄。 初始SSLSession
具有SSL_NULL_WITH_NULL_NULL
密碼,這是正常的,因為SSL握手尚未發生。 激活-Djavax.net.debug=all
顯示握手確實發生,並且交互式調試顯示SSLEngine
使用真實密碼升級到SSLSession
。 問題只是Jetty的SslConnection
仍然使用初始SSLSession
對象記錄。 (它還使用來自初始SSLSession
值來分配緩沖區,但這是另一個問題。)
使用_engine.getSession()
修補SslConnection
以進行日志記錄可獲得預期結果。
結語:Jetty 9完全重寫了它的SslConnection
。
至少在以下情況下你會看到這個:
您已修改enabledCipherSuites
以包含所有受支持的密碼套件。 (別!)
SSL握手尚未完成。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.