您可以包装默认的信任管理器并捕获此特定异常。这将是这样的:
class IgnoreClientUsageTrustManager extends X509TrustManager {
private final X509TrustManager origTrustManager;
public class IgnoreClientUsageTrustManager(X509TrustManager origTrustManager) {
this.origTrustManager = origTrustManager;
}
public checkClientTrusted(X509Certificate[] chain, String authType
throws IllegalArgumentException, CertificateException {
try {
this.origTrustManager.checkClientTrusted(chain, authType);
} catch (ValidatorException e) {
// Check it's that very exception, otherwise, re-throw.
}
}
// delegate the other methods to the origTrustManager
}
然后,使用该信任管理器创建一个SSLContext
并将其与您的服务器一起使用。
TrustManagerFactory tmf = TrustManagerFactory.getInstance(
TrustManagerFactory.getDefaultAlgorithm());
tmf.init((KeyStore)null);
TrustManager[] trustManagers = tmf.getTrustManagers();
for (int i = 0; i < trustManagers.length; i++) {
if (trustManagers[i] instanceof X509TrustManager) {
trustManagers[i] = IgnoreClientUsageTrustManager(trustManagers[i]);
}
}
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(... you keymanagers ..., trustManagers, null);
您应该从服务器密钥库初始化密钥管理器(正常情况下)。然后您应该能够使用HttpsServer
's HttpsConfigurator
设置SSLContext
(请参阅文档中的示例)。
但这种技术并不理想。
- 首先,
ValidatorException
是在一个sun.*
不属于公共 API 的包:此代码将特定于 Oracle/OpenJDK JRE。
- 其次,它依赖于最终实体检查的事实(验证密钥使用扩展)在其余的信任验证之后发生 http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/sun/security/validator/Validator.java#Validator.validate%28java.security.cert.X509Certificate%5b%5d%2Cjava.util.Collection%2Cjava.lang.Object%29(这使得忽略该异常是可以接受的,因为您不会以这种方式忽略其他更基本的检查)。
当然,您可以使用 Java 证书路径 API 重新实现您自己的验证,并仅忽略用于此目的的密钥用法。这需要更多的代码。
更一般地说,如果您想在没有正确扩展名的情况下使用 SSL/TLS 证书作为客户端证书,那么您无论如何都会尝试绕过规范。解决此问题的最佳方法是修改您的 CA 策略,如果它是内部 CA,那么这应该是可行的。服务器证书也设置 TLS 客户端扩展密钥使用是很常见的,即使对于大型 CA 也是如此。