证书KeyUsage和Cipher加密模式

时间:2012-07-15 04:16:05

标签: certificate encryption

我正在尝试使用cxf和sprint加密传出的SOAP标头。由于我使用spring配置,我最终使用outInterceptor为我的客户端:

 <bean id="encryptOutInterceptor" class="org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor">

  <constructor-arg>
     <map>
        <entry key="action" value="UsernameToken Timestamp Encrypt" />
        <entry key="passwordCallbackRef" value-ref="passwordCallback" />
        <entry key="user" value="mykey" />
        <entry key="encryptionPropFile" value="keystore.properties" />

     </map>
  </constructor-arg>
  </bean>

`

因此最终调用

`org.apache.ws.security.message.WSSecEncryptedKey.prepareInternal()`  

方法,我通过调试器到源:

try {
        **cipher.init(Cipher.ENCRYPT_MODE, remoteCert);**
    } catch (InvalidKeyException e) {
        throw new WSSecurityException(
            WSSecurityException.FAILED_ENCRYPTION, null, null, e
        );
    }

此时它会抛出:“密钥用法无效”例外。

我最后编写了一个测试java类,我手动读取证书,然后尝试使用 Cipher.ENCRYPT_MODE 参数初始化Cipher, 我最终得到了相同的例外。这是特定的片段:

        InputStream inStream = new FileInputStream("our_cert.cer");
        CertificateFactory cf = CertificateFactory.getInstance("X.509");
        X509Certificate cert =(X509Certificate)cf.generateCertificate(inStream);
        inStream.close();

        // Read the public key from certificate file
        RSAPublicKey pubkey = (RSAPublicKey) cert.getPublicKey();
        cipher.init(Cipher. ENCRYPT_MODE, certif);  // it breaks at this point

我用

替换了最后一行
cipher.init(Cipher. PRIVATE_KEY, certif);

它过得很好。

我不认为这是正确的密钥用法,我认为 ENCRYPT_MODE 也应该有效。

从查看证书关键字部分,我注意到它的密钥用法定义为:

ObjectId: 2.5.29.15 Criticality=true
KeyUsage [
  DigitalSignature
  Key_Encipherment
]

并且其中没有其他关键部分。

从它的行为方式来看, Cipher.init(..)仅适用于 DigitalSignature (即 Cipher。PRIVATE_KEY ),但不适用于 Key_Encipherment (即 Cipher。ENCRYPT_MODE )。

所以我的问题是,是否可能没有为该证书正确定义 KeyUsage 。 我不是加密专家,但我正在阅读的一些留言板表明,如果keyUsage很关键,它可能需要使用 DATA_ENCIPHERMENT 以及......

感谢您的帮助!

2 个答案:

答案 0 :(得分:1)

通过source code进行探讨,有两个条件会抛出该异常,其中一个是永不发生的错误,另一个是检查有问题的证书是否支持相关操作。

if (critSet != null && !critSet.isEmpty()
    && critSet.contains(KEY_USAGE_EXTENSION_OID)) {
  boolean[] keyUsageInfo = cert.getKeyUsage();
  // keyUsageInfo[2] is for keyEncipherment;
  // keyUsageInfo[3] is for dataEncipherment.
  if ((keyUsageInfo != null) &&
      (((opmode == Cipher.ENCRYPT_MODE) &&
      (keyUsageInfo.length > 3) &&
      (keyUsageInfo[3] == false)) ||
      ((opmode == Cipher.WRAP_MODE) &&
      (keyUsageInfo.length > 2) &&
      (keyUsageInfo[2] == false)))) {
      throw new InvalidKeyException("Wrong key usage");
    }
}

这表明您使用的证书对加密无效。也许您尝试使用公钥证书进行加密?

看起来代码没有检查像PRIVATE_KEY一样无效的opmode,只是有时像ENCRYPT_MODE那样无效的代码,这可能就是为什么它对你的测试代码没问题

答案 1 :(得分:1)

最近进入这个。只要正确创建,您就可以使用公钥证书进行加密。证书应具有密钥用法对象ID(ObjectId:2.5.29.15 Criticality = true),设置为'b0'(10110000),基本上启用了数据加密位。否则您将遇到错误密钥使用错误。任何CA颁发的证书都将Key Usage criticality设置为true。自签名证书(例如在dev / sandbox环境中)不会将该关键性设置为true,因此这些将起作用,前提是您已设置为信任它们。 我尝试使用服务器上的公钥证书,并且正在努力解决这个错误。然后我们的IT部门找回了适用于Data Encipherment的正确公钥证书,它开始为我工作。