如何在Java6中禁用约束检查(Netscape证书类型)?

如何在Java6中禁用约束检查(Netscape证书类型)?,java,ssl,certificate,x509,jsse,Java,Ssl,Certificate,X509,Jsse,我正在尝试使用内置类com.sun.net.httpserver.HttpsServer在Java(6)中构建一个自定义HTTPS服务器。在我需要客户端身份验证之前,它可以正常工作。此时,它失败,服务器上的SSL调试出现以下异常 sun.security.validator.validator异常:Netscape证书类型不允许用于SSL客户端 我使用我们内部CA颁发的证书,该证书用于我们内部的所有应用程序。我检查了证书的详细信息,发现类型是“SSL服务器”(下面引用了详细信息)。由于我们的策略

我正在尝试使用内置类com.sun.net.httpserver.HttpsServer在Java(6)中构建一个自定义HTTPS服务器。在我需要客户端身份验证之前,它可以正常工作。此时,它失败,服务器上的SSL调试出现以下异常

sun.security.validator.validator异常:Netscape证书类型不允许用于SSL客户端

我使用我们内部CA颁发的证书,该证书用于我们内部的所有应用程序。我检查了证书的详细信息,发现类型是“SSL服务器”(下面引用了详细信息)。由于我们的策略是对所有内部应用程序使用“SSL服务器”类型,因此很难更改证书。因为我想为客户端使用服务器证书,所以我不认为这是一个安全问题

我要寻找的是一种在Java中禁用此约束检查的方法。有人遇到过这个问题并解决了这个问题吗?非常感谢您的帮助

致以最良好的祝愿, 阿伦


您可以包装默认的信任管理器并捕获此特定异常。这将是以下几点:

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);
TrustManagerFactory tmf=TrustManagerFactory.getInstance(
TrustManagerFactory.getDefaultAlgorithm());
init((密钥库)null);
TrustManager[]TrustManager=tmf.getTrustManager();
for(int i=0;i
您应该从服务器密钥库初始化密钥管理器(正常情况下)。然后,您应该能够使用
HttpsServer
httpscoconfigurator
设置
SSLContext
(请参阅文档中的示例)

不过,这种技术并不理想

  • 首先,
    ValidatorException
    在一个
    sun.*
    包中,该包不是公共API的一部分:该代码特定于Oracle/OpenJDK JRE
  • 其次,它依赖于最终实体已检查(验证密钥使用扩展)这一事实(这使得忽略该异常是可以接受的,因为您不会以这种方式忽略其他更基本的检查)
当然,您可以使用Java证书路径API重新实现您自己的验证,并且为此只忽略密钥的使用。这需要更多的代码


更一般地说,如果您想在SSL/TLS证书没有正确的扩展名时将其用作客户端证书,那么您无论如何都会尝试绕过规范。最好的解决办法是修改您的CA策略,如果它是内部CA,那么这应该是可行的。服务器证书设置TLS客户端扩展密钥使用也是很常见的,即使是大型CA。

非常感谢。它工作得很好。我同意理想的解决方案是更新CA策略,但如果我不构建旁路解决方案,则在数百个系统上升级我的软件将需要立即更新证书。这听起来像是一个肮脏的修复程序,将永远持续。@sjas Yes,我只希望读过这篇文章的人都能读到这篇文章,而不是简单地复制和粘贴。
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);