如何使用手动设置的RSA参数加密和解密字符串?为什么RSACryptoServiceProvider抛出?

时间:2018-08-19 06:10:32

标签: c# encryption .net-core rsa

我正在尝试手动设置RSAParameters的属性,但是在尝试加密,解密甚至实例化RSACryptoServiceProvider时遇到各种错误。

如果仅设置指数和模量,则可以对消息进行加密;但是当我尝试解密它时,我得到了Internal.Cryptography.CryptoThrowHelper.WindowsCryptographicException: 'Key does not exist

如果我设置了所有属性,则会得到一个System.Security.Cryptography.CryptographicException: 'The specified RSA parameters are not valid; both Exponent and Modulus are required fields.'

我的问题是为什么?我的参数出了什么问题?

我显然对字段使用随机值;我从另一个项目中复制粘贴了它们的字符串表示形式。

下面是我编写的代码,它针对.Net Core 2.1。 我对其进行了更新,以包含此length restrictions

namespace PlayingWithCryptography {
    using System;
    using System.Linq;
    using System.Numerics;
    using System.Security.Cryptography;
    using System.Text;

    public static class Program {
        private static void Main(string[] args) {
            var msg = "lol";
            var encodedMessage = Encoding.ASCII.GetBytes(msg);

            var parameters = new RSAParameters();

            parameters.P = BigInteger.Parse("14299623962416399520070177382898895550795403345466153217470516082934737582776038882967213386204600674145392845853859217990626450972452084065728686565928113").ToByteArray();

            parameters.Q = BigInteger.Parse("7630979195970404721891201847792002125535401292779123937207447574596692788513647179235335529307251350570728407373705564708871762033017096809910315212884101").ToByteArray();

            parameters.D = BigInteger.Parse("46730330223584118622160180015036832148732986808519344675210555262940258739805766860224610646919605860206328024326703361630109888417839241959507572247284807035235569619173792292786907845791904955103601652822519121908367187885509270025388641700821735345222087940578381210879116823013776808975766851829020659073").ToByteArray();

            parameters.Modulus = BigInteger.Parse("109120132967399429278860960508995541528237502902798129123468757937266291492576446330739696001110603907230888610072655818825358503429057592827629436413108566029093628212635953836686562675849720620786279431090218017681061521755056710823876476444260558147179707119674283982419152118103759076030616683978566631413").ToByteArray();

            parameters.Exponent = BigInteger.Parse("65537").ToByteArray();

            parameters.DP = BigInteger.Parse("11141736698610418925078406669215087697114858422461871124661098818361832856659225315773346115219673296375487744032858798960485665997181641221483584094519937").ToByteArray();

            parameters.DQ = BigInteger.Parse("4886309137722172729208909250386672706991365415741885286554321031904881408516947737562153523770981322408725111241551398797744838697461929408240938369297973").ToByteArray();

            parameters.InverseQ = BigInteger.Parse("5610960212328996596431206032772162188356793727360507633581722789998709372832546447914318965787194031968482458122348411654607397146261039733584248408719418").ToByteArray();

            Console.WriteLine($"M[0]={parameters.Modulus[0]}");
            Console.WriteLine($"D.Length={parameters.D.Length}");
            Console.WriteLine($"M.Length={parameters.Modulus.Length}");
            Console.WriteLine($"E={parameters.Exponent[0]}");
            Console.WriteLine($"P.Length={ parameters.P.Length}");
            Console.WriteLine($"Q.Length={ parameters.Q.Length}");
            Console.WriteLine($"DP.Length={ parameters.DP.Length}");
            Console.WriteLine($"DQ.Length={ parameters.DQ.Length}");
            Console.WriteLine($"InverseQ.Length={ parameters.InverseQ.Length}");

            // Adding zeros coz https://stackoverflow.com/questions/42098493/decrypting-with-rsa-encryption-in-vb-net/42117655#42117655
            parameters.D = new byte[] { 0 }.Concat(parameters.D).ToArray();
            parameters.DQ = new byte[] { 0 }.Concat(parameters.DQ).ToArray();
            parameters.InverseQ = new byte[] { 0 }.Concat(parameters.InverseQ).ToArray();
            Console.WriteLine();

            Console.WriteLine($"M[0]={parameters.Modulus[0]}");
            Console.WriteLine($"D.Length={parameters.D.Length}");
            Console.WriteLine($"M.Length={parameters.Modulus.Length}");
            Console.WriteLine($"E[0]={parameters.Exponent[0]}");
            Console.WriteLine($"P.Length={ parameters.P.Length}");
            Console.WriteLine($"Q.Length={ parameters.Q.Length}");
            Console.WriteLine($"DP.Length={ parameters.DP.Length}");
            Console.WriteLine($"DQ.Length={ parameters.DQ.Length}");
            Console.WriteLine($"InverseQ.Length={ parameters.InverseQ.Length}");

            var csp = RSACryptoServiceProvider.Create();

            csp.ImportParameters(parameters);

            var encrypted = csp.Encrypt(
                  data: encodedMessage,
                  padding: RSAEncryptionPadding.OaepSHA256);
            var decrypted = csp.Decrypt(
                  data: encrypted,
                  padding: RSAEncryptionPadding.OaepSHA256);

            var decoded = Encoding.ASCII.GetString(decrypted);
            Console.WriteLine(decoded);
            Console.WriteLine("Done!");
        }

    }
}

回答对于处于类似情况的任何人,请阅读(并投票)James K Polk的答案,然后阅读answer for this question

2 个答案:

答案 0 :(得分:1)

用户bartonjs(也是同名的stackoverflow用户)在“反馈”部分的页面底部给出了RSAParameters字段的外观详细信息。这是他的评论的副本:

RSAParameters值的数组的完整规则如下:

  • 模量:RSA模数的无符号大端表示 值。模数[0]的值不能为0x00。
  • 指数:RSA公共指数值的无符号big-endian表示形式。指数[0]的值不能为0x00。

其他字段必须全部为空(公用密钥参数)或全部非空(专用密钥参数)。当值不为空时:

  • D:RSA私有指数值的无符号big-endian表示形式。此数组的长度必须等于Modulus值的长度,并根据需要在低索引处插入0x00值的字节。
  • P:RSA素数p的无符号big-endian表示形式。该数组的长度必须等于Modulus值长度的一半(必要时向上舍入),并在低位插入0x00值字节 索引。
  • Q:RSA素数q的无符号big-endian表示形式。该数组的长度必须等于Modulus值长度的一半(如有必要,向上取整),并根据需要在低索引处插入0x00值字节。 DP:RSA CRT参数dp的无符号大端格式表示。此数组的长度必须等于Modulus值长度的一半(必要时向上取整),并在必要时在低索引处插入0x00值字节。
  • DQ:RSA CRT参数dq的无符号大端表示。此数组的长度必须等于Modulus值长度的一半(必要时向上取整),并在必要时在低索引处插入0x00值字节。
  • InverseQ:RSA CRT参数qInverse的无符号大端表示。此数组的长度必须等于Modulus值长度的一半(必要时向上取整),并在必要时在低索引处插入0x00值字节。

示例:对于KeySize值为2056且标准公共指数值为0x010001的RSA密钥,数组长度为:

公钥:

  • 模数:257个字节
  • 指数:3个字节
  • D,P,Q,DP,DQ,InverseQ:null 值。

私钥:

  • 模数:257个字节
  • 指数:3个字节
  • D:257个字节
  • P:129个字节
  • Q:129个字节
  • DP:129个字节
  • DQ:129个字节
  • InverseQ:129个字节

请注意,BigInteger.ToByteArray()返回little-endian表示形式。必须颠倒此顺序才能获得RSAParameters所要求的big-endian表示形式。

答案 1 :(得分:1)

我现在暂时不回答其他问题,但是我有理由怀疑其中引用的限制。对于问题中的参数,我的另一个答案表明RSAParameters.P必须正好是64个字节。但这不可能,因为问题中的P至少需要65个字节才能表示。

相反,我建议您使用RSA.FromXMLString()方法导入RSA公钥和私钥。这似乎是基于称为XML Signature Syntax and Processing Version 1.1的外部标准,尤其是this section和更多详细信息here。基本思想是将整数编码为最小长度的Big-endian字节数组-没有前导零-然后对其进行base64编码。

这里有一些经过严格测试的代码可以完成它。请注意,C#和.NET并不是我的强项,因此可以随时进行改进。请注意,在我的平台上,OAEPSha1是唯一受支持的OAEP填充。

using System;
using System.Numerics;
using System.Security.Cryptography;
using System.Text;

namespace PlayingWithCryptography
{
    public static class ConvertToRSAParameters
    {

        public static string ConvertXML(BigInteger e, BigInteger n)
        {
            var xml = new StringBuilder();
            xml.AppendLine(WrapTags(BigToBase64(n), "Modulus"));
            xml.AppendLine(WrapTags(BigToBase64(e), "Exponent"));
            WrapTags(xml, "RSAKeyValue");
            return xml.ToString();
        }

        public static string ConvertXML(BigInteger e, BigInteger n, BigInteger p, BigInteger q,
                                            BigInteger d, BigInteger dp, BigInteger dq, BigInteger inverseQ)
        {
            var xml = new StringBuilder();
            xml.AppendLine(WrapTags(BigToBase64(n), "Modulus"));
            xml.AppendLine(WrapTags(BigToBase64(e), "Exponent"));
            xml.AppendLine(WrapTags(BigToBase64(p), "P"));
            xml.AppendLine(WrapTags(BigToBase64(q), "Q"));
            xml.AppendLine(WrapTags(BigToBase64(d), "D"));
            xml.AppendLine(WrapTags(BigToBase64(dp), "DP"));
            xml.AppendLine(WrapTags(BigToBase64(dq), "DQ"));
            xml.AppendLine(WrapTags(BigToBase64(inverseQ), "InverseQ"));
            WrapTags(xml, "RSAKeyValue");
            return xml.ToString();

        }

        private static string BigToBase64(BigInteger val)
        {
            var valBytes = val.ToByteArray();
            int len = valBytes.Length;
            while (valBytes[len - 1] == 0)
            {
                --len;
                if (len == 0)
                {
                    break;
                }
            }
            Array.Resize(ref valBytes, len);
            Array.Reverse(valBytes);
            return System.Convert.ToBase64String(valBytes);
        }

        private static string WrapTags(string target, string tag)
        {
            return String.Format("<{0}>{1}</{0}>", tag, target);
        }

        private static StringBuilder WrapTags(StringBuilder target, string tag)
        {
            return target.Insert(0, String.Format("<{0}>", tag)).AppendFormat("</{0}>", tag);
        }

        private static void Main(string[] args)
        {
            var msg = "lol";
            var encodedMessage = Encoding.ASCII.GetBytes(msg);

            Console.WriteLine();

            var publicRsa = RSA.Create();

            publicRsa.FromXmlString(
                ConvertToRSAParameters.ConvertXML(
                    BigInteger.Parse("65537"),
                    BigInteger.Parse("109120132967399429278860960508995541528237502902798129123468757937266291492576446330739696001110603907230888610072655818825358503429057592827629436413108566029093628212635953836686562675849720620786279431090218017681061521755056710823876476444260558147179707119674283982419152118103759076030616683978566631413")
                )
            );

            var privateRsa = RSA.Create();

            privateRsa.FromXmlString(
                ConvertToRSAParameters.ConvertXML(
                    BigInteger.Parse("65537"),
                    BigInteger.Parse("109120132967399429278860960508995541528237502902798129123468757937266291492576446330739696001110603907230888610072655818825358503429057592827629436413108566029093628212635953836686562675849720620786279431090218017681061521755056710823876476444260558147179707119674283982419152118103759076030616683978566631413"),
                    BigInteger.Parse("14299623962416399520070177382898895550795403345466153217470516082934737582776038882967213386204600674145392845853859217990626450972452084065728686565928113"),
                    BigInteger.Parse("7630979195970404721891201847792002125535401292779123937207447574596692788513647179235335529307251350570728407373705564708871762033017096809910315212884101"),
                    BigInteger.Parse("46730330223584118622160180015036832148732986808519344675210555262940258739805766860224610646919605860206328024326703361630109888417839241959507572247284807035235569619173792292786907845791904955103601652822519121908367187885509270025388641700821735345222087940578381210879116823013776808975766851829020659073"),
                    BigInteger.Parse("11141736698610418925078406669215087697114858422461871124661098818361832856659225315773346115219673296375487744032858798960485665997181641221483584094519937"),
                    BigInteger.Parse("4886309137722172729208909250386672706991365415741885286554321031904881408516947737562153523770981322408725111241551398797744838697461929408240938369297973"),
                    BigInteger.Parse("5610960212328996596431206032772162188356793727360507633581722789998709372832546447914318965787194031968482458122348411654607397146261039733584248408719418")
                )
            );

            var encrypted = publicRsa.Encrypt(
                  data: encodedMessage,
                  padding: RSAEncryptionPadding.OaepSHA1);
            var decrypted = privateRsa.Decrypt(
                  data: encrypted,
                  padding: RSAEncryptionPadding.OaepSHA1);

            var decoded = Encoding.ASCII.GetString(decrypted);
           Console.WriteLine(decoded);
            Console.WriteLine("Done!");
        }
    }
}