如何将 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# 加密功能?的主要内容,如果未能解决你的问题,请参考以下文章