How to fix bad length error for DecodeRSAPrivateKey?

懵懂的女人 提交于 2019-12-08 07:21:18

问题


I'm getting this error:

  1. Bad length
  2. Object reference not set to an instance of an object.

I'm using this code:

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

I would appreciate any guidance, advice, or assistance in helping me get this resolved.


回答1:


What you asked for:

The NullReferenceException is caused by your exception handler in DecodeRSAPrivateKey returning null, but the caller not checking for it.

Your decode needs to be adjusted a bit for DER encoding vs CAPI encoding concerns (.NET has inherited the CAPI encoding restrictions):

1) The leading byte of Modulus must not be 0x00. (DER requires it to be inserted if the next byte is >= 0x80). So if it's there, you need to trim it out.

2) The leading byte of Exponent must not be 0x00. Probably not an issue since your exponent is probably [ 0x01, 0x00, 0x01 ], but it doesn't hurt to code defensively.

3) D must be exactly the same length as Modulus. If D is too short you need to pad it (on "the left") with 0x00-values. If it's one byte too long and starts with 0x00, remove the 0x00.

4) P.Length = (Modulus.Length + 1) / 2. (RSA-1024 has Modulus.Length 128 and P.Length 64. RSA-1032 has Modulus.Length 129 and P.Length 65). Left pad with 0x00 as necessary. If P is one byte too long and it starts with 0x00, remove the 0x00. (If it's too long still, then CAPI (and therefore .NET) cannot read this key from parameters; P and Q aren't of similar lengths)

5) Q.Length, DP.Length, DQ.Length, and InverseQ.Length must all equal P.Length. Left pad with 0x00 as necessary, remove a leading 0x00 as necessary.

You also seem to be reading BEGIN PRIVATE KEY files (PKCS#8), but you're interpreting it like BEGIN RSA PRIVATE KEY (PKCS#1 RSAPrivateKey). So you need to account for the header portions in the PrivateKeyInfo structure (https://tools.ietf.org/html/rfc5208#section-5).

What you might want instead:

If you're really reading a PKCS#8, and you're on .NET Framework 4.6 or higher, just let CngKey and RSACng do the work for you:

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

Then you just need to change your calling code to stop caring that it got an RSACryptoServiceProvider, just that it's 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);
}

I left it as PKCS1 signature padding since that's what RSACryptoServiceProvider's method would have done.



来源:https://stackoverflow.com/questions/46968514/how-to-fix-bad-length-error-for-decodersaprivatekey

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!