【问题标题】:What are the differences between C# and Java (Android) when encrypting using RSA with PKCS1 padding?使用带有 PKCS1 填充的 RSA 进行加密时,C# 和 Java (Android) 有什么区别?
【发布时间】:2019-02-08 09:30:54
【问题描述】:

我在 Java (Android) 中使用带有 PKCS1 填充的 RSA 加密文本,之后我必须将其作为 Web 服务正文的一部分发送。我的 Java 代码的输出不起作用,但如果我在 C# 中执行,技术上相同的算法,那么它可以正常工作。

这是运行良好的 C# 代码:

static void Main(string[] args)
    {
        var plainData = "plain_text";
        RSA publicKeyEncryptor = getRSAPublic(@"<public_key>");
        var plainBytes = Encoding.ASCII.GetBytes(plainData);
        string encryptedPayload = System.Convert.ToBase64String(publicKeyEncryptor.Encrypt(plainBytes, RSAEncryptionPadding.Pkcs1));
        Console.WriteLine(encryptedPayload);
    }

public static RSA getRSAPublic(string publicKey)
    {
        string publicKeyPem = $"-----BEGIN PUBLIC KEY-----\r\n{ publicKey }\r\n-----END PUBLIC KEY-----\r\n";
        var pemReader = new PemReader(new StringReader(publicKeyPem));
        AsymmetricKeyParameter keyPairRaw = (AsymmetricKeyParameter)pemReader.ReadObject();
        RSAParameters rsaParams = DotNetUtilities.ToRSAParameters((RsaKeyParameters)keyPairRaw);
        RSA rsaObj = System.Security.Cryptography.RSA.Create();
        rsaObj.ImportParameters(rsaParams);
        return rsaObj;
    }

当我尝试将其转换为 Java (Android) 时,这是我想出的代码,但它不会生成有效的输出:

public static void main(String args[]) {
    String stringToEncrypt = "plain_text";
    String publicKey = "<public_key>";
    System.out.println(encrypt(stringToEncrypt, publicKey))
}

public String encrypt(String plain, String publicKey) {
    try {
        byte[] keyBytes = Base64.decode(publicKey, Base64.DEFAULT);
        X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
        PublicKey rsaPublicKey = KeyFactory.getInstance("RSA").generatePublic(spec);
        Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
        cipher.init(Cipher.ENCRYPT_MODE, rsaPublicKey);
        byte[] plainTextBytes = plain.getBytes(Charset.forName("US-ASCII"));
        byte[] encryptedBytes = cipher.doFinal(plainTextBytes);
        return Base64.encodeToString(encryptedBytes, Base64.NO_WRAP));
    } catch (Exception e) {
        e.printStackTrace();
    }
    return "";
}

有什么不同?

谢谢!

【问题讨论】:

  • 可能是“RSA/CBC/PKCS1Padding”
  • 该算法不存在,我得到:java.security.NoSuchAlgorithmException: No provider found for RSA/CBC/PKCS1Padding 我也尝试了RSA/ECB/PKCS1PaddingRSA/NONE/PKCS1Padding,但它也不起作用。
  • 您在使用 BouncyCastle 吗?这是安卓吗?
  • 是的,这是安卓。我尝试通过使用 Cipher.getInstance("RSA/ECB/PKCS1Padding", "BC"); 请求密码来使用 BouncyCastle 提供程序,但结果相同。
  • @zaitsman 我用Java 1.8做了一个测试,结果是一样的,所以它似乎与Android无关

标签: java c# android encryption rsa


【解决方案1】:

两个代码都是正确的。您可以使用 Java 版本和 C# 版本以相同的方式加密数据。如果其中一个正在工作而另一个失败,则可能您使用的公钥不正确(复制/粘贴错误、一些不同的字节等...)

【讨论】:

    【解决方案2】:

    Java 和 C# 程序都产生兼容输出。兼容,但不完全相同,因为输出应该每次都不同。出于安全原因,PKCS1 填充包含一个随机组件。因此,如果您期望相同的输出,那么您的期望是错误的。相同的输出表明有问题,输出应该每次都不同。

    RFC 8017, section 7.2.1 详细解释了正在发生的事情。特别是,步骤 2a 说:

    生成长度为 k - mLen - 3 的八位字节字符串 PS 由伪随机生成的非零八位字节组成。 PS 的长度至少为八个八位字节。

    为了验证它们的兼容性,我根据你的代码编写了一些小的 C# 和 Java 程序,发现它们都可以解密对方的输出并得到正确的结果。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-05-23
      • 1970-01-01
      • 2017-07-16
      • 2020-01-08
      • 1970-01-01
      • 2011-02-18
      相关资源
      最近更新 更多