6

我有一个 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)
4

6 回答 6

9

有解决办法,见http://www-01.ibm.com/support/docview.wss?uid=swg1IV18625

与财产

-Dcom.ibm.crypto.provider.DoRSATypeChecking=false

您可以使用私钥来加密数据。

于 2014-08-12T12:37:00.513 回答
5

我不确定这一点,但我相信 JCE 有一个嵌入式策略,限制对公钥的加密和对私钥的解密。

在示例代码中,加密是使用私钥完成的。这将需要公钥解密,这意味着任何拥有公钥的人都可以访问编码数据。尽管这有它的用途,但它不是公认的模式,IBM 实现可能会“保护”您免于意外创建公开可读的加密数据。

当这些被逆转时它正确测试的事实往往证实了我的怀疑,但我还没有找到一份官方文件说明这么多。

于 2011-02-02T17:39:44.857 回答
3

我最近遇到了同样的问题。最终通过使用充气城堡实现并将这一行添加到 java.security 文件中解决了这个问题

security.provider.1=org.bouncycastle.jce.provider.BouncyCastleProvider

于 2014-03-31T13:24:37.787 回答
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);

本质上,我用私钥的加密材料创建了一个公钥对象。您需要做相反的事情,使用公钥的加密材料创建一个私钥对象,如果您想避免“公钥不能用于解密”异常,请使用公钥解密。

于 2011-11-12T05:56:08.967 回答
2

@T.Rob 评论说您在使用私钥加密时可能犯了一个错误。如果“每个人”都知道公钥,那么任何人都可以解密您的文件。因此,IBM 的 JCE 行为正在保护人们免受此错误的影响。

我可以看到其中的逻辑。

但是,在某些情况下,您确实需要使用私钥进行加密;例如,作为需要证明您知道与已发布公钥对应的私钥的协议的一部分。

如果这确实是您想要做的,您可能需要使用最近的 Sun JCE 实现(旧的 Sun JCE 没有实现 RSA)或 Bouncy Castle。

于 2011-02-02T05:22:59.677 回答
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)

如您所见,我们正在使用“签名”并调用“initSign”,这确实需要一个 PrivateKey。这证明 IBM 对这个事实一无所知,显然他们甚至没有有效的回归测试!

使用另一个加密货币提供商,在他们改变主意之前不要相信 IBM。

最好的问候,克里斯蒂安

于 2013-02-20T17:34:19.413 回答