0

我正在尝试使用 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 ...</p>

谢谢你的帮助!

4

2 回答 2

1

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

于 2013-07-11T15:00:09.660 回答
1

仔细研究源代码,有两种情况会引发该异常,一种是从未发生过的错误,另一种是检查相关证书是否支持相关操作。

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 这样有时无效的 opmode,这可能就是你的测试代码很好的原因。

于 2012-07-15T05:53:12.697 回答