【问题标题】:How to fix bad length error for DecodeRSAPrivateKey?如何修复 DecodeRSAPrivateKey 的错误长度错误?
【发布时间】:2017-10-27 02:24:05
【问题描述】:

我收到此错误:

  1. 长度错误
  2. 对象引用未设置为对象的实例。

我正在使用此代码:

    public string RSASign(string data, string PhysicalApplicationPath)
    {
        RSACryptoServiceProvider rsaCsp = LoadCertificateFile(PhysicalApplicationPath);
        byte[] dataBytes = System.Text.Encoding.Default.GetBytes(data);
        byte[] signatureBytes = rsaCsp.SignData(dataBytes, "SHA256");    <--------error here:Object reference not set to an instance of an object.
        return BitConverter.ToString(signatureBytes).Replace("-", null);
    }

    byte[] GetPem(string type, byte[] data)
    {
        string pem = Encoding.UTF8.GetString(data);
        string header = String.Format("-----BEGIN {0}-----\\n", type);
        string footer = String.Format("-----END {0}-----", type);
        int start = pem.IndexOf(header) + header.Length;
        int end = pem.IndexOf(footer, start);
        string base64 = pem.Substring(start, (end - start));
        return Convert.FromBase64String(base64);
    }

    public byte[] HexToBytes(string hex)
    {
        hex = hex.Trim();

        byte[] bytes = new byte[hex.Length / 2];

        for (int index = 0; index < bytes.Length; index++)
        {
            bytes[index] = byte.Parse(hex.Substring(index * 2, 2), NumberStyles.HexNumber);
            //  Console.WriteLine("bytes: " + bytes);
        }

        return bytes;
    }

    RSACryptoServiceProvider LoadCertificateFile(string filename)
    {
        using (System.IO.FileStream fs = System.IO.File.OpenRead(filename))
        {
            byte[] data = new byte[fs.Length];
            byte[] res = null;
            fs.Read(data, 0, data.Length);
            if (data[0] != 0x30)
            {
                res = GetPem("PRIVATE KEY", data);
            }
            try
            {
                RSACryptoServiceProvider rsa = DecodeRSAPrivateKey(res);

                return rsa;


            }
            catch (Exception ex)
            {
                Console.WriteLine("ex :" + ex);

            }

            return null;
        }
    }

    bool verbose = false;

    public RSACryptoServiceProvider DecodeRSAPrivateKey(byte[] privkey)
    {
        byte[] MODULUS, E, D, P, Q, DP, DQ, IQ;

        // --------- Set up stream to decode the asn.1 encoded RSA private key ------
        MemoryStream mem = new MemoryStream(privkey);
        BinaryReader binr = new BinaryReader(mem);  //wrap Memory Stream with BinaryReader for easy reading
        byte bt = 0;
        ushort twobytes = 0;
        int elems = 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 null;

            twobytes = binr.ReadUInt16();
            if (twobytes != 0x0102) //version number
                return null;
            bt = binr.ReadByte();
            if (bt != 0x00)
                return null;


            //------ all private key components are Integer sequences ----
            elems = GetIntegerSize(binr);
            MODULUS = binr.ReadBytes(elems);

            elems = GetIntegerSize(binr);
            E = binr.ReadBytes(elems);

            elems = GetIntegerSize(binr);
            D = binr.ReadBytes(elems);

            elems = GetIntegerSize(binr);
            P = binr.ReadBytes(elems);

            elems = GetIntegerSize(binr);
            Q = binr.ReadBytes(elems);

            elems = GetIntegerSize(binr);
            DP = binr.ReadBytes(elems);

            elems = GetIntegerSize(binr);
            DQ = binr.ReadBytes(elems);

            elems = GetIntegerSize(binr);
            IQ = binr.ReadBytes(elems);

            Console.WriteLine("showing components ..");
            if (verbose)
            {
                showBytes("\nModulus", MODULUS);
                showBytes("\nExponent", E);
                showBytes("\nD", D);
                showBytes("\nP", P);
                showBytes("\nQ", Q);
                showBytes("\nDP", DP);
                showBytes("\nDQ", DQ);
                showBytes("\nIQ", IQ);
            }

            // ------- create RSACryptoServiceProvider instance and initialize with public key -----
            CspParameters CspParameters = new CspParameters();
            CspParameters.Flags = CspProviderFlags.UseMachineKeyStore;
            RSACryptoServiceProvider RSA = new RSACryptoServiceProvider(1024, CspParameters);
            RSAParameters RSAparams = new RSAParameters();

            RSAparams.Modulus = MODULUS;
            RSAparams.Exponent = E;
            RSAparams.D = D;
            RSAparams.P = P;
            RSAparams.Q = Q;
            RSAparams.DP = DP;
            RSAparams.DQ = DQ;
            RSAparams.InverseQ = IQ;
            RSA.ImportParameters(RSAparams);
            return RSA;
        }
        catch (Exception ex)
        {
            Console.WriteLine("ex1 :" + ex);     <-----error here : bad length
            return null;
        }
        finally
        {
            binr.Close();
        }
    }

如果有任何指导、建议或帮助帮助我解决此问题,我将不胜感激。

【问题讨论】:

  • 编程问题不在 SU 的范围内。我已投票决定将此问题迁移到 Stackoverflow。如果您的问题在 SO 被禁止,则无法迁移此问题,然后必须关闭。将采取适当的行动。你不应该自己在 SO 提交这个问题
  • 将该行中的“SHA256”替换为new SHA256CryptoServiceProvider()

标签: c# encryption rsa public-key-encryption private-key


【解决方案1】:

###你的要求:

NullReferenceException 是由 DecodeRSAPrivateKey 中的异常处理程序返回 null 引起的,但调用者没有检查它。

您的解码需要针对 DER 编码与 CAPI 编码问题进行一些调整(.NET 继承了 CAPI 编码限制):

  1. Modulus 的前导字节不能是 0x00。 (如果下一个字节 >= 0x80,DER 要求插入它)。所以如果它在那里,你需要把它修剪掉。

  2. Exponent 的前导字节不能是 0x00。可能不是问题,因为您的指数可能是 [ 0x01, 0x00, 0x01 ],但防御性编码并没有什么坏处。

  3. D 必须与 Modulus 的长度完全相同。如果 D 太短,您需要用 0x00 值填充它(在“左侧”)。如果一个字节太长并且以 0x00 开头,则删除 0x00。

  4. P.Length = (Modulus.Length + 1) / 2。(RSA-1024 具有 Modulus.Length 128 和 P.Length 64。RSA-1032 具有 Modulus.Length 129 和 P.Length 65)。必要时用 0x00 填充左侧。如果 P 太长一个字节并且它以 0x00 开头,则删除 0x00。 (如果它仍然太长,那么 CAPI(因此 .NET)无法从参数中读取此密钥;P 和 Q 的长度不相似)

  5. Q.Length、DP.Length、DQ.Length 和 InverseQ.Length 必须都等于 P.Length。必要时用 0x00 左填充,必要时删除前导 0x00。

您似乎也在阅读 BEGIN PRIVATE KEY 文件 (PKCS#8),但您将其解释为 BEGIN RSA PRIVATE KEY (PKCS#1 RSAPrivateKey)。因此,您需要考虑 PrivateKeyInfo 结构 (https://www.rfc-editor.org/rfc/rfc5208#section-5) 中的标题部分。

###你可能想要什么:

如果您真的在阅读 PKCS#8,并且您使用的是 .NET Framework 4.6 或更高版本,只需让 CngKey 和 RSACng 为您完成工作:

RSA LoadKeyFile(string filename)
{
    using (System.IO.FileStream fs = System.IO.File.OpenRead(filename))
    {
        byte[] data = new byte[fs.Length];
        byte[] res = null;
        fs.Read(data, 0, data.Length);
        if (data[0] != 0x30)
        {
            res = GetPem("PRIVATE KEY", data);
        }

        try
        {
            using (CngKey key = CngKey.Import(res, CngKeyBlobFormat.Pkcs8PrivateBlob))
            {
                return new RSACng(key);
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine("ex :" + ex);
        }

        return null;
    }
}

然后你只需要改变你的调用代码就不再关心它有一个 RSACryptoServiceProvider,只是它是 RSA:

public string RSASign(string data, string PhysicalApplicationPath)
{
    RSA rsa = LoadCertificateFile(PhysicalApplicationPath);
    byte[] dataBytes = System.Text.Encoding.Default.GetBytes(data);
    byte[] signatureBytes = rsa.SignData(dataBytes, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
    return BitConverter.ToString(signatureBytes).Replace("-", null);
}

我将其保留为 PKCS1 签名填充,因为这就是 RSACryptoServiceProvider 的方法所做的。

【讨论】:

    猜你喜欢
    • 2014-03-31
    • 1970-01-01
    • 1970-01-01
    • 2021-12-12
    • 2018-09-11
    • 2012-08-13
    • 2019-11-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多