使用RSA公钥来解密使用RSA被加密的字符串私钥

问题描述:

这是一个悬而未决的问题在这里的副本:Using an RSA Public Key to decrypt a string that was encrypted using RSA Private Key使用RSA公钥来解密使用RSA被加密的字符串私钥

你可以看到笔者发现使用一些代码的解决方案从这里: http://www.codeproject.com/KB/security/PrivateEncryption.aspx

使用该链接的代码看起来很有希望。唯一缺少的是填充。我通常使用PKCS1.5填充,这是OpenSSL RSA的默认填充。

我知道这个问题的答案非常接近。我知道唯一阻止解密的是加密的openssl密文上的pkcs1.5填充。

我很惊讶地发现这个主题上的信息很少,因为在很多情况下你需要一个服务器来加密某些东西,签署某些东西等等,并且让客户端应用程序验证,解密等等公钥。

我也广泛尝试使用RSACryptoServiceProvider来验证使用OpenSSL进行加密所产生的哈希结果。例如,我会使用明文的SHA256散列进行私钥加密,然后尝试对该签名执行RSACryptoServiceProvider验证,但它不起作用。我认为MS做这件事的方式并不标准,可能还有特殊的定制。

所以,另一种方法是这个问题,它只是采用私钥加密的密文并使用C#解密它,从而验证其真实性。哈希可以合并为一个简单的签名验证系统,用于由服务器签名并在客户端进行验证的数据对象。

我已经浏览了PKCS1 RFC,OpenSSL rsa源代码和其他项目,但是在做RSA解密时,我无法得到关于如何解释PKCS1填充的明确答案。我无法找到他们处理PKCS1填充的OpenSSL源代码的位置,否则,我现在可能会有答案。另外,这是我的第一个问题,我知道它是一个未答复的问题的重复,所以,该怎么办?我也搜索了这个,并没有发现任何东西。

我不明白的另一件事是为什么我的解密方法不起作用。由于填充在解密后被移除,我的解密数据应该类似于明文,并且它甚至不是很接近。所以,我几乎肯定pkcs1填充意味着其他事情正在发生,特别是对于密文,这意味着密文在解密之前必须被预处理以去除填充元素。

也许只是过滤密文去除填充元素是这里最简单的解决方案......

这里是我的解密方法:

public static byte[] PublicDecryption(this RSACryptoServiceProvider rsa, byte[] cipherData) 
    { 
     if (cipherData == null) 
      throw new ArgumentNullException("cipherData"); 

     BigInteger numEncData = new BigInteger(cipherData); 

     RSAParameters rsaParams = rsa.ExportParameters(false); 
     BigInteger Exponent = GetBig(rsaParams.Exponent); 
     BigInteger Modulus = GetBig(rsaParams.Modulus); 

     BigInteger decData = BigInteger.ModPow(numEncData, Exponent, Modulus); 

     byte[] data = decData.ToByteArray(); 
     byte[] result = new byte[data.Length - 1]; 
     Array.Copy(data, result, result.Length); 
     result = RemovePadding(result); 
     Array.Reverse(result); 
     return result; 
    } 

    private static byte[] RemovePadding(byte[] data) 
    { 
     byte[] results = new byte[data.Length - 4]; 
     Array.Copy(data, results, results.Length); 
     return results; 
    } 
+0

加密代码在哪里?有*两种* PKCS#1版本1.5填充:块类型(BT)1和块类型2填充。 BT 1用于你正在做的RSA签名,BT 2用于RSA加密。 –

的问题是不是与填充。事实上,从解密的密文中移除填充值实际上非常简单。问题是与在此位置的软件: 你可以看到笔者发现利用这里的一些代码的解决方案:http://www.codeproject.com/KB/security/PrivateEncryption.aspx

以及与Microsoft的实现System.Numeric的这根本无法处理更大的整数...

为了解决这个问题,我查看了codeproject站点上的以前版本的代码,并以此PublicDecrypt方法结束。

public static byte[] PublicDecryption(this RSACryptoServiceProvider rsa, byte[] cipherData) 
    { 
     if (cipherData == null) 
      throw new ArgumentNullException("cipherData"); 

     BigInteger numEncData = new BigInteger(cipherData); 
     RSAParameters rsaParams = rsa.ExportParameters(false); 
     BigInteger Exponent = new BigInteger(rsaParams.Exponent); 
     BigInteger Modulus = new BigInteger(rsaParams.Modulus); 

     BigInteger decData2 = numEncData.modPow(Exponent, Modulus); 
     byte[] data = decData2.getBytes(); 
     bool first = false; 
     List<byte> bl = new List<byte>(); 
     for (int i = 0; i < data.Length; ++i) 
     { 
      if (!first && data[i] == 0x00) 
      { 
       first = true; 
      } 
      else if (first) 
      { 
       if (data[i] == 0x00) 
       { 
        return bl.ToArray(); 
       } 
       bl.Add(data[i]); 
      } 
     } 
     if (bl.Count > 0) 
      return bl.ToArray(); 
     return new byte[0]; 
    } 

这将完全解密使用rsautl实用程序通过OpenSSL的创建的密文,或Perl的隐窝:: OpenSSL的:: RSA private_encrypt方法。

另一个重大变化是丢弃了Microsoft BitInteger库,它根本无法工作。我最终使用了代码项目文章中提到的一个,并在此处找到: http://www.codeproject.com/Articles/2728/C-BigInteger-Class

此处的关键是将库中的maxintsize设置为基于密钥大小有多大的值使用。对于4096位,500的值工作得很好(近似模数的长度)。

这里是调用方法:

  var encmsg3 = "JIA7qtOrbBthptILxnurAeiQM3JzSoi5WiPCpZrIIqURKfVQMN1BrondF9kyNzbjTs1DaEKEuMBVwKExZe22yCvXXpm8pwcEGc9EHcVK2MPqNo89tIF8LJcaDqBMwLvxdaa/QgebtpmOVN/TIWfuiV8KR+Wn07KwsgV+3SALbNgbOIR3RtCx3IiQ3tZzybJb08+ZKwJEOT011uwvvGmtJskQgq9PC8kr1RPXMgaP6xMX7PHFJ8ORhkuWOFfCh+R4NhY1cItVhnRewKpIC2qVlpzUYRAgKIKdCXuZDqUQdIponR29eTovvLb0DvKQCLTf9WI1SzUm6pKRn0vLsQL7L3UYHWl43ISrTpDdp+3oclhgRF3uITR4WCvoljephbGc6Gelk5z3Vi6lN0oQaazJ7zIen+a/Ts7ZX3KKlwPl4/lAFRjdjoqu7u4IAK7O7u1Jf2xDiGw18C/eGt8UHl09zU4qQf9/u+7gtJ+10z2NERlLSaCDjVqslwmmxu81pG2gCv8LfpR4JlPaFfBZMGfGBihyGryWhJwizUXXo8wgdoYbHRXe8/gL19qro0ea5pA9aAhDjTpX1Zzbwu2rUU7j6wtwQtUDJOGXXCw1VOHsx6WXeW196RkqG72ucVIaSAlx5TFJv8cnj6werEx1Ung5456gth3gj19zHc8E/Mwcpsk="; 
      byte[] enc = Convert.FromBase64String(encmsg3); 
      var dec = rsa2.PublicDecryption(enc); 
      Debug.Print("PLAINTEXT: " + Encoding.UTF8.GetString(dec)); 

唯一的最后一件事,有人会需要完全复制此会得到私钥到OpenSSL的格式,使他们能够通过私钥和公钥来回在openssl和C#之间。

我使用openssl.net,并创建了一个RSA实例,并使用bignumbers设置所有变量。下面是该代码:

 RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(); 
     rsa.FromXmlString(Properties.Resources.RSAParameters); 

     RSAParameters par = rsa.ExportParameters(true); // export the private key 


     using (OpenSSL.Crypto.RSA rsaos = new OpenSSL.Crypto.RSA()) 
     using (BigNumber bnmod = BigNumber.FromArray(par.Modulus)) 
     using (BigNumber bnexp = BigNumber.FromArray(par.Exponent)) 
     using (BigNumber bnD = BigNumber.FromArray(par.D)) 
     using (BigNumber bnP = BigNumber.FromArray(par.P)) 
     using (BigNumber bnQ = BigNumber.FromArray(par.Q)) 
     using (BigNumber bnDmodP = BigNumber.FromArray(par.DP)) 
     using (BigNumber bnDmodQ = BigNumber.FromArray(par.DQ)) 
     using (BigNumber bnInverse = BigNumber.FromArray(par.InverseQ)) 
     { 
      rsaos.PublicExponent = bnexp; 
      rsaos.PublicModulus = bnmod; 

      rsaos.IQmodP = bnInverse; 
      rsaos.DmodP1 = bnDmodP; 
      rsaos.DmodQ1 = bnDmodQ; 
      rsaos.SecretPrimeFactorP = bnP; 
      rsaos.SecretPrimeFactorQ = bnQ; 
      rsaos.PrivateExponent = bnD; 
      string privatekey = rsaos.PrivateKeyAsPEM; 
      string publickey = rsaos.PublicKeyAsPEM 
     } 

这样,您可以轻松地创建RSA密钥,出口一切的OpenSSL和加密/解密你想在合理范围内任何东西。处理私钥加密和公钥解密足够了。

很酷。

PublicDecryption函数中有一行存在问题: BigInteger numEncData = new BigInteger(cipherData);

它应该是: BigInteger numEncData = GetBig(cipherData);

该行也应该删除: Array.Reverse(result);

您可能会遇到一些填充问题,但如果您可以正确获取数据,则应该很容易对其进行纠正。