首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >当使用自定义SSL时,X509KeyManager Java无法为SSL握手确定匹配的密码套件。

当使用自定义SSL时,X509KeyManager Java无法为SSL握手确定匹配的密码套件。
EN

Stack Overflow用户
提问于 2016-10-12 10:19:59
回答 2查看 5.9K关注 0票数 0

我正在使用Java7和JAX-WS2.2。

对于SOAP服务,我需要创建一个自定义X509KeyManager,以便为JKS中的每个连接客户端找到正确的证书。

但是,我已经很难让我的自定义密钥管理器运行。到目前为止,我使用的是默认的证书(从初始化的KeyManagerFactory中检索),它基本上可以工作--但是它当然没有选择正确的证书。因此,第一个想法是创建一个自定义X509KeyManager,它保存原始密钥管理器,只写出一些日志消息,但通常使用默认行为。

因为某种原因根本不起作用。无法建立SSL握手。在ClientHello之后,日志显示以下错误:

代码语言:javascript
复制
Allow unsafe renegotiation: false
Allow legacy hello messages: true
Is initial handshake: true
Is secure renegotiation: false
Thread-3, READ: TLSv1 Handshake, length = 149
*** ClientHello, TLSv1
RandomCookie:  GMT: 1476877930 bytes = { 207, 226, 8, 128, 40, 207, 47, 180, 146, 211, 157, 64, 239, 13, 201, 92, 158, 111, 108, 44, 223, 136, 193, 251, 33, 202, 7, 90 }
Session ID:  {}
Cipher Suites: [TLS_DHE_RSA_WITH_AES_128_CBC_SHA, TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA, SSL_RSA_WITH_RC4_128_SHA, TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, TLS_ECDHE_RSA_WITH_RC4_128_SHA, TLS_ECDH_ECDSA_WITH_RC4_128_SHA, TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, TLS_ECDH_RSA_WITH_RC4_128_SHA, TLS_EMPTY_RENEGOTIATION_INFO_SCSV, TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA, TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_RSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, SSL_RSA_WITH_RC4_128_MD5, TLS_DHE_DSS_WITH_AES_128_CBC_SHA, SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA, SSL_RSA_WITH_3DES_EDE_CBC_SHA]
Compression Methods:  { 0 }
Extension elliptic_curves, curve names: {secp256r1, sect163k1, sect163r2, secp192r1, secp224r1, sect233k1, sect233r1, sect283k1, sect283r1, secp384r1, sect409k1, sect409r1, secp521r1, sect571k1, sect571r1, secp160k1, secp160r1, secp160r2, sect163r1, secp192k1, sect193r1, sect193r2, secp224k1, sect239k1, secp256k1}
Extension ec_point_formats, formats: [uncompressed]
***
%% Initialized:  [Session-3, SSL_NULL_WITH_NULL_NULL]
Thread-3, fatal error: 40: no cipher suites in common
javax.net.ssl.SSLHandshakeException: no cipher suites in common
%% Invalidated:  [Session-3, SSL_NULL_WITH_NULL_NULL]
Thread-3, SEND TLSv1 ALERT:  fatal, description = handshake_failure
Thread-3, WRITE: TLSv1 Alert, length = 2
Thread-3, fatal: engine already closed.  Rethrowing javax.net.ssl.SSLHandshakeException: no cipher suites in common

据我所知,我根本没有移除任何密码套件!可以使用相同的证书进行SSL握手。

这是我的主要经理:

代码语言:javascript
复制
public class CustomX509KeyManager extends X509ExtendedKeyManager
{
   private static final Logger LOG = Logger.getLogger( CustomX509KeyManager.class );
   private final X509KeyManager originalKeyManager;

   public CustomX509KeyManager(final X509KeyManager keyManager)
   {
      super();
      this.originalKeyManager = keyManager;
   }

   @Override
   public String chooseServerAlias(final String keyType, final Principal[] issuers,
     final Socket socket)
   {
      final String serverAliases=
            this.originalKeyManager.chooseServerAlias( keyType, issuers, socket );
      CustomX509KeyManager.LOG.info( "chooseServerAlias() " + serverAliases );
      return serverAliases;
   }

   ...
}

其他方法(这里没有显示)也只是在originalKeyManager中调用相应的方法。在测试期间,我从未看到来自chooseServerAlias()方法的日志消息。

它是从getSslContext()方法的另一个类初始化的:

代码语言:javascript
复制
private KeyManager[] getKeyManagers(final KeyManagerFactory keyManagerFactory)
{
   final KeyManager[] keyManagers = keyManagerFactory.getKeyManagers();

   // replace any X509KeyManager with our own implementation
   for ( int i = 0; i < keyManagers.length; i++ )
   {
      if ( keyManagers[i] instanceof X509KeyManager )
      {
         keyManagers[i] =
               new CustomX509KeyManager( ( X509KeyManager ) keyManagers[i] );
      }
   }

   return keyManagers;
}

public SSLContext getSslContext()
{
  // create the KeyStore and load the JKS file
  final KeyStore keyStore = createKeyStore();

  // initialize key and trust manager factory
  final KeyManagerFactory keyManagerFactory =
        KeyManagerFactory.getInstance( KeyManagerFactory.getDefaultAlgorithm() );
  keyManagerFactory.init( keyStore, "changeit".toCharArray() );
  final TrustManagerFactory trustManagerFactory =
        TrustManagerFactory.getInstance( TrustManagerFactory.getDefaultAlgorithm() );
  trustManagerFactory.init( keyStore );

  // initialize the SSL context
  final SSLContext sslContext = SSLContext.getInstance( "TLS" );
  // sslContext.init( keyManagerFactory.getKeyManagers(),
  //      trustManagerFactory.getTrustManagers(), new SecureRandom() );
  sslContext.init( getKeyManagers( keyManagerFactory ),
        trustManagerFactory.getTrustManagers(), new SecureRandom() );
  return sslContext;
}

注释行显示默认密钥管理器的原始用法。

你知道怎么回事吗?为什么使用我的CustomX509KeyManager的行为与默认的密钥管理器如此不同,以至于无法进行握手?对于默认的密钥管理器,对TLS_DHE_RSA_WITH_AES_128_CBC_SHA算法进行加密协商,自定义密钥管理器也可以使用该算法,但由于某些原因没有选择。

更新1

我现在正试图以客户端模式连接openssl到服务器,但是服务器遇到了使用SSL的相同问题。当我使用TLS协议时,那么附加的错误消息

不支持扩展type_35,数据:

就会出现。

更新2

我可以确认,上述关于不受支持的扩展的通知在成功握手时也会出现,因此这是一个错误的跟踪。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2016-10-20 09:25:03

经过几天的尝试和错误,我终于发现了我的错误!

在Java7中,自定义密钥管理器应该扩展X509ExtendedKeyManager,这迫使您实现接口X509KeyManager的五种方法。然而,类X509ExtendedKeyManager中有两个额外的方法没有被声明为抽象,但是为了正确使用,必须覆盖:

  • chooseEngineClientAlias(String[], Principal[], SSLEngine)
  • chooseEngineServerAlias(String, Principal[], SSLEngine)

在通过将调用委托给我的originalKeyManager (也是X509ExtendedKeyManager类型)来覆盖和实现这些方法之后,SSL握手终于成功了。

票数 6
EN

Stack Overflow用户

发布于 2016-10-19 21:22:35

代码片段中的任何地方似乎都没有读取密钥文件。这就是SSL_NULL_WITH_NULL_NULL的原因。我建议您实现X509KeyManager并在构造函数中读取文件,这样就可以使用字母来选择适当的键。这一行中的一些内容(并不是为了简短的答案而描述所有必需的方法):

代码语言:javascript
复制
public class CustomX509KeyManager implements X509KeyManager
{
   private final KeyStore keyStore;
   private final String alias;
   private final char[] password;

   public CustomX509KeyManager(final String keyStoreFile, final char[] password, final String alias)
    throws IOException, GeneralSecurityException
   {
       this.alias = alias;
       this.password = password;
       synchronized(keyStoreFile)
       {
          InputStream stream = new FileInputStream(keyStoreFile);
          keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
          keyStore.load(stream, password);
          stream.close();
       }
   }

   @Override
   public PrivateKey getPrivateKey(String alias)
   {
       try {
           return (PrivateKey) keyStore.getKey(alias, password);
       } catch (Exception e) {
            e.printStackTrace();
            return null;
       }
    }

    @Override
    public X509Certificate[] getCertificateChain(String alias)
    {
        try {
            java.security.cert.Certificate[] certs = keyStore.getCertificateChain(alias);
            if (certs == null || certs.length == 0)
                return null;
            X509Certificate[] x509 = new X509Certificate[certs.length];
            for (int i = 0; i < certs.length; i++)
                x509[i] = (X509Certificate)certs[i];
            return x509;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }          
    }

}

然后把它当作

代码语言:javascript
复制
sslContext.init(new X509KeyManager[] { 
                    new CustomX509KeyManager(keyStoreFile, 
                        keyStorePass.toCharArray(), alias) }, null, null);
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/39996178

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档