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

5
我正在尝试使用内置类com.sun.net.httpserver.HttpsServer在Java(6)中构建自定义HTTPS服务器。在不需要客户端认证的情况下,一切顺利。但是,在需要客户端认证时,服务器的SSL调试中会出现以下异常:sun.security.validator.ValidatorException: Netscape cert type does not permit use for SSL client
我正在使用我们内部CA颁发的证书,该证书用于我们所有内部应用程序。我检查了证书详细信息,并发现类型为“SSL服务器”(下面引用了详细信息)。由于我们的政策是为所有内部应用程序使用“SSL服务器”类型,因此更改证书很困难。由于我想将服务器证书用于客户端,我不认为这是一个安全问题。
我正在寻找一种方法来禁用Java中的此约束检查。有人遇到过这个问题并解决了吗?任何帮助都将不胜感激。 最好的问候, Arun
Owner: CN=myapp, OU=mygroup, O=mycompany

Issuer: O=MYCA

Serial number: 4cc8c1da

Valid from: Mon Jan 10 13:46:34 EST 2011 until: Thu Jan 10 14:16:34 EST 2013

Certificate fingerprints:
         MD5:  8C:84:7F:7A:40:23:F1:B5:81:CD:F9:0C:27:16:69:5E
         SHA1: 9B:39:0B:2F:61:83:52:93:D5:58:E5:43:13:7A:8F:E1:FD:AC:98:A4
         Signature algorithm name: SHA1withRSA
         Version: 3

Extensions:

[1]: ObjectId: 2.5.29.16 Criticality=false
PrivateKeyUsage: [
From: Mon Jan 10 13:46:34 EST 2011, To: Wed Jul 11 21:16:34 EDT 2012]

[2]: ObjectId: 2.5.29.15 Criticality=false
KeyUsage [
  DigitalSignature
  Key_Encipherment
]

[3]: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: D3 47 35 9B B4 B7 03 18   C6 53 2C B0 FE FD 49 D8  .G5......S,...I.
0010: D0 FB EE 15                                        ....
]
]

[4]: ObjectId: 1.2.840.113533.7.65.0 Criticality=false

[5]: ObjectId: 2.5.29.31 Criticality=false
CRLDistributionPoints [
  [DistributionPoint:
     [CN=CRL413, O=SWIFT]
]]

[6]: ObjectId: 2.5.29.19 Criticality=false
BasicConstraints:[
  CA:false
  PathLen: undefined
]

****[7]: ObjectId: 2.16.840.1.113730.1.1 Criticality=false
NetscapeCertType [
   SSL server
]****

[8]: ObjectId: 2.5.29.35 Criticality=false
AuthorityKeyIdentifier [
KeyIdentifier [
0000: 8F AF 56 BC 80 77 A3 FD   9E D2 89 83 98 FE 98 C7  ..V..w..........
0010: 20 65 23 CC                                         e#.
]

]
1个回答

1

您可以封装默认的信任管理器并捕获这个特定的异常。代码如下:

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);

您应该从服务器密钥库(与平常一样)初始化您的键管理器。然后,您应该能够使用HttpsConfigurator设置SSLContext(请参阅文档中的示例)。

虽然这种技术不是理想的。

  • 首先,ValidatorExceptionsun.*包中,不是公共API的一部分:此代码将专门针对Oracle / OpenJDK JRE编写。
  • 其次,它依赖于检查的最终实体(验证密钥使用扩展名)在信任验证的其余部分之后发生(这使得忽略该异常是可以接受的,因为您不会以这种方式忽略其他更基本的检查)。

当然,您也可以重新实现自己的验证,使用Java证书路径API,并仅出于此目的忽略密钥使用。这需要更多的代码。

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

非常感谢。它运行得非常好。我同意理想的解决方案是更新CA策略,但如果我不构建旁路解决方案,那么在数百个系统上升级我的软件将需要立即更新证书。 - Arun
2
这听起来像是一种肮脏的解决方案,将永远持续下去。 - sjas
@sjas 是的,我只希望阅读此内容的人能够认真阅读文本,而不是简单地复制粘贴。 - Bruno

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接