问题
I'm getting this error:
- Bad length
- 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