2011-10-21 85 views
9

我知道我可能得到的主要答案是为什么你会想要这样做?使用RSA公钥解密使用RSA私钥加密的字符串

不幸的是,尽管我抗议,但我必须这么做,尽管我知道这没什么意义。

我用.Net编写函数来解密使用私钥,使用公钥加密。 我也RSA签署和验证,并有一个合理的理解这一切,我认为如何工作。

我现在正在发送的值是使用它我应该使用公共密钥来解密以获得一个可用值私钥加密的RSA。

我似乎无法弄清楚如何做到这一点。我是个白痴吗?这是一个正常的事情吗?

发送给我的人告诉我,这在PHP中没有问题。我不知道和尚未使用PHP。我无法找到一个图书馆来使用我所知道的任何主要语言,即C++,Java,C#。我正在使用的服务器使用.Net。

我希望有人能够帮助我。

,如果有某种合理的解决方案,除了乞求他们改变他们在做什么,这将是巨大的。

这是我的方法(从我以前的坏一个更新铱星指出的),但是当我尝试解密值我得到一个异常

“而解码OAEP填充时发生错误。”

如果我使用rsa.Decrypt(bytes,false),我得到一个坏键异常。

public static string DecryptUsingPublic(string dataEncrypted, string publicKey) 
    { 
     if (dataEncrypted == null) throw new ArgumentNullException("dataEncrypted"); 
     if (publicKey == null) throw new ArgumentNullException("publicKey"); 
     try 
     { 
      RSAParameters _publicKey = LoadRsaPublicKey(publicKey, false); 
      RSACryptoServiceProvider rsa = InitRSAProvider(_publicKey); 

      byte[] bytes = Convert.FromBase64String(dataEncrypted); 
      byte[] decryptedBytes = rsa.Decrypt(bytes, true); 

      ArrayList arrayList = new ArrayList(); 
      arrayList.AddRange(decryptedBytes); 

      return Encoding.UTF8.GetString(decryptedBytes); 
     } 
     catch 
     { 
      return null; 
     } 
    } 

    private static RSAParameters LoadRsaPublicKey(String publicKeyFilePath, Boolean isFile) 
    { 
     RSAParameters RSAKeyInfo = new RSAParameters(); 
     byte[] pubkey = ReadFileKey(publicKeyFilePath, "PUBLIC KEY", isFile); 
     byte[] SeqOID = { 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00 }; 
     byte[] seq = new byte[15]; 
     // --------- Set up stream to read the asn.1 encoded SubjectPublicKeyInfo blob ------ 
     MemoryStream mem = new MemoryStream(pubkey); 
     BinaryReader binr = new BinaryReader(mem); //wrap Memory Stream with BinaryReader for easy reading 
     byte bt = 0; 
     ushort twobytes = 0; 

     try 
     { 

      twobytes = binr.ReadUInt16(); 
      if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81) 
       binr.ReadByte(); //advance 1 byte 
      else if (twobytes == 0x8230) 
       binr.ReadInt16(); //advance 2 bytes 
      else 
       return RSAKeyInfo; 

      seq = binr.ReadBytes(15);  //read the Sequence OID 
      if (!CompareBytearrays(seq, SeqOID)) //make sure Sequence for OID is correct 
       return RSAKeyInfo; 

      twobytes = binr.ReadUInt16(); 
      if (twobytes == 0x8103) //data read as little endian order (actual data order for Bit String is 03 81) 
       binr.ReadByte(); //advance 1 byte 
      else if (twobytes == 0x8203) 
       binr.ReadInt16(); //advance 2 bytes 
      else 
       return RSAKeyInfo; 

      bt = binr.ReadByte(); 
      if (bt != 0x00)  //expect null byte next 
       return RSAKeyInfo; 

      twobytes = binr.ReadUInt16(); 
      if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81) 
       binr.ReadByte(); //advance 1 byte 
      else if (twobytes == 0x8230) 
       binr.ReadInt16(); //advance 2 bytes 
      else 
       return RSAKeyInfo; 

      twobytes = binr.ReadUInt16(); 
      byte lowbyte = 0x00; 
      byte highbyte = 0x00; 

      if (twobytes == 0x8102) //data read as little endian order (actual data order for Integer is 02 81) 
       lowbyte = binr.ReadByte(); // read next bytes which is bytes in modulus 
      else if (twobytes == 0x8202) 
      { 
       highbyte = binr.ReadByte(); //advance 2 bytes 
       lowbyte = binr.ReadByte(); 
      } 
      else 
       return RSAKeyInfo; 
      byte[] modint = { lowbyte, highbyte, 0x00, 0x00 }; //reverse byte order since asn.1 key uses big endian order 
      int modsize = BitConverter.ToInt32(modint, 0); 

      byte firstbyte = binr.ReadByte(); 
      binr.BaseStream.Seek(-1, SeekOrigin.Current); 

      if (firstbyte == 0x00) 
      { //if first byte (highest order) of modulus is zero, don't include it 
       binr.ReadByte(); //skip this null byte 
       modsize -= 1; //reduce modulus buffer size by 1 
      } 

      byte[] modulus = binr.ReadBytes(modsize); //read the modulus bytes 

      if (binr.ReadByte() != 0x02)   //expect an Integer for the exponent data 
       return RSAKeyInfo; 
      int expbytes = (int)binr.ReadByte();  // should only need one byte for actual exponent data (for all useful values) 
      byte[] exponent = binr.ReadBytes(expbytes); 


      RSAKeyInfo.Modulus = modulus; 
      RSAKeyInfo.Exponent = exponent; 

      return RSAKeyInfo; 
     } 
     catch (Exception) 
     { 
      return RSAKeyInfo; 
     } 

     finally { binr.Close(); } 
     //return RSAparams; 

    } 

private static RSACryptoServiceProvider InitRSAProvider(RSAParameters rsaParam) 
    { 
     // 
     // Initailize the CSP 
     // Supresses creation of a new key 
     // 
     CspParameters csp = new CspParameters(); 
     //csp.KeyContainerName = "RSA Test (OK to Delete)"; 

     const int PROV_RSA_FULL = 1; 
     csp.ProviderType = PROV_RSA_FULL; 

     const int AT_KEYEXCHANGE = 1; 
     // const int AT_SIGNATURE = 2; 
     csp.KeyNumber = AT_KEYEXCHANGE; 
     // 
     // Initialize the Provider 
     // 
     RSACryptoServiceProvider rsa = 
      new RSACryptoServiceProvider(csp); 
     rsa.PersistKeyInCsp = false; 

     // 
     // The moment of truth... 
     // 
     rsa.ImportParameters(rsaParam); 
     return rsa; 
    } 

    private static int GetIntegerSize(BinaryReader binr) 
    { 
     byte bt = 0; 
     byte lowbyte = 0x00; 
     byte highbyte = 0x00; 
     int count = 0; 
     bt = binr.ReadByte(); 
     if (bt != 0x02)  //expect integer 
      return 0; 
     bt = binr.ReadByte(); 

     if (bt == 0x81) 
      count = binr.ReadByte(); // data size in next byte 
     else 
      if (bt == 0x82) 
      { 
       highbyte = binr.ReadByte(); // data size in next 2 bytes 
       lowbyte = binr.ReadByte(); 
       byte[] modint = { lowbyte, highbyte, 0x00, 0x00 }; 
       count = BitConverter.ToInt32(modint, 0); 
      } 
      else 
      { 
       count = bt;  // we already have the data size 
      } 

     while (binr.ReadByte() == 0x00) 
     { //remove high order zeros in data 
      count -= 1; 
     } 
     binr.BaseStream.Seek(-1, SeekOrigin.Current);  //last ReadByte wasn't a removed zero, so back up a byte 
     return count; 
    } 

    private static bool CompareBytearrays(byte[] a, byte[] b) 
    { 
     if (a.Length != b.Length) 
      return false; 
     int i = 0; 
     foreach (byte c in a) 
     { 
      if (c != b[i]) 
       return false; 
      i++; 
     } 
     return true; 
    } 

两种方法上述InitRSAProvider和LoadRsaPublicKey被得到了教程以允许PEM键字字符串与.net使用。

+0

说什么?这正是算法应该做的。使用私钥解密使用相应公钥加密的内容。无论你是困惑的大时间还是我! C#完全支持RSA :) –

+0

对不起。编辑我的问题。我完全写错了。星期五大脑开始争吵。 –

+0

你不能弄清楚什么?你说你已经有了使用公钥解密的功能。 – James

回答

12

查看了一些有关RSA加密模式的信息之后,就会发现PKCS#1 v1。5(你使用它,因为你打电话Decrypt(..., false)

” ......可以在长度的消息多达K操作 - 11个字节(k是RSA的八位字节长度模量)“

(RFC 3447,强调我的)。

根据错误消息(表明您的密钥是128字节),这意味着您无法使用PKCS#1 v1.5在超过128 - 11的消息上执行RSA(en | de)加密= 117个字节。

不应使用RSA直接对消息进行加密,而应使用对称算法来加密消息正文,并使用RSA仅对对称加密密钥进行加密。只有当您的邮件相当短(即您的密钥大小低于117字节)时,才应考虑直接使用RSA加密邮件。

我已经添加了以下假设你输入Base64编码如您在下面的评论指出:

public string DecryptUsingPublic(string dataEncryptedBase64, string publicKey) 
    { 
     if (dataEncryptedBase64 == null) throw new ArgumentNullException("dataEncryptedBase64"); 
     if (publicKey == null) throw new ArgumentNullException("publicKey"); 
     try 
     { 
      RSAParameters _publicKey = LoadRsaPublicKey(publicKey, false); 
      RSACryptoServiceProvider rsa = InitRSAProvider(_publicKey); 

      byte[] bytes = Convert.FromBase64String(dataEncryptedBase64); 
      byte[] decryptedBytes = rsa.Decrypt(bytes, false); 

      // I assume here that the decrypted data is intended to be a 
      // human-readable string, and that it was UTF8 encoded. 
      return Encoding.UTF8.GetString(decryptedBytes); 
     } 
     catch 
     { 
      return null; 
     } 
    } 
+0

感谢您的回复。我一整天都在阅读RFC 3447,试图找到解决方案。主要问题是我无法控制数值的加密方式。基本上我给了一个值,即值= BASE64 [RSA_PRIVATE_ENCRYPT(some_text)]我已经知道如何解密和使用这个值。我尝试过很多东西,但没有快乐。我已经要求为发送给我的人解密的一个工作示例。如果我得到它,我会在这里发布。 –

+0

这不是你的代码片断所暗示的,因为你没有Base64解码。 (你不应该为此使用Encoding.ASCII,因为它只有7位)。如果你的输入是Base64,你应该使用Convert.FromBase64String(dataEncrypted)来获得一个字节数组,以便与RSA函数一起使用。 – Iridium

+0

尽管我的原始答案仍然适用,但我已经在我的答案中添加了一些代码,可以解决您的问题,这是基于假设输入实际上是Base64编码,就像您在上面的注释中提到的那样。 – Iridium

13

RSA内置于.NET:System.Security.Cryptography.RSA

使用公共密钥加密和用私钥解密是最常见的人们使用非对称算法之一,它允许任何人安全地发送你的东西。

如果你做它的其他方式:使用加密私钥,并用公钥则证明了该消息是由私有密钥的所有者发送的解密。但是因为任何人都可以获得公钥,所以人们不倾向于加密整个消息,而只是使用私钥对数据进行哈希签名。因此RSACryptoServiceProviderSign__Verify__方法来做到这一点。

不过,也有Encrypt/Decrypt方法,如果你的伴侣坚持。

话说,我已经找到了微软加密类有点棘手,处理,并在某些领域缺乏,更喜欢Bouncy Castle libraries

+0

对不起,我是个白痴。我完全倒退了我的问题。我已经编辑它来显示我的意思。 –

+1

这么想!更新了答案。 –

+0

非常感谢您的回答。在这种情况下,我似乎无法获得解密方法。我已更新我的问题以显示出错的地方。我可能会做一些愚蠢的事情,只要我弄清楚它是什么,就会将其标记为答案。 –

6

RSA是意味着加密任意数据,甚至更少的任意数据长度(如@铱星已经告诉你了)。限制取决于使用的填充,并且使用填充很重要(足以使MS不会让您直接拨打EncryptValueDecryptValue)。

right这样做的方法是使用对称密码(如AES)对您的字符串进行加密,然后使用RSA公钥对密钥进行加密。

另一方将能够使用RSA私钥解密秘密(AES)密钥。然后用密钥解密你的字符串。

我有一个旧的(但仍然是最新的)blog entry关于包括源代码(C#)的主题。

+0

感谢您的回应。我完全同意你的看法。问题是我没有加密值。我有一个私钥和一个公钥。我正在发送一个使用私钥加密的值,现在我必须弄清楚如何解密它。这可能没有道理,并且可能没有简单的解决方案,但我对此不是很有经验,所以我非常感谢所有意见和建议。 –

+1

喜欢你的博客的方式。 –

+0

如果你坚持**解密**结束,那么你没有选择(安全或没有),而不是做反向操作来取回数据。希望它不会要求你使用'DecryptValue'(它不应该如果其他的,加密,派对使用.NET),但即使如此,有解决方案:)让我们知道! – poupou