1

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.

bartonjs
  • 30,352
  • 2
  • 71
  • 111
J'ai Zarini
  • 11
  • 1
  • 2
  • Programming questions are not within scope here at SU. I have voted to migrate this question to Stackoverflow. If you are question banned over at SO this question cannot be migrated and would then have to be closed. The proper action will be taken. You shouldn’t submit this question at SO yourself – Security Hound Oct 27 '17 at 03:13
  • 1
    Replace "SHA256" in that line with `new SHA256CryptoServiceProvider()`. – President James K. Polk Oct 27 '17 at 19:33

1 Answers1

0

###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://www.rfc-editor.org/rfc/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.

Community
  • 1
  • 1
bartonjs
  • 30,352
  • 2
  • 71
  • 111