java IBM 的 JCE 提供程序有什么问题?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4869904/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
What's wrong with IBM's JCE provider?
提问by FelixM
I have a JCE test that works fine with all Sun JDKs I have tried, but fails with various IBM J9 JDKs (e.g. 1.6.0 build pwi3260sr8-20100409_01(SR8)). The exception below happens when the cipher is initialized in encrypt mode. Why can the IBM JCE not use its own private key? Am I missing something in my code?
我有一个 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 );
}
Here is the stack trace:
这是堆栈跟踪:
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)
采纳答案by T.Rob
I don't know this for sure but I believe that the JCE has an embedded policy limiting encryption to the public key and decryption to the private key.
我不确定这一点,但我相信 JCE 有一个嵌入式策略,限制对公钥的加密和对私钥的解密。
In the example code the encryption was done with the private key. This would require the public key to decrypt, meaning that anyone with the public key could access the encoded data. Although this has it's uses it is not the accepted pattern and the IBM implementation may be "protecting" you from accidentally creating encrypted data that was publicly readable.
在示例代码中,加密是使用私钥完成的。这将需要公钥进行解密,这意味着任何拥有公钥的人都可以访问编码数据。尽管这有它的用途,但它不是公认的模式,IBM 实现可能会“保护”您免于意外创建可公开读取的加密数据。
The fact that it tested properly when these were reversed tends to confirm my suspicions but I haven't yet found an official document stating as much.
当这些被颠倒时它正确测试的事实往往证实了我的怀疑,但我还没有找到一份官方文件说明这么多。
回答by Peter Heuchert
There is a solution, see http://www-01.ibm.com/support/docview.wss?uid=swg1IV18625
有一个解决办法,见http://www-01.ibm.com/support/docview.wss?uid=swg1IV18625
with the property
与财产
-Dcom.ibm.crypto.provider.DoRSATypeChecking=false
you can use private keys to encrypt data.
您可以使用私钥来加密数据。
回答by Val
IBM insists private keys cannot be used for encryption and public keys cannot be used for decryption, so they either see this artificial restriction as a feature, or someone is seriously confused here.
IBM 坚持私钥不能用于加密,公钥不能用于解密,因此他们要么将此人为限制视为一项功能,要么在这里有人严重混淆。
Here is how I worked around this problem:
以下是我解决此问题的方法:
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);
Essentially, I created a public key object with private key's crypto material. You will need to do the reverse, create a private key object with public key's crypto material, to decrypt with public key if you want to avoid the "Public key cannot be used to decrypt" exception.
本质上,我使用私钥的加密材料创建了一个公钥对象。如果您想避免“公钥不能用于解密”异常,您将需要执行相反的操作,使用公钥的加密材料创建一个私钥对象,然后使用公钥进行解密。
回答by bacon
I recently ran in to the same problem. This was eventually solved by using the bouncy castleimplementation and adding this line to the java.security file
我最近遇到了同样的问题。最终通过使用充气城堡实现并将这一行添加到 java.security 文件中解决了这个问题
security.provider.1=org.bouncycastle.jce.provider.BouncyCastleProvider
security.provider.1=org.bouncycastle.jce.provider.BouncyCastleProvider
回答by Christian Kahlo
@Stephen C / @FelixM: IBM seems to be completely clueless about how RSA cryptography works and how it is intended to be used. Basically both operations (encrypt / decrypt) must be available for the public AND private key.
@Stephen C / @FelixM:IBM 似乎对 RSA 加密的工作原理及其使用方式一无所知。基本上两种操作(加密/解密)都必须可用于公钥和私钥。
Encrypt with public key is needed to transmit the client-side part of the pre master secret in SSL/TLS handshakes. The server needs to decrypt with its private key. But if they negotiate something like ECDHE_RSA the server needs to SIGN parts of the handshake with the private key - thats encrypt with PrivateKey. Vice versa the client needs to decrypt with the public key from the certificate of the server to verify the hash value of the signature. (proving authenticity of the message)
需要使用公钥加密以在 SSL/TLS 握手中传输预主密钥的客户端部分。服务器需要用它的私钥解密。但是如果他们协商 ECDHE_RSA 之类的东西,服务器需要用私钥签署握手的部分 - 那是用私钥加密的。反之亦然,客户端需要使用来自服务器证书的公钥进行解密,以验证签名的哈希值。(证明消息的真实性)
So if I try to run ECDHE_RSA (server-side) on latest IBM JDK 7 the following happens:
因此,如果我尝试在最新的 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)
As you can see we're using "Signature" and call "initSign", which requires indeed a PrivateKey. This proves IBM being clueless about this fact and obviously they don't even have valid regression tests!
正如您所看到的,我们正在使用“签名”并调用“initSign”,这确实需要一个私钥。这证明 IBM 对这一事实一无所知,显然他们甚至没有有效的回归测试!
Use another crypto provider and don't believe IBM until they change their mind.
使用其他加密货币提供商,在他们改变主意之前不要相信 IBM。
Best regards, Christian
最好的问候,基督徒
回答by Stephen C
@T.Rob commented that you may have made a mistake in encrypting with the private key. If "everyone" knows the public key, then anyone can decrypt your file. IBM's JCE behaviour is thus protecting people against this mistake.
@T.Rob 评论说您可能在使用私钥加密时犯了一个错误。如果“每个人”都知道公钥,那么任何人都可以解密您的文件。IBM 的 JCE 行为因此可以保护人们免受此错误的影响。
I can see the logic of that.
我可以看出其中的逻辑。
However, there may be cases where you really do need to encrypt with the private key; e.g. as part of a protocol that needs to prove that you know the private key corresponding to a published public key.
但是,在某些情况下,您确实需要使用私钥进行加密;例如,作为需要证明您知道与已发布公钥对应的私钥的协议的一部分。
If this is really what you want to do, you probably need to use a recent Sun JCE implementation (older Sun JCEs didn't implement RSA), or Bouncy Castle.
如果这确实是您想要做的,您可能需要使用最近的 Sun JCE 实现(较旧的 Sun JCE 没有实现 RSA)或 Bouncy Castle。