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

我知道我可能会得到的主要答案是你为什么要这么做?!

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

我有.Net编写的函数使用私钥解密,使用公钥加密。 我也签署并validation并对我认为这一切的工作方式有一个合理的理解。

我现在被发送一个使用私钥进行RSA加密的值,我应该通过使用公钥解密来获得可用值。

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

发送给我的人告诉我这在PHP中没问题。 我不知道,还没有使用过PHP。 我找不到一个库,用我所知的任何主要语言,即C ++,Java,C#。 我正在使用的服务器使用.Net。

我希望有人可以帮助我。

如果除了乞求他们改变他们正在做的事情之外还有某种合理的解决方案,那将是很棒的。

这是我的方法(根据Iridium指出的我之前的错误更新)但是当我尝试解密该值时,我得到了一个exception

“解码OAEP填充时出错。”

如果我使用rsa.Decrypt(bytes,false),我会得到一个错误的密钥exception。

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一起使用。

看了一些关于RSA加密模式的信息后,看起来PKCS#1 v1.5(你正在使用它,因为你正在调用Decrypt(..., false)

“…可以对长度达k-11个八位字节的消息进行操作(k是RSA模数的八位字节长度)”

(RFC 3447,强调我的)。

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

您应该使用对称算法来加密消息正文,并使用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; } } 

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

使用公钥进行加密并使用私钥进行解密是人们使用非对称算法最常见的事情之一,它允许任何人安全地向您发送信息。

如果你以另一种方式执行此操作:使用私钥加密,并使用公钥解密,则certificate该消息是由私钥持有者发送的。 但是因为任何人都可能掌握了公钥,人们不会加密整个消息,而只是使用私钥对数据的哈希进行签名。 因此, RSACryptoServiceProviderSign__Verify__方法来做到这一点。

如果您的合作伙伴坚持认为,仍有Encrypt/Decrypt方法。

说到这一点,我发现Microsoft加密类在处理方面有点棘手,在某些方面缺乏,而且更喜欢Bouncy Castle库 。

RSA 并不意味着加密任意数据,甚至更少的数据长度(如@Iridium已告诉你)。 限制取决于使用的填充和使用填充非常重要(足以让MS不会让你直接调用EncryptValueDecryptValue )。

正确的方法是使用对称密码(如AES)加密字符串,然后使用RSA公钥加密密钥。

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

我有一个关于这个主题的旧的(但仍然是最新的) 博客条目 , 其中包括源代码(C#)。