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

时间:2011-10-21 09:54:54

标签: c# .net encryption cryptography rsa

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

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

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

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

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

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

我希望有人可以帮助我。

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

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

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

3 个答案:

答案 0 :(得分:14)

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

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

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

如果你的伴侣坚持,还有Encrypt/Decrypt个方法。

说,我发现Microsoft加密类在处理和某些方面缺乏时有点棘手,而且更喜欢Bouncy Castle libraries

答案 1 :(得分:12)

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

  

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

(RFC 3447,强调我的)。

基于错误消息,表明您的密钥是128字节,这意味着您无法在超过128 - 11 =的消息上使用PKCS#1 v1.5执行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;
        }
    }

答案 2 :(得分:6)

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

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

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

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