我有一个JCE测试适用于我尝试的所有Sun JDK,但是使用各种IBM J9 JDK(例如1.6.0 build pwi3260sr8-20100409_01(SR8))失败了。密码在加密模式下初始化时会发生以下异常。为什么IBM JCE不能使用自己的私钥?我在代码中遗漏了什么吗?
public void testBasicKeyGeneration() throws NoSuchAlgorithmException,
NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException,
BadPaddingException, NoSuchProviderException, SignatureException {
KeyPairGenerator generator = KeyPairGenerator.getInstance( "RSA" );
generator.initialize( 2048 );
KeyPair pair = generator.generateKeyPair();
String data1 = "123456789012345678901234567890123456789012345678901234567890";
Cipher cipher = Cipher.getInstance( "RSA" );
cipher.init( Cipher.ENCRYPT_MODE, pair.getPrivate() );
byte[] encrypted = cipher.doFinal( data1.getBytes() );
cipher.init( Cipher.DECRYPT_MODE, pair.getPublic() );
byte[] decrypted = cipher.doFinal( encrypted );
String data2 = new String( decrypted );
assertEquals( "en/decryption failed", data1, data2 );
}
这是堆栈跟踪:
java.security.InvalidKeyException: Private key cannot be used to encrypt.
at com.ibm.crypto.provider.RSA.engineInit(Unknown Source)
at javax.crypto.Cipher.a(Unknown Source)
at javax.crypto.Cipher.a(Unknown Source)
at javax.crypto.Cipher.init(Unknown Source)
at javax.crypto.Cipher.init(Unknown Source)
at test.Test.testBasicKeyGeneration(LicenseHelperTest.java:56)
答案 0 :(得分:5)
我不确定这一点,但我相信JCE有一个嵌入式策略,限制对公钥的加密和对私钥的解密。
在示例代码中,加密是使用私钥完成的。这将需要公钥解密,这意味着具有公钥的任何人都可以访问编码数据。虽然这有它的用途,但它不是公认的模式,IBM实现可能会“保护”您不会意外地创建公开可读的加密数据。
当它们被逆转时,它正确测试的事实往往证实了我的怀疑,但我还没有找到一份正式文件说明。
答案 1 :(得分:5)
有一个解决方案,请参阅http://www-01.ibm.com/support/docview.wss?uid=swg1IV18625
带有属性
-Dcom.ibm.crypto.provider.DoRSATypeChecking=false
您可以使用私钥加密数据。
答案 2 :(得分:3)
IBM坚持认为私钥不能用于加密,公钥不能用于解密,所以他们要么将这种人为限制视为一种功能,要么就会在这里严重混淆。
以下是我解决此问题的方法:
RSAPrivateCrtKey privateKey = (RSAPrivateCrtKey) ks.getKey(keyAlias, ksPassword.trim().toCharArray());
RSAPublicKeySpec spec = new RSAPublicKeySpec(privateKey.getModulus(),privateKey.getPrivateExponent());
Key fakePublicKey = KeyFactory.getInstance("RSA").generatePublic(spec);
encryptCipher.init(Cipher.ENCRYPT_MODE, fakePublicKey);
基本上,我使用私钥的加密材料创建了一个公钥对象。您需要执行相反的操作,使用公钥的加密材料创建私钥对象,如果要避免“公钥不能用于解密”异常,则使用公钥解密。
答案 3 :(得分:3)
我最近遇到了同样的问题。这最终通过使用the bouncy castle实现并将此行添加到java.security文件
来解决security.provider.1 = org.bouncycastle.jce.provider.BouncyCastleProvider
答案 4 :(得分:2)
@ T.Rob评论说,您可能在使用私钥加密时犯了一个错误。如果“每个人”都知道公钥,那么任何人都可以解密您的文件。因此,IBM的JCE行为可以保护人们免受这种错误的影响。
我可以看到它的逻辑。
但是,在某些情况下,您确实需要使用私钥进行加密;例如作为协议的一部分,需要证明您知道对应于已发布公钥的私钥。
如果这真的是你想要做的,你可能需要使用最近的Sun JCE实现(较旧的Sun JCE没有实现RSA)或Bouncy Castle。
答案 5 :(得分:2)
@Stephen C / @FelixM:IBM似乎完全不知道RSA加密如何工作以及如何使用它。基本上,两个操作(加密/解密)必须可用于公钥和私钥。
需要使用公钥进行加密,以便在SSL / TLS握手中传输预主密钥的客户端部分。服务器需要使用其私钥解密。但是,如果他们协商像ECDHE_RSA之类的东西,服务器需要用私钥来签署部分握手 - 那就是用PrivateKey加密。反之亦然,客户端需要使用服务器证书中的公钥进行解密,以验证签名的哈希值。 (证明信息的真实性)
因此,如果我尝试在最新的IBM JDK 7上运行ECDHE_RSA(服务器端),则会发生以下情况:
java.security.InvalidKeyException: Private key cannot be used to encrypt.
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:614)
at java.lang.Thread.run(Thread.java:777)
at com.ibm.crypto.provider.RSASSL.engineInit(Unknown Source)
at javax.crypto.Cipher.init(Unknown Source)
at javax.crypto.Cipher.init(Unknown Source)
at java.security.Signature$CipherAdapter.engineInitSign(Signature.java:1239)
at java.security.Signature$Delegate.init(Signature.java:1116)
at java.security.Signature$Delegate.chooseProvider(Signature.java:1076)
at java.security.Signature$Delegate.engineInitSign(Signature.java:1140)
at java.security.Signature.initSign(Signature.java:522)
at net.vx4.lib.tls.core.TLSSignature.createSignature(TLSSignature.java:120)
正如您所看到的,我们正在使用“Signature”并调用“initSign”,这确实需要一个PrivateKey。这证明了IBM对这一事实一无所知,显然他们甚至没有 有有效的回归测试!
使用另一个加密提供程序,在他们改变主意之前不要相信IBM。
祝你好运, 基督教