Android和Java中的RSA加密

3

我想使用RSA加密算法来加密一个字符串。我的公钥/私钥已经生成并存储在数据库中。在Android中,我使用以下代码:

public static String encryptRSAToString(String text, String strPublicKey) {
    byte[] cipherText = null;
    String strEncryInfoData="";
    try {

    KeyFactory keyFac = KeyFactory.getInstance("RSA");
    KeySpec keySpec = new X509EncodedKeySpec(Base64.decode(strPublicKey.trim().getBytes(), Base64.DEFAULT));
    Key publicKey = keyFac.generatePublic(keySpec);

    // get an RSA cipher object and print the provider
    final Cipher cipher = Cipher.getInstance("RSA");
    // encrypt the plain text using the public key
    cipher.init(Cipher.ENCRYPT_MODE, publicKey);
    cipherText = cipher.doFinal(text.getBytes());
    strEncryInfoData = new String(Base64.encode(cipherText,Base64.DEFAULT));

    } catch (Exception e) {
    e.printStackTrace();
    }
    return strEncryInfoData.replaceAll("(\\r|\\n)", "");
}

为了调试目的,我尝试使用相同的参数调用此方法两次,并且字符串结果是相似的(如预期)。

我想在Java中生成相同的加密字符串。但是,“android.util.Base64”类在Java中不可用,因此我尝试使用默认的Base64类:

public static String encryptRSAToString(String text, String strPublicKey) {

        byte[] cipherText = null;
        String strEncryInfoData="";
        try {

        KeyFactory keyFac = KeyFactory.getInstance("RSA");
        KeySpec keySpec = new X509EncodedKeySpec(Base64.decodeBase64(strPublicKey.trim().getBytes()));
        Key publicKey = keyFac.generatePublic(keySpec);

        // get an RSA cipher object and print the provider
        final Cipher cipher = Cipher.getInstance("RSA");
        // encrypt the plain text using the public key
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        cipherText = cipher.doFinal(text.getBytes());
        strEncryInfoData = new String(Base64.encodeBase64(cipherText)); 

        } catch (Exception e) {
        e.printStackTrace();
        }
        return strEncryInfoData.replaceAll("(\\r|\\n)", "");
    }

在 Android 生成的字符串与 Java 生成的字符串是不同的。
在 Android 端生成的字符串为:
Ky2T4j1JdI081ZESVJgxZXEf/xmtpehfv/EwpVvKQxUu1JI8lwXP2Rc66jHZRc0P846ZYuF3C9YEmWoKbXGXk2MBuT5KVxa2yoTbwZlMmhVOX3X3Efq0VyaO5zZ4qavIq036cA3MzvQbUAb678UdbALW/CjRCsOdeH+hSCzNQ+0=

在JAVA端生成:

XhSLxfiJUUdZW5kWh0MEPSrqoROBBhNC/krfTx+sdnXML3WegYbMzSvNnPgB8+8Z9joEUBMmoeBI1OhTF6qPFL1EEixkFYAkGaryEFxvN/aFI75kEUj71OHNzAHAuvS+h+9Nssx9psSZ5gc2OoLQH0QtbGDyXB4p+qUGFCde4tY=

有人知道如何解决我的问题吗?

谢谢。


唯一的区别在于Base64类。对于Java方面,我使用org.apache.commons.codec.binary.Base64。 - johann
在调用 getBytes() 时,您没有指定字符集。默认字符集在 Android 和 Java/Windows 上有所不同。 - Robert
这段代码存在很多粗糙之处(例如Cipher.getInstance("RSA");),但归根结底,你为什么认为字符串应该是相同的呢?如果正确地进行RSA加密,则会包含随机组件,因此即使输入相同,输出也会不同。 - President James K. Polk
如果我在Android中使用相同的参数两次调用encryptRSAToString函数,输出的字符串将是相同的。 - johann
3个回答

8

看起来你在依赖默认值时失败了。如果你希望实现互操作性,永远不要这样做。

这里是我发现你代码中错误地依赖默认值的两个例子:

final Cipher cipher = Cipher.getInstance("RSA");

转换字符串应该是形式为“算法/模式/填充”,但是你省略了模式填充规范。结果,你得到那些的默认值。显然,Android 和 Oracle Java 的默认值不同。你应该始终完全指定转换,例如:
final Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWITHSHA-256ANDMGF1PADDING");

另一个糟糕的例子是 cipherText = cipher.doFinal(text.getBytes());

text.getBytes() 中,你依赖于使用平台的默认字符集的无参 getBytes() 方法。但是这个默认字符集在不同的平台上是不同的,因此它不可移植。几乎所有我遇到的情况下,你应该指定 UTF-8 字符集。因此,正确的行是
cipherText = cipher.doFinal(text.getBytes("UTF-8"));

并且在 decrypt 方法中使用的正确字符串构造函数是 String(byte [] data, String charsetName)


非常感谢您的宝贵帮助! - johann

2

0

不同的密码和哈希实现会有偏差。我建议使用OpenSSL作为通用实现。


我的安卓应用已经发布,无法更改安卓代码。 - johann
你遇到问题的根本原因是,尽管Android在JVM上运行,但JVM被修改了,一些底层库与现成的JVM相比存在偏差。 - frogcdcn
这没有意义,因为openssl不是Java库。 - President James K. Polk
@JamesKPolk Java实现了自己的加密算法,独立于openssl。这就解释了偏差的原因。 - frogcdcn
不,这里发生的并不是那样。 - President James K. Polk

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