c#中的数字签名而不使用BouncyCastle

Posted

技术标签:

【中文标题】c#中的数字签名而不使用BouncyCastle【英文标题】:Digital signature in c# without using BouncyCastle 【发布时间】:2018-08-01 00:09:02 【问题描述】:

不使用第 3 方 BouncyCastle 库,有没有办法读取自定义私钥并签署消息? (sha256 hash+使用私钥加密)

【问题讨论】:

【参考方案1】:

从技术上讲,是的。根据您拥有的密钥类型,答案会变得更加棘手。

编辑(2019 年至 10 月):.NET Core 3.0 以 DER 编码(相对于 PEM 编码)形式内置了对所有这些格式的支持。我在每种文件格式的子标题后添加了 .NET Core 3.0+ 答案。

PKCS#8 PrivateKeyInfo (PEM "BEGIN PRIVATE KEY")

如果您有这种类型的文件,并且您使用的是 .NET 4.6 或更高版本,那么可以。您需要使用 DER 编码(相对于 PEM 编码)数据 blob(如果是 PEM,请参见下文)。

using (CngKey key = CngKey.Import(blob, CngKeyBlobFormat.Pkcs8PrivateBlob))
using (RSA rsa = new RSACng(key))

    return rsa.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);

RSA 需要 4.6,ECDSA 需要 4.6.1,DSA 需要 4.6.2。

.NET Core 3.0+ PKCS#8 PrivateKeyInfo

ImportPkcs8PrivateKey 方法在 AsymmetricAlgorithm 上声明,所有非对称内置类型(RSADSAECDsaECDiffieHellman)都支持它。

using (RSA rsa = RSA.Create())

    rsa.ImportPkcs8PrivateKey(blob, out _);
    return rsa.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);

PKCS#8 EncryptedPrivateKeyInfo (PEM "BEGIN ENCRYPTED PRIVATE KEY")

恭喜,您的私钥传输功能强大。可悲的是,如果您想实际处理它,这需要编写最大数量的代码。你不想处理它。你真的,真的,想要

为密钥创建证书 将证书和密钥放入 PFX 文件中 将 PFX 加载到 X509Certificate2 中 使用 cert.GetRSAPrivateKey()、cert.GetDSAPrivateKey() 或 cert.GetECDsaPrivateKey()(视情况而定)

请参阅How is a private key encrypted in a pem certificate?,然后继续阅读下一节的艰难入门知识。不过,你要做的工作比它所说的要多得多。您需要读取文件,了解加密方案和参数,解密 blob,然后使用 CNG 读取 PKCS#8,或者继续潜入兔子洞并享受您的文件解析器。

.NET Core 3.0+ PKCS#8 EncryptedPrivateKeyInfo

ImportEncryptedPkcs8PrivateKey 方法在 AsymmetricAlgorithm 上声明,所有非对称内置类型(RSADSAECDsaECDiffieHellman)都支持它。

using (RSA rsa = RSA.Create())

    rsa.ImportEncryptedPkcs8PrivateKey(password, blob, out _);
    return rsa.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);

PKCS#1 RSAPrivateKey (PEM "BEGIN RSA PRIVATE KEY")

不幸的是,你正处于“相对简单”和“相对困难”的不幸交汇处,这被数学专业的学生称为“留给读者的练习”。

强烈考虑从 EncryptedPrivateKeyInfo 执行 PFX 方法。或者,您可以在自定义代码中执行此操作。自定义代码?好的,让我们这样做。此时您需要的参考文本是

    ITU.T-REC X.680-201508。
这定义了 ASN.1 语言,它告诉您如何读取 RSAPrivateKey (et al) 对象结构定义。 对于 RSAPrivateKey,这主要是可选的,因为它使用的 SEQUENCE 没有太多细微差别,而且 INTEGER 非常简单。
    ITU.T-REC X.690-201508
本文档描述了 ASN.1 的 BER(和 CER)和 DER 编码规则。 这些密钥文件位于 DER 中。 (除非他们在 PEM 中,但我们会尽快解决)
    适合您的对象类型的 RFC。
RSAPrivateKey (RFC 3447) EncryptedPrivateKeyInfo (RFC 5208) PrivateKeyInfo(也是 RFC 5208) 其他格式在其他 RFC 中。

好的,我们继续。

    如果文件是 PEM 编码的(“-----BEGIN RSA PRIVATE KEY-----”或“-----BEGIN PRIVATE KEY-----”等),您需要“un -PEM”它。
PEM 格式为 (换行符或文件开头) 5 个连字符、BEGIN、空格、类型标识符、5 个连字符、换行符 base64 编码的有效负载(每 72 个文本字符后有换行符) 换行符(除非您以换行符结尾,因为您是 72 个文本字符的倍数) 5个连字符,END,与之前相同的类型标识符,5个连字符 我们想要的部分是有效载荷。通过 Convert.FromBase64String 运行它,现在我们有了 DER 编码的 byte[] 键对象。
    使用类型定义和 ITU 文档,为您的密钥文件格式编写解析器。 解析密钥。 将解析后的密钥转换为 RSAParameters 对象(或 DSAParameters 或 ECParameters,视情况而定) 调用 RSA.Create()(等) 通过 ImportParameters 方法加载密钥。 很好。

对于第 4 步,需要注意一些事项。具体来说,ASN.1/DER INTEGER 组件有两个 RSAParameters 不喜欢的规则。

所有前导 0x00 值都被删除。 如果前导字节设置了高位 (>=0x80),但数字应该是正数,则插入 0x00。

.NET 希望将值作为大端字节数组(与 DER 编码的字节顺序相同)具有以下关系:

只要不以 0x00 开头,指数就可以尽可能大。 只要不以 0x00 开头,模数就可以任意大。 D 的大小必须与模数相同(根据需要插入 0x00) P 必须是模数 ((Modulus.Length + 1) / 2) 大小的“半舍入”,必要时插入 0x00。 Q、DP、DQ 和 InverseQ 的长度必须与 P 相同。(根据需要插入 0x00)。

.NET Core 3.0+ PKCS#1 RSAPrivateKey

ImportRSAPrivateKey 方法在 RSA 上声明,由于它解析数据并调用 ImportParameters,它适用于所有 RSA 派生类型(假设它们已经支持参数导入)。

using (RSA rsa = RSA.Create())

    rsa.ImportRSAPrivateKey(blob, out _);
    return rsa.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);

其他格式

确定 RFC 为您的密钥格式定义的 ASN.1 结构,然后牢记这一点并评估 RSAPrivateKey 部分。

DSAParameters 和 ECParameters 都有自己的空间期望。

进一步阅读

其中一些包括并非总是优雅但经常运行的代码:

Export private/public keys from X509 certificate to PEM How to parse(Convert to RSAParameters) X.509 private key in C#? How to fix bad length error for DecodeRSAPrivateKey? How to decrypt using rsa from PEM file

【讨论】:

【参考方案2】:

Microsoft 提供了一个类 SignedXML 来对文件进行签名。想了解更多,请查看https://msdn.microsoft.com/en-us/library/system.security.cryptography.xml.signedxml(v=vs.110).aspx

【讨论】:

以上是关于c#中的数字签名而不使用BouncyCastle的主要内容,如果未能解决你的问题,请参考以下文章

使用唯一随机值更新sql表中的所有行,而不使用c#中的主键或唯一键

从 C# 中的列表创建逗号分隔列表而不终止逗号

为啥 C# 中的基类允许实现接口契约而不继承它?

如何在 C# 中的 Windows CE 5.0 (Build 1400) 中播放 .ogg、mp3,而不使用 Directsound 库?

使用自定义组和小数分隔符将数字格式化为字符串而不更改精度

如何使用 Windows 10 附带的 Microsoft Print To PDF 打印机以编程方式打印到 PDF 文件而不提示 C# 中的文件名