Net平台使用公钥解密Java平台下RSA 密文

Written .

原文作者 家一

概述

.Net 平台下 RSA 算法的设计目的仅用于公钥加密,同时为了增强安全性没有使用标准的填充方式。而根据 RSA 算法的设计原理,公私钥是可以互换加密解密的,因此需要自行开发和标准算法兼容的公钥解密实现。本文提供了在已知公钥的 Exponent 和 Modulus 信息的情况下使用 BigInteger 方法解密的算法。

RSA 算法原理

  1. 若要生成密钥对,可以从创建名为 p 和 q 的两个大的质数开始。
  2. 这两个数相乘,结果称为 n。 因为 p 和 q 都是质数,所以 n 的全部因数为 1、p、q 和 n。
  3. 如果仅考虑小于 n 的数,则与 n 为互质数(即与 n 没有公因数)的数的个数等于 (p - 1)(q - 1)。
  4. 现在,选择一个数 e,它与计算的值为互质数。 则公钥表示为 {e, n}。
  5. 若要创建私钥,则必须计算 d,它是满足 (d)(e) mod (p - 1)(q - 1) = 1 的一个数。 根据 Euclidean 算法,私钥为 {d, n}。
  6. 纯文本 m 到密码文本 c 的加密定义为 c = (m ^ e) mod n。 解密则定义为 m = (c ^ d) mod n。

    解密实现

在 PKCS #1: RSA Cryptography Standard(PKCS #1:RSA 加密标准)中,e 即公钥指数被记做 publicExponent;n 被记做 modulus;

由 RSA 非对称密钥的原理,得出如下等式:

密文 = (原文 ^ publicExponent) mod modulus  
原文 = (密文 ^ privateExponent) mod modulus

可以看出将 publicExponent 及 privateExponent 互换,等式仍将成立;则以此可证能够使用公钥解密

示例代码如下:

const string modulusString = "26776227981054958423361002229225864104504099905604032832708121440617177965091187707885399936440109588672059308597507707317530763450908076009274095161517890724002641917748556507884082149161893220335080200458560667350593691928770608937820648686877739587593131723189423722112560861663560170832512867137647584478248010213076201952637568909374165661361305648814931589090761269257147318247994684422931906181709505797130024518676350011602254894573041969400206656893772498674544953305149231855812520228961895242234883039843673780139978343433310511976126880376803769532232848274111779357567853424700645450368095290624904834891";
var modulus = BigInteger.Parse(modulusString);

const string exponentString = "65537";
var exponent = BigInteger.Parse(exponentString);

const string encryptString = "d7C8ph77SaqWsSk+T2KpHXKuhplBdZOosP9a7XnQAziC4A0aO8yQG0RdyMz/Ya2G77V0ufOq0QyHdv25dONOwuCGrq+fUMrn+l8D5fdIsGI0mIvbVVum2A3arxuG0toMhqIlxKD88CIs2hyEMit6exRRMnFgHFjcDh1KVajHC7DecfmhRunQctPFX9Z2JxIpLMGYsqb6qKqSaO0sdfamnFpl2ozwSKBTijAECj7Xx354SiLJTqbsERWx1b5dLR/iuZpODSY9IY3RHdEJ60e+ggk1q+n5MHEdL+M9tnbqw7kYsiLYSVvFJ7YTyqSR4qGC/GyGUAJdNiiNjB8MOGsUBQ==";
var encryptBytes = Convert.FromBase64String(encryptString);
Array.Reverse(encryptBytes); // 需要调整字节序
var value = new BigInteger(encryptBytes);

var temp = BigInteger.ModPow(value, exponent, modulus);
var tempBytes = temp.ToByteArray();
int index = Array.FindIndex(tempBytes, b => b == 0);
var resultBytes = new byte[index];
Array.Copy(tempBytes, resultBytes, index); // 将0排除
Array.Reverse(resultBytes); // 调整顺序

Console.WriteLine(Encoding.UTF8.GetString(resultBytes));

运行结果为:

{"order_id":"2012081303064857","account":"swf011","amount":"5.00","billno":"1100000000000474125","status":"1"}

在 Java 中,获取 Exponent 和 Modulus 的方法为:

String publicKeyString =  "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1BvMF7LGlQb7OEetChUg" + "\r\n"
                            + "grG6+/GpaH7os5WapsMbcRHftljf2A1Wgy3GvcbILJRcINWohuhrBQ2+PIBDyBof" + "\r\n"
                            + "eVU/LEvaT1hQyyJ3OOI1Qa/vXPtXCTUPjfKk5d+0jr7xKa1rES0xJF8s6Bpll6QA" + "\r\n"
                            + "nfuiSEbBq0O5TTFJAmPR0o9+Ity0retQ0W91O4rrCkfS2aSMsKeA5aaz1ixFwDS3" + "\r\n"
                            + "4dpAO0gqhFUvyHITWkS0n7/4MAVqCIoVSfZwIFZ7k2Bf39EouAYbkuYue6rxIlbV" + "\r\n"
                            + "wABAcopMxr4aHbbJRs7Ll62uHyio10jIHXesdz3Ur4GrKOSomay6vAaT4RjggeCv" + "\r\n"
                            + "SwIDAQAB" + "\r\n";

Rsa rsa = new Rsa();
PublicKey key = Rsa.convertPublicKey(publicKeyString);
RSAPublicKey keyValue = (RSAPublicKey)key;
System.out.println(keyValue.getModulus());
System.out.println(keyValue.getPublicExponent());

注意事项

  1. BigInteger 类是 .Net 4.0 后才提供的;
  2. 在实际使用中可能不能兼容各种填充模式;
  3. 在不同 CPU 上可能存在字节序问题;

    参考

http://boytnt.blog.51cto.com/966121/1350441 http://boytnt.blog.51cto.com/966121/1351089 http://boytnt.blog.51cto.com/966121/1351207 http://www.ruanyifeng.com/blog/2013/07/rsaalgorithmpart_two.html http://msdn.microsoft.com/zh-cn/library/system.security.cryptography.rsaparameters(v=vs.110).aspx.aspx) http://msdn.microsoft.com/zh-cn/library/system.numerics.biginteger(v=vs.100).aspx.aspx)

comments powered by Disqus