1

前言

OpenJDK 11.0.2 2019-01-15

我在使用 TLSv1.2 连接到 Jetty (9.4.14.v20151114) 服务器时使用相互身份验证,密码套件 TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384。

我的客户端使用标准的 Java TLS 机制:

javax.net.ssl.SSLContext sslContext = SSLContext.getInstance("TLS");
javax.net.ssl.KeyManager keyManager = ...;
javax.net.ssl.TrustManager trustManager = ...;
sslContext.init(new KeyManager[] { keyManager }, new TrustManager[] { trustManager }, null);
javax.net.ssl.SSLSocketFactory socketFactory = sslContext.getSocketFactory();
// ... and so on

我的客户使用智能卡支持的 PKCS11 密钥库:

Provider pkcs11Provider = Security.getProvider("SunPKCS11").configure(configFile);
Security.addProvider(pkcs11Provider);
CallbackHandlerProtection callbackHandler = ...;
KeyStore keyStore = KeyStore.Builder.newInstance("PKCS11", provider, callbackHandler).getKeyStore();

问题

Jetty 会发送很长的支持签名算法列表,用于客户端身份验证。客户端通过调用来决定使用其中的哪一个:

sun.security.ssl.SignatureScheme.getPreferableAlgorithm(List<SignatureScheme> schemes, PrivateKey signingKey, ProtocolVersion version);

这将选择签名方案 RSASSA-PSS。

后来在方法

java.security.Signature$Delegate.chooseProvider(int type, java.security.Key key, SecureRandom sr)

客户端选择支持所选 RSASSA-PSS 算法的 java.security.Provider$Service。这个 RSASSA-PSS 服务的提供者是 sun.security.rsa.SunRsaSign。密钥库中使用的 SunPKCS11 提供程序不提供 RSASSA-PSS 服务。

问题是这个 SunRsaSign 提供者的服务不支持用于签名的基于 PKCS11 的私钥,我得到以下堆栈跟踪(手工复制,小心拼写错误):

java.security.InvalidKeyException: No installed provider supports this key: sun.security.pkcs11.P11Key$P11PrivateKey
    at java.base/java.security.Signature$Delegate.chooseProvider(Signature.java:1163)
    ...

问题

似乎选定的签名算法是由不支持客户端身份验证中使用的 PrivateKey 的 java.security.Provider 实现的。

有没有办法解决这个问题?我可以让客户端以某种方式选择与 PKCS11 密钥库(例如 RSA_PKCS1_SHA512)一起使用的客户端签名算法吗?我可以将 Jetty 服务器配置为不建议不起作用的客户端签名算法吗?

编辑

Jetty SSLContextFactory 配置(可能包含拼写错误):

<New id="sslContextFactory" class="org.eclipse.jetty.util.ssl.SslContextFactory">
    <Set name="keyStorePath">keystore.jks</Set>
    <Set name="keyStorePassword">...</Set>
    <Set name="trustStorePath">truststore.jks</Set>
    <Set name="needClientAuth">true</Set>
    <Set name="includeProtocols">
        <Array type="java.lang.String">
            <Item>TLSv1.2</Item>
        </Array>
    </Set>
    <Set name="includeCipherSuites">
        <Array type="java.lang.String">
            <Item>TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384</Item>
        </Array>
    </Set>
</New>
4

0 回答 0