6

我正在使用以下代码调用远程 https URL:

   def inputStream = new URL("https://somewebsite.com").openStream()

这在我的本地机器上效果很好,但是当我部署到服务器时,我得到以下异常:

java.security.cert.CertPathValidatorException: CA key usage check failed: keyCertSign bit is not set

这个错误的原因是什么,什么可以解释它在一台机器上而不是另一台机器上工作?


更新


我正在生产中运行 Ubuntu 服务器并在本地 Mac 上开发。我尝试访问的站点(我们称之为 peopleware.com)具有以下证书信息:

  1. AddTrust 外部 CA 根
  2. UTN-USERFirst-Hardware
  3. peopleware.com

我尝试从浏览器中保存 .cer 文件并将它们安装到 /etc/ssl/certs/java/castore 的密钥库中

4

1 回答 1

5

我假设您正在谈论来自 UTN-USERFirst-Hardware 的证书:

-----BEGIN CERTIFICATE----- 
MIIEdDCCA1ygAwIBAgIQRL4Mi1AAJLQR0zYq/mUK/TANBgkqhkiG9w0BAQUFADCB
lzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug
Q2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho
dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xHzAdBgNVBAMTFlVUTi1VU0VSRmlyc3Qt
SGFyZHdhcmUwHhcNOTkwNzA5MTgxMDQyWhcNMTkwNzA5MTgxOTIyWjCBlzELMAkG
A1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0eTEe
MBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8v
d3d3LnVzZXJ0cnVzdC5jb20xHzAdBgNVBAMTFlVUTi1VU0VSRmlyc3QtSGFyZHdh
cmUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCx98M4P7Sof885glFn
0G2f0v9Y8+efK+wNiVSZuTiZFvfgIXlIwrthdBKWHTxqctU8EGc6Oe0rE81m65UJ
M6Rsl7HoxuzBdXmcRl6Nq9Bq/bkqVRcQVLMZ8Jr28bFdtqdt++BxF2uiiPsA3/4a
MXcMmgF6sTLjKwEHOG7DpV4jvEWbe1DByTCP2+UretNb+zNAHqDVmBe8i4fDidNd
oI6yqqr2jmmIBsX6iSHzCJ1pLgkzmykNRg+MzEk0sGlRvfkGzWitZky8PqxhvQqI
DsjfPe58BEydCl5rkdbux+0ojatNh4lz0G6k0B4WixThdkQDf2Os5M1JnMWS9Ksy
oUhbAgMBAAGjgbkwgbYwCwYDVR0PBAQDAgHGMA8GA1UdEwEB/wQFMAMBAf8wHQYD
VR0OBBYEFKFyXyYbKJhDlV0HN9WFlp1L0sNFMEQGA1UdHwQ9MDswOaA3oDWGM2h0
dHA6Ly9jcmwudXNlcnRydXN0LmNvbS9VVE4tVVNFUkZpcnN0LUhhcmR3YXJlLmNy
bDAxBgNVHSUEKjAoBggrBgEFBQcDAQYIKwYBBQUHAwUGCCsGAQUFBwMGBggrBgEF
BQcDBzANBgkqhkiG9w0BAQUFAAOCAQEARxkP3nTGmZev/K0oXnWO6y1n7k57K9cM
//bey1WiCuFMVGWTYGufEpytXoMs61quwOQt9ABjHbjAbPLPSbtNk28Gpgoiskli
CE7/yMgUsogWXecB5BKV5UU0s4tpvc+0hY91UZ59Ojg6FEgSxvunOxqNDYJAB+gE
CJChicsZUN/KHAG8HQQZexB2lzvukJDKxA4fFm517zP4029bHpbj4HR3dHuKom4t
3XbWOTCC8KucUvIqx69JXn7HaOWCgchqJ/kniCrVWFCVH/A7HFe7fRQ5YiuayZSS
KqMiDP+JJn1fIytH1xUdqWqeUQ0qUZ6B+dQ7XnASfxAynB67nfhmqA== 
-----END CERTIFICATE-----

在人类可读的版本中:

Version: 3 (0x2)
Serial Number:
    44:be:0c:8b:50:00:24:b4:11:d3:36:2a:fe:65:0a:fd
Signature Algorithm: sha1WithRSAEncryption
Issuer: C=US, ST=UT, L=Salt Lake City, O=The USERTRUST Network, OU=http://www.usertrust.com, CN=UTN-USERFirst-Hardware
Validity
    Not Before: Jul  9 18:10:42 1999 GMT
    Not After : Jul  9 18:19:22 2019 GMT
Subject: C=US, ST=UT, L=Salt Lake City, O=The USERTRUST Network, OU=http://www.usertrust.com, CN=UTN-USERFirst-Hardware
Subject Public Key Info:
    [...]
X509v3 extensions:
    X509v3 Key Usage: 
        Digital Signature, Non Repudiation, Certificate Sign, CRL Sign
    X509v3 Basic Constraints: critical
        CA:TRUE
    X509v3 Subject Key Identifier: 
        A1:72:5F:26:1B:28:98:43:95:5D:07:37:D5:85:96:9D:4B:D2:C3:45
    X509v3 CRL Distribution Points: 

        Full Name:
          URI:http://crl.usertrust.com/UTN-USERFirst-Hardware.crl

    X509v3 Extended Key Usage: 
        TLS Web Server Authentication, IPSec End System, IPSec Tunnel, IPSec User

本质上,我们这里有一个 CA 证书,其中包含X509v3 Key UsageX509v3 Extended Key Usage.

但是,RFC 3280 对扩展密钥使用扩展有以下说明

一般来说,这个扩展只会出现在终端实体证书中。

这对于 CA 证书来说并不是很好,但后来,同一部分也这样说:

如果证书同时包含密钥使用扩展和扩展密钥使用扩展,则必须独立处理这两个扩展,并且证书必须仅用于与这两个扩展一致的目的。如果没有与两个扩展一致的目的,则证书不得用于任何目的。

此 RFC 中此证书中唯一的扩展密钥使用扩展是TLS Web 服务器身份验证

   id-kp-serverAuth             OBJECT IDENTIFIER ::= { id-kp 1 }
   -- TLS WWW server authentication
   -- Key usage bits that may be consistent: digitalSignature,
   -- keyEncipherment or keyAgreement

当然,这与keyCertSignRFC 3280(和 RFC 5280)不一致。(我也怀疑任何 IPSec 扩展都兼容keyCertSign)。这使得该证书无法颁发证书(对于 CA 证书不是很有用)。

我会使用此证书联系网站,要求他们联系他们的 CA(UTN-USERFirst-Hardware,显然是 Comodo)并要求他们解决这个问题。我必须说,靠这些 RFC 赚钱的人看起来并不好。

当然,这可能需要一段时间,并且不会解决您眼前的问题。

我想我已经在其他中间 CA 证书中看到了这个主题 DN(UTN-USERFirst-Hardware),所以上面的那个可能不是你正在使用的那个。

您可能能够做的(假设您能够手动验证服务器证书本身,尽管存在这些问题)是使用一个SSLContext并且TrustManager特别限制使用该证书,用于此连接。这可能会阻止证书路径算法尝试查找颁发者证书并陷入此问题。

编辑:

以下是有关此解决方法的更多详细信息(仍应确保您的连接安全)。

  • 用 Firefox 连接到这个网站。
  • 单击蓝色/绿色条并选择“更多信息...”
  • 安全 -> 查看证书 -> 详细信息
  • 从顶部的列表中选择服务器证书,然后选择“导出...”
  • 在某处相同的 PEM 文件。

用于keytool创建新的密钥库(选择信任该证书并选择合理的密码):

keytool -importcert -keystore example.jks -file example.pem

然后,使用这个 Java 代码,移植到 Groovy 应该不会太难:

TrustManagerFactory tmf = TrustManagerFactory
    .getInstance(TrustManagerFactory.getDefaultAlgorithm());
KeyStore ks = KeyStore.getInstance("JKS");
FileInputStream fis = new FileInputStream("/.../example.jks");
ks.load(fis, null);
// or ks.load(fis, "thepassword".toCharArray());
fis.close();

tmf.init(ks);

SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, tmf.getTrustManagers(), null);

URL url = new URL("https://somewebsite.com");
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
conn.setSSLSocketFactory(sslContext.getSocketFactory());

InputStream is = conn.getInputStream();
于 2012-04-24T18:10:52.000 回答