【问题标题】:Different encryption Android vs pure Java - RSA/ECB/OAEPWithMD5AndMGF1PaddingAndroid与纯Java的不同加密 - RSA/ECB/OAEPWithMD5AndMGF1Padding
【发布时间】:2020-05-30 07:10:39
【问题描述】:

我在 Android 中使用公钥加密了一个字符串。但是,当我尝试通过纯 Java 代码中的私钥解密加密字符串时,出现异常“解密错误”。谁能帮忙找出问题?

要加密的 Android 代码

import android.util.Base64;
public static String encryptMessage(final String plainText, final PublicKey publicKey) throws Exception {
    Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithAndMGF1Padding");
    cipher.init(Cipher.ENCRYPT_MODE, publicKey);
    return Base64.encodeToString(cipher.doFinal(plainText.getBytes()), Base64.NO_WRAP);
}

要解密的纯 Java 代码

import java.util.Base64;
public static String decryptMessage(final String encryptedText, final PrivateKey privateKey) throws Exception {
    Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithAndMGF1Padding");
    cipher.init(Cipher.DECRYPT_MODE, privateKey);

    Base64.Decoder decoder = Base64.getDecoder();
    byte[] byteArray = decoder.decode(encryptedText);
    byte[] decryptedArray = cipher.doFinal(byteArray);  // throw exception here
    String plainText = new String(decryptedArray);
    return plainText;
}

您可能注意到我必须在 Android 和纯 Java 中使用不同的 Base64 API。我试过“RSA/ECB/PKCS1Padding”,它可以正确解密,无一例外。也尝试了“RSA/ECB/OAEPWithSHA-256AndMGF1Padding”,但得到了同样的例外。

【问题讨论】:

  • 有趣。首先,如果您使用错误的密文/私钥组合,也会引发填充错误,因为总是可以执行模幂运算。您能否指出您正在使用的运行时环境(Java 版本和 Android API 级别)?
  • 这两个代码可能使用不同的提供程序,这些提供程序为OAEPWithMD5AndMGF1Padding 应用不同的摘要。这里必须考虑到 OAEP 在两个地方使用摘要:作为掩码生成函数 MGF1 的基础和 OAEP 标签的散列(有关详细信息,请参阅 RFC 8017)。对于解密,两个摘要必须与加密摘要相同。为了测试这一点,可以使用OAEPParameterSpec显式定义两个摘要。
  • 基于 IntelliJ IDEA 2019.1.4 构建的纯 Java,Java SDK /Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home。从 Android Studio 3.6.3 构建的 Android 代码; compileSdkVersion 28, buildToolsVersion '28.0.3', minSdkVersion 21, targetSdkVersion 28, androidTestImplementation 'androidx.test.ext:junit:1.1.1', androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0', 运行 AndroidTest在三星 Android 8.0.0 版中。
  • 在我的机器上,BC Provider 用于 Android,API 级别 28,OAEPWithMD5AndMGF1Padding,它将 MD5 应用于标签和 MGF。对于 Java 8,OAEPWithMD5AndMGF1Padding 默认使用 SunJCE 提供程序,它对标签应用 MD5,而对 MGF 应用 SHA1。由于摘要不同,如果在两个代码中都使用OAEPWithMD5AndMGF1Padding,则无法使用Java 解密使用Android 生成的密文。这个问题可以通过OAEPParameterSpec 解决。检查提供程序可能是值得的,如有必要,使用OAEPParameterSpec 调整摘要。
  • @Topaco 在三星上的 Android 中,cipher.getParameters().getParameterSpec(OAEPParameterSpec.class) 抛出异常“找不到 RSA/ECB/OAEPWithAndMGF1Padding 的提供程序”。在我的 MAC 上的 Java 中,它可以工作,并为 getDigestAlgorithm() 获取 MD5,为 getMGFParameters() 获取 MGF1。

标签: java android encryption


【解决方案1】:

OAEP 使用两个摘要,一个用于 OAEP 标签,另一个作为 MGF1 的基础,请参阅 RFC 8017、7.1. RSAES-OAEPB.1. Hash FunctionsB.2. Mask Generation Functions

此问题是因为在 OP 代码(Android/API 级别 28 和 Java 8)的两侧使用的提供程序为 OAEPWithMD5AndMGF1Padding 应用了不同的 MGF1 摘要。

在密码初始化后可以确定双方的相关参数(提供者、OAEP 摘要、MGF、MGF1 摘要),例如与:

OAEPParameterSpec parameterSpec = cipher.getParameters().getParameterSpec(OAEPParameterSpec.class);
System.out.println("Provider: " + cipher.getProvider().getName());          
System.out.println("OAEP digest: " + parameterSpec.getDigestAlgorithm());  
System.out.println("OAEP MGF : " + parameterSpec.getMGFAlgorithm());        
System.out.println("OAEP MGF1 digest: " + ((MGF1ParameterSpec)parameterSpec.getMGFParameters()).getDigestAlgorithm()); 

这样,MD5 在 Android 端被确定为 MGF1 摘要,在 Java 端被确定为 SHA-1。两边都使用 MD5 作为 OAEP 摘要。如果使用OAEPParameterSpec 显式设置摘要以便双方使用相同的摘要,则可以解决此问题。

例如,Java端的以下代码确保MD5用作OAEP和MGF1摘要,类似于Android端。

OAEPParameterSpec oaepParameterSpecDec = new OAEPParameterSpec("MD5", "MGF1", new MGF1ParameterSpec("MD5"), PSource.PSpecified.DEFAULT);
cipher.init(Cipher.DECRYPT_MODE, privateKey, oaepParameterSpecDec);

这同样适用于OAEPWithSHA-256AndMGF1Padding

另请注意,B.1. Hash Functions 中的 RFC 8017 推荐 SHA-1 和 SHA-2 用于 RSAES-OAEP,但 MD5。

【讨论】:

  • 我不得不说你是真正的加密专家。有这么多不同的算法,Specs ...你可以立即找到问题。这并不容易。谢谢!
猜你喜欢
  • 2021-02-28
  • 1970-01-01
  • 2014-08-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-09-10
  • 2015-11-04
  • 1970-01-01
相关资源
最近更新 更多