【问题标题】:How to encrypt and decrypt a string with manually set RSA parameters? Why RSACryptoServiceProvider is throwing?如何使用手动设置的 RSA 参数加密和解密字符串?为什么 RSACryptoServiceProvider 抛出?
【发布时间】:2018-08-19 06:10:31
【问题描述】:

我正在尝试手动设置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

【问题讨论】:

  • D 参数通常称为解密指数。您需要它解密数据。这就是为什么我们说 RSA 是非对称加密的一个例子:不对称是一个密钥用于加密,另一个密钥用于解密。

标签: c# encryption .net-core rsa


【解决方案1】:

RSAParameters 的字段必须是什么样子的详细信息由用户 bartonjs(他也是同名的 stackoverflow 用户)在页面底部的反馈部分给出。这是他的评论的副本:

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

  • 模数:RSA 模数的无符号大端表示 价值。 Modulus[0] 的值不得为 0x00。
  • 指数:RSA 公共指数值的无符号大端表示。 Exponent[0] 的值不得为 0x00。

其他字段必须要么全部为空(公钥参数),要么全部为非空(私钥参数)。当值为非空时:

  • D:RSA 私有指数值的无符号大端表示。此数组的长度必须与 Modulus 值的长度相等,必要时在低索引处插入 0x00 值字节。
  • P:RSA 素数 p 的无符号大端表示。该数组的长度必须等于 Modulus 值长度的一半(必要时向上取整),在低位插入 0x00 值字节 必要时索引。
  • Q:RSA 素数 q 的无符号大端表示。该数组的长度必须等于 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:空 价值观。

私钥:

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

请注意,BigInteger.ToByteArray() 返回小端表示。这必须反转以获得RSAParameters 所需的大端表示。

【讨论】:

  • 天哪,在反馈部分?不确定这是否能解决我的问题(将测试并返回),但感谢您提供链接。我认为这是一个字节顺序问题。
  • 大声笑,我的反应完全正确。
【解决方案2】:

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

相反,我建议您使用RSA.FromXMLString() 方法来导入RSA 公钥和私钥。这似乎基于一个名为XML Signature Syntax and Processing Version 1.1 的外部标准,尤其是this section 以及更多详细信息here。基本思想是将整数编码为最小长度的大端字节数组(没有前导零),然后对其进行 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!");
        }
    }
}

【讨论】:

  • 很遗憾,这行不通。 github.com/dotnet/core/issues/874 但是用这个来补充你的答案,工作:stackoverflow.com/questions/46415078/… 老实说,我以前从未对 C# 感到如此痛苦。我感觉在 C++ 领域。非常感谢詹姆斯,我希望我能给你 500 分。
  • 呃,真是一场噩梦。我查看了表明支持 .NET Core 2.0 和 2.1 的 Microsoft 文档。如果您忽略 P 的大小必须恰好是模数大小的一半的限制,则其他答案可能有效。我真的不知道。
  • 大小限制不是 .NET,而是 CAPI。唯一输入到 blob 的大小是 KeySize,然后其他所有内容都得到派生的偏移量。
  • @bartonjs:那么,它可以使用 128 字节模数和 65 字节 P 吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-12-08
  • 2013-06-08
相关资源
最近更新 更多