不同编码下RSA加密字符串对比及固定加密结果方案咨询
首先得明确:你遇到的加密结果每次变化的情况,是RSA安全填充机制导致的正常现象——默认使用的OAEP(当你调用Encrypt时传true)或PKCS#1 v1.5(传false)填充会在明文前加入随机数,目的是防止相同明文每次加密出相同密文,避免攻击者通过密文频率破解。所以哪怕是同一明文、同一密钥,每次加密结果都不一样,这是安全设计的要求。
关于取消填充的可行性
确实可以实现无填充的RSA加密(也就是教科书式RSA),但我必须强烈警告你:绝对不要在生产环境这么做——无填充的RSA完全没有安全性可言,攻击者可以通过简单的数学分析直接恢复明文,甚至伪造签名。如果只是为了测试UTF-16和UTF-8编码的加密差异(仅限测试场景),可以临时这么做,但一定要清楚风险。
在C#里,原生的RSACryptoServiceProvider不直接支持无填充,得用第三方库比如BouncyCastle。我给你写个能跑通的示例,解决你之前遇到的技术问题:
using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Security; using System; using System.Text; class Program { static void Main() { string plainText = "test_string"; // 生成RSA密钥对 var keyPairGenerator = GeneratorUtilities.GetKeyPairGenerator("RSA"); keyPairGenerator.Init(new KeyGenerationParameters(new SecureRandom(), 2048)); var keyPair = keyPairGenerator.GenerateKeyPair(); var publicKey = (RsaKeyParameters)keyPair.Public; var privateKey = (RsaPrivateCrtKeyParameters)keyPair.Private; // UTF-16编码加密(无填充) byte[] utf16Bytes = Encoding.Unicode.GetBytes(plainText); byte[] encryptedUtf16 = EncryptWithoutPadding(utf16Bytes, publicKey); string base64Utf16 = Convert.ToBase64String(encryptedUtf16); Console.WriteLine("UTF-16无填充加密结果:" + base64Utf16); // UTF-8编码加密(无填充) byte[] utf8Bytes = Encoding.UTF8.GetBytes(plainText); byte[] encryptedUtf8 = EncryptWithoutPadding(utf8Bytes, publicKey); string base64Utf8 = Convert.ToBase64String(encryptedUtf8); Console.WriteLine("UTF-8无填充加密结果:" + base64Utf8); // 验证解密结果 byte[] decryptedUtf16 = DecryptWithoutPadding(encryptedUtf16, privateKey); Console.WriteLine("UTF-16解密结果:" + Encoding.Unicode.GetString(decryptedUtf16)); byte[] decryptedUtf8 = DecryptWithoutPadding(encryptedUtf8, privateKey); Console.WriteLine("UTF-8解密结果:" + Encoding.UTF8.GetString(decryptedUtf8)); } static byte[] EncryptWithoutPadding(byte[] data, RsaKeyParameters publicKey) { var engine = new Org.BouncyCastle.Crypto.Engines.RsaEngine(); engine.Init(true, publicKey); return engine.ProcessBlock(data, 0, data.Length); } static byte[] DecryptWithoutPadding(byte[] data, RsaPrivateCrtKeyParameters privateKey) { var engine = new Org.BouncyCastle.Crypto.Engines.RsaEngine(); engine.Init(false, privateKey); return engine.ProcessBlock(data, 0, data.Length); } }
注意:无填充的RSA对明文长度有严格要求,必须小于等于RSA密钥的字节长度(比如2048位密钥对应256字节,明文长度不能超过这个值),否则会直接报错。
更安全的替代方案(强烈推荐)
与其冒险取消填充,不如换个思路:通过解密后验证明文来确认编码差异。这样既保留了RSA的安全性,又能达到你的验证目的:
- 分别对UTF-16和UTF-8编码的字节数组进行加密(保留安全填充)
- 解密两个密文,得到对应的字节数组
- 对比解密后的字节数组和原编码的字节数组是否一致,或者转成字符串验证
我修改了你的原代码来实现这个方案:
using System; using System.Security.Cryptography; using System.Text; class Program { static void Main() { string plainText = "test_string"; RSA rsa = RSA.Create(); RSAParameters rsaParams = rsa.ExportParameters(true); // UTF-16加密与解密验证 using (var rsa1 = new RSACryptoServiceProvider()) { rsa1.ImportParameters(rsaParams); byte[] utf16Bytes = Encoding.Unicode.GetBytes(plainText); byte[] encryptedUtf16 = rsa1.Encrypt(utf16Bytes, true); // 使用OAEP安全填充 byte[] decryptedUtf16 = rsa1.Decrypt(encryptedUtf16, true); // 验证解密后的字节是否和原UTF-16字节完全一致 bool isUtf16Valid = CompareByteArrays(utf16Bytes, decryptedUtf16); Console.WriteLine("UTF-16加密解密验证:" + (isUtf16Valid ? "成功" : "失败")); Console.WriteLine("原UTF-16字节长度:" + utf16Bytes.Length); } // UTF-8加密与解密验证 using (var rsa2 = new RSACryptoServiceProvider()) { rsa2.ImportParameters(rsaParams); byte[] utf8Bytes = Encoding.UTF8.GetBytes(plainText); byte[] encryptedUtf8 = rsa2.Encrypt(utf8Bytes, true); // 使用OAEP安全填充 byte[] decryptedUtf8 = rsa2.Decrypt(encryptedUtf8, true); // 验证解密后的字节是否和原UTF-8字节完全一致 bool isUtf8Valid = CompareByteArrays(utf8Bytes, decryptedUtf8); Console.WriteLine("UTF-8加密解密验证:" + (isUtf8Valid ? "成功" : "失败")); Console.WriteLine("原UTF-8字节长度:" + utf8Bytes.Length); } } // 辅助方法:对比两个字节数组是否完全相同 static bool CompareByteArrays(byte[] a, byte[] b) { if (a.Length != b.Length) return false; for (int i = 0; i < a.Length; i++) { if (a[i] != b[i]) return false; } return true; } }
这个方案不仅安全,还能让你清晰看到两种编码的差异:比如"test_string"的UTF-16字节长度是22,UTF-8是11,这直接说明加密的输入是不同的。哪怕每次加密的密文不一样,解密后都能还原出正确的编码字节,完全能满足你的验证需求。
内容的提问来源于stack exchange,提问作者user8947720

