我收到此错误:
- 错误的长度
- 未将对象引用设置为对象的实例。
醇>
我正在使用此代码:
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();
}
}
感谢您帮助我解决问题的任何指导,建议或帮助。
答案 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的方法所做的。