如何修复DecodeRSAPrivateKey的错误长度错误?

时间:2017-10-27 02:24:05

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

我收到此错误:

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

我正在使用此代码:

    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();
        }
    }

感谢您帮助我解决问题的任何指导,建议或帮助。

1 个答案:

答案 0 :(得分:0)

你要求:

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

对于DER编码与CAPI编码问题(.NET继承了CAPI编码限制),您的解码需要稍微调整一下:

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

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

3)D必须与模数完全相同。如果D太短,你需要用0x00值填充它(在&#34;左边&#34;)。如果它的一个字节太长而且以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://tools.ietf.org/html/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的方法所做的。