我正在尝试使用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 以及......
感谢您的帮助!
答案 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的正确公钥证书,它开始为我工作。