Android 和 Java 中的 RSA 加密

RSA encryption in Android and Java

我想用 RSA 加密来加密一个字符串。我的 public/private 密钥已生成并存储在数据库中。在 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)", "");
}

出于调试目的,我尝试使用相同的参数调用此方法 2 次,字符串结果相似(如预期)。

我想在 java 中生成相同的加密字符串。但是,"android.util.Base64" class 在 Java 中不可用,所以我尝试使用默认的 Base64 class:

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生成的String和java生成的String不一样

在 Android 侧生成:

Ky2T4j1JdI081ZESVJgxZXEf/xmtpehfv/EwpVvKQxUu1JI8lwXP2Rc66jHZRc0P846ZYuF3C9YEmWoKbXGXk2MBuT5KVxa2yoTbwZlMmhVOX3X3Efq0VyaO5zZ4qavIq036cA3MzvQbUAb678UdbALW/CjRCsOdeH+hSCzNQ+0=

在 JAVA 侧生成:

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

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

谢谢你

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

我还不能评论所以我回答。

可能使用了不同的默认配置。检查这个问题:Is there any difference between Apache's Base64.encodeBase64 and Android's Base64.encode with Base64.Default flag?

看起来你已经因为依赖默认设置而失败了。如果您希望互操作性,请不要这样做。

这是我在您的代码中发现的两个错误依赖默认值的示例。

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

转换字符串应该是 "algorithm/mode/padding" 的形式,但是你没有使用 modepadding规格。结果,您获得了这些的默认值。 Android 和 Oracle Java 上的默认值明显不同。您应该始终 fully specify the transformation,例如:
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"));

用于在解密方法中重新创建原始字符串的正确字符串构造函数是 String(byte [] data, String charsetName)