如何将 Java RSA 公钥移植到 c# 加密功能?

Posted

技术标签:

【中文标题】如何将 Java RSA 公钥移植到 c# 加密功能?【英文标题】:How to porting Java RSA public key to c# encrypt function? 【发布时间】:2020-12-17 19:36:44 【问题描述】:

想在java中使用生成的RSA公钥,在c#函数中加密数据并在Java解密函数中解密。

生成的 Java 公钥已在 c# 中的 Modulus 标记中替换:

static string publicKey = "<RSAKeyValue><Modulus>MFwwDQ...wEAAQ==</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>";

C# 加密函数:

  static string Encrypt(string text)
    
        const int PROVIDER_RSA_FULL = 1;
        const string CONTAINER_NAME = "Tracker";

        CspParameters cspParams;
        cspParams = new CspParameters(PROVIDER_RSA_FULL);
        cspParams.KeyContainerName = CONTAINER_NAME;
       
        RSACryptoServiceProvider rsa1 = new RSACryptoServiceProvider(512,cspParams);
        rsa1.FromXmlString(publicKey);

        byte[] textBytes = Encoding.UTF8.GetBytes(text);
        byte[] encryptedOutput = rsa1.Encrypt(textBytes, RSAEncryptionPadding.Pkcs1);
        string outputB64 = Convert.ToBase64String(encryptedOutput);

        return outputB64;
    

Java解密函数:

static String Decrypt(String encodedString,PrivateKey privKey) 
    try 
        Cipher cipher = Cipher.getInstance(cipherInstancename);
        cipher.init(Cipher.DECRYPT_MODE, privKey);
        byte[] decrypted = cipher.doFinal(Base64.getDecoder().decode(encodedString));
        return new String(decrypted, "UTF-8");
     catch (Exception err) 
        return err.fillInStackTrace().toString();
    

第一个问题:在 c# XML 字符串中替换 Modulus 标记中的 Java 公钥是否正确?指数标签呢?我使用了 AQAB 值。

第二个问题:为什么在Java中解密时会出现这个错误:

 javax.crypto.IllegalBlockSizeException: Data must not be longer than 64 bytes

经过一番研究,我发现这是一个普遍的错误,什么原因会导致这种错误?

【问题讨论】:

RSA 私钥/公钥加密仅限于小数据,并且取决于密钥长度。您使用的是不安全的 512 位长密钥,因此您一次只能加密 64 个字节。拥有更长的密钥将导致更大的明文可以被加密,但加密的时间也会增加,因此在“现实世界”中,您将使用混合加密(生成随机 AES 密钥,使用此密钥加密您的数据,加密密钥(AES 256 为 32 字节)和公钥,并将加密数据和加密密钥一起发送到 Java - 在那里你用私钥解密密钥并解密。 【参考方案1】:

第一个问题:在 Modulus 中替换 Java 公钥是否正确 c# XML 字符串中的标记?

不!密钥与模数不同,但包含模数和指数。因此,在您的情况下,两者都必须由 Java 生成的密钥确定。

公钥可以有不同的格式。例如,在本机 Java 中生成的公钥通常具有 X.509/SPKI 格式,并且可能是 byte[],即采用 DER 编码。如果byte[] 是Base64 编码的(这对应于发布的MFwwDQ...wEAAQ==)并且标题-----BEGIN PUBLIC KEY----- 和页脚--- --END PUBLIC KEY-----添加(通常在Base64编码的正文中,每64个字符后还有一个换行符),密钥将采用PEM编码。

手动确定模数和指数的最简单方法是在 ASN.1 解析器中加载 PEM 密钥,例如here,或在合适的网站上直接将其转换为 XML 格式,例如here.

指数标签呢?我使用了 AQAB 值。

这个问题已经用前面所说的含蓄地回答了。但是要注意一点:对于指数,通常选择值 65537(十六进制编码 0x010001 和 Base64 编码 AQAB)。但并非总是如此。因此,如果现有键的指数盲目地被这个值替换,它很有可能会起作用,但你不能依赖它,例如here 和 here。

第二个问题:为什么在Java中解密时会出现这个错误:javax.crypto.IllegalBlockSizeException: Data must not be long than 64 bytes

这已经在 Michael Fehr 的评论中得到了回答,以及其他重要的点(例如安全性、性能和混合加密):密钥长度限制了明文的长度,使用 512 位密钥最多可以有 64 个字节加密。

另外,应该注意的是,不仅密钥的长度,而且使用的填充限制了明文的长度,例如PKCS#1 v1.5 填充需要 11 个字节,因此 512 位密钥的最大明文长度为 64 - 11 = 53 个字节,here。在 OAEP 的情况下,使用的摘要决定了填充需要多少字节,参见例如here。如果不使用填充(tetxbook RSA),明文的最大长度对应于密钥长度。然而,在实践中,出于安全原因,必须始终使用填充


从 .NET Core 3.0 开始,直接支持导入 X.509/SPKI 密钥(以 DER 编码)和类似格式,参见例如here,尤其是RSA.ImportSubjectPublicKey。在早期的 .NET Core 版本和 .NET Framework 中,这些功能不可用,但可以使用 BouncyCastle

【讨论】:

以上是关于如何将 Java RSA 公钥移植到 c# 加密功能?的主要内容,如果未能解决你的问题,请参考以下文章

C# 与JAVA 的RSA 加密解密交互,互通,C#使用BouncyCastle来实现私钥加密,公钥解密的方法

C# RSA 加密/解密与传输

(转)C#实现RSA非对称加密解密

C# RSA加密算法PEM格式转换XML

C# RSA加密算法PEM格式转换XML

dotnet c#中的公钥RSA加密