RSA加密:Java和Android之间的区别

27

我正在使用 RSA 对 Android 上的用户名和密码进行加密,并在服务器上(tomcat 6,java 1.6)对其进行解密。

    PublicKey pubKey = readPublicKeyFromFile(mod, ex);
    Cipher cipher = Cipher.getInstance("RSA");
    cipher.init(Cipher.ENCRYPT_MODE, pubKey);
    byte[] cipherData = cipher.doFinal(data);
    return cipherData;

Java Tomcat 解密:

    PrivateKey pubKey = readPrivateKeyFromFile(mod, ex);
    Cipher cipher = Cipher.getInstance("RSA");
    cipher.init(Cipher.DECRYPT_MODE, pubKey);
    byte[] cipherData = cipher.doFinal(data);
    return cipherData;

如果我在Android之外(只是在主方法中)使用它,那么它可以正常工作。但在我的Android模拟器中不能正常工作。服务器端会出现以下错误:

javax.crypto.BadPaddingException: Blocktype mismatch: 0
    at sun.security.rsa.RSAPadding.unpadV15(RSAPadding.java:311)
    at sun.security.rsa.RSAPadding.unpad(RSAPadding.java:255)
    at com.sun.crypto.provider.RSACipher.a(DashoA13*..)
    at com.sun.crypto.provider.RSACipher.engineDoFinal(DashoA13*..)
    at javax.crypto.Cipher.doFinal(DashoA13*..)

我将mod和ex作为BigIntegers常量保留,以便我不将它们写入文件。 我知道java1.6和java 1.5加密之间存在差异,因此两者都使用java 1.6编译。

一些调试信息:

在Android中进行调试时,我可以看到pubKey以十六进制形式包含模数和指数。如果我在一个main方法中进行调试(再次是相同的代码),我可以看到pubKey以十进制形式包含模数和指数。

我做错了什么?

谢谢


1
可能是重复问题:https://dev59.com/12445IYBdhLWcg3wUYnM - Graham Borland
你真的把私钥称作 pubKey 吗?这是真正的代码吗,还是你只是从记忆中输入到问题中的? - President James K. Polk
1
警告:对于新协议,请尝试使用OAEP填充。PKCS#1容易受到填充预言攻击的威胁。 - Maarten Bodewes
你如何实际使用这个Cipher方法?我得到了“未解决的引用:Cipher”的错误。 - metamonkey
3个回答

44

我正在使用Android 2.2+进行RSA加密,并在Tomcat 6 Java 1.6服务器上进行解密。

我遇到了完全相同的问题,到处阅读,部分归功于@Femi的回答,我找到了我需要的东西。

解决方案是对Cipher使用以下算法规范:

Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");

这可以在Android和BlackBerry智能手机上进行加密。我知道这个问题已经存在四个月了,但以防其他人遇到同样的问题。


4
确认一下,这个解决方案仍然适用于 Android 4.x 和 Java 8。基本上,如果你使用 Cipher.getInstance("RSA"),它将无法工作,你需要指定更具体的一个。 - sam
这在解密JS加密的数据时也有效。在我的情况下,使用Cipher.getInstance("RSA")而不带有"/ECB/PKCS1Padding"会在所需数据之前添加一些额外字符。 - DH28
谢谢。你救了我。 - Mohit
你如何实际使用这个Cipher方法?我得到了“未解决的引用:Cipher”的错误。 - metamonkey

13

我建议您使用特定的密码初始化方法:例如,

Cipher cipher = Cipher.getInstance("RSA/ECB/NoPadding");

这将适用于两者。你遇到的异常 (BadPaddingException) 出现的原因是默认的加密初始化填充在桌面JVM和Android JVM之间似乎不同。


你如何实际使用这个Cipher方法?我得到了“未解决的引用:Cipher”的错误。 - metamonkey

2
首先,看起来你正在使用公钥初始化两个密码。加密使用公钥,解密使用私钥。但我希望这只是一个笔误。
我也曾在RSA加密方面遇到很多麻烦,大部分都是试错。我建议你尝试另一个提供者。我成功地使用BouncyCastle实现了RSA加密。
Cipher wrapper = Cipher.getInstance("RSA", "BC");
wrapper.init(Cipher.ENCRYPT_MODE, publicKey);
encryptedData= wrapper.doFinal(unencryptedData);

虽然这是一次会话加密,但我还是生成了自己的密钥对。

kpg = KeyPairGenerator.getInstance("RSA");
        kpg.initialize(1024);
        KeyPair kp = kpg.genKeyPair();
        publicKey = kp.getPublic();
        privateKey = kp.getPrivate();

+1 因为经过数小时的尝试和错误,我找到了这篇帖子并解决了我的问题。你今天救了我一命,谢谢 :D - Master_T
创建自己的密钥对是不安全的,因为需要建立公钥的信任。此外,指定提供程序不是一种可移植的编码方式,并且默认密钥算法可能会更改。应该优先选择仅指定完整算法(包括填充模式,如其他答案中所示)而不是这种解决方案。 - Maarten Bodewes
你如何实际使用这个Cipher方法?我得到了“未解决的引用:Cipher”的错误。 - metamonkey

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接