如果您刚刚从私钥文件的Base64编码中提取了字节,则会得到一个PKCS#1、PKCS#8或加密的PKCS#8私钥blob(取决于它是否说“BEGIN RSA PRIVATE KEY”,“BEGIN PRIVATE KEY”或“BEGIN ENCRYPTED PRIVATE KEY”)。
ImportCspBlob
需要一种自定义格式的数据,这就是它抱怨的原因。
在不使用BouncyCastle的情况下使用c#进行数字签名有解释方法的说明。最简单/最公式化的方法是只需使用证书和密钥创建PFX,然后让
X509Certificate2
构造函数完成其工作即可。
如果您直接加载密钥对象,则配对私钥与证书的方式是使用新的
CopyWithPrivateKey
扩展方法之一。这将返回一个新的
X509Certificate2
实例,该实例知道私钥。
PrivateKey
设置器已从.NET Core中“删除”,因为它在Windows上具有许多副作用,在Linux和macOS上很难复制,特别是如果您从X509Store实例中检索了证书。
这段代码对于真正的BER规则来说既过于严格又过于接受,但是除非它们包含属性,否则应该可以读取有效编码的PKCS#8文件。
private static readonly byte[] s_derIntegerZero = { 0x02, 0x01, 0x00 };
private static readonly byte[] s_rsaAlgorithmId =
{
0x30, 0x0D,
0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01,
0x05, 0x00,
};
private static int ReadLength(byte[] data, ref int offset)
{
byte lengthOrLengthLength = data[offset++];
if (lengthOrLengthLength < 0x80)
{
return lengthOrLengthLength;
}
int lengthLength = lengthOrLengthLength & 0x7F;
int length = 0;
for (int i = 0; i < lengthLength; i++)
{
if (length > ushort.MaxValue)
{
throw new InvalidOperationException("This seems way too big.");
}
length <<= 8;
length |= data[offset++];
}
return length;
}
private static byte[] ReadUnsignedInteger(byte[] data, ref int offset, int targetSize = 0)
{
if (data[offset++] != 0x02)
{
throw new InvalidOperationException("Invalid encoding");
}
int length = ReadLength(data, ref offset);
if (length < 1 || data[offset] >= 0x80)
{
throw new InvalidOperationException("Invalid encoding");
}
byte[] ret;
if (length == 1)
{
ret = new byte[length];
ret[0] = data[offset++];
return ret;
}
if (data[offset] == 0)
{
offset++;
length--;
}
if (targetSize != 0)
{
if (length > targetSize)
{
throw new InvalidOperationException("Bad key parameters");
}
ret = new byte[targetSize];
}
else
{
ret = new byte[length];
}
Buffer.BlockCopy(data, offset, ret, ret.Length - length, length);
offset += length;
return ret;
}
private static void EatFullPayloadTag(byte[] data, ref int offset, byte tagValue)
{
if (data[offset++] != tagValue)
{
throw new InvalidOperationException("Invalid encoding");
}
int length = ReadLength(data, ref offset);
if (data.Length - offset != length)
{
throw new InvalidOperationException("Data does not represent precisely one value");
}
}
private static void EatMatch(byte[] data, ref int offset, byte[] toMatch)
{
if (data.Length - offset > toMatch.Length)
{
if (data.Skip(offset).Take(toMatch.Length).SequenceEqual(toMatch))
{
offset += toMatch.Length;
return;
}
}
throw new InvalidOperationException("Bad data.");
}
private static RSA DecodeRSAPkcs8(byte[] pkcs8Bytes)
{
int offset = 0;
EatFullPayloadTag(pkcs8Bytes, ref offset, 0x30);
EatMatch(pkcs8Bytes, ref offset, s_derIntegerZero);
EatMatch(pkcs8Bytes, ref offset, s_rsaAlgorithmId);
EatFullPayloadTag(pkcs8Bytes, ref offset, 0x04);
EatFullPayloadTag(pkcs8Bytes, ref offset, 0x30);
EatMatch(pkcs8Bytes, ref offset, s_derIntegerZero);
RSAParameters rsaParameters = new RSAParameters();
rsaParameters.Modulus = ReadUnsignedInteger(pkcs8Bytes, ref offset);
rsaParameters.Exponent = ReadUnsignedInteger(pkcs8Bytes, ref offset);
rsaParameters.D = ReadUnsignedInteger(pkcs8Bytes, ref offset, rsaParameters.Modulus.Length);
int halfModulus = (rsaParameters.Modulus.Length + 1) / 2;
rsaParameters.P = ReadUnsignedInteger(pkcs8Bytes, ref offset, halfModulus);
rsaParameters.Q = ReadUnsignedInteger(pkcs8Bytes, ref offset, halfModulus);
rsaParameters.DP = ReadUnsignedInteger(pkcs8Bytes, ref offset, halfModulus);
rsaParameters.DQ = ReadUnsignedInteger(pkcs8Bytes, ref offset, halfModulus);
rsaParameters.InverseQ = ReadUnsignedInteger(pkcs8Bytes, ref offset, halfModulus);
if (offset != pkcs8Bytes.Length)
{
throw new InvalidOperationException("Something didn't add up");
}
RSA rsa = RSA.Create();
rsa.ImportParameters(rsaParameters);
return rsa;
}