简体   繁体   English

不使用BouncyCastle的C#中的数字签名

[英]Digital signature in c# without using BouncyCastle

Without using 3rd party BouncyCastle library, is there a way to read a custom private key and sign the message ? 在不使用第三方BouncyCastle库的情况下,有没有办法读取自定义私钥并对消息签名? (sha256 hash+encryption using private key) (使用私钥进行sha256哈希+加密)

Technically, yes. 从技术上讲,是的。 Depending on what kind of key you have the answer gets more tricky. 根据您拥有哪种键,答案会变得更加棘手。

Edit (2019-Oct): .NET Core 3.0 has built-in support for all of these formats, in their DER-encoded (vs PEM-encoded) forms. 编辑(2019年10月):. NET Core 3.0以DER编码(相对于PEM编码)形式对所有这些格式均提供内置支持。 I'm adding the .NET Core 3.0+ answers after a sub-heading within each file format. 我在每种文件格式的子标题后添加.NET Core 3.0+答案。

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

If you have this type of file, and you're on .NET 4.6 or higher, then yes. 如果您拥有这种类型的文件,并且您使用的是.NET 4.6或更高版本,则可以。 You need to have the DER encoded (vs PEM encoded) data blob (see below if it's PEM). 您需要具有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);
}

4.6 is required for for RSA, 4.6.1 for ECDSA, 4.6.2 for DSA. RSA需要4.6,ECDSA需要4.6.1,DSA需要4.6.2。

.NET Core 3.0+ PKCS#8 PrivateKeyInfo .NET Core 3.0+ PKCS#8 PrivateKeyInfo

The ImportPkcs8PrivateKey method is declared on AsymmetricAlgorithm , and all asymmetric built-in types ( RSA , DSA , ECDsa , ECDiffieHellman ) support it. 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") PKCS#8 EncryptedPrivateKeyInfo(PEM“开始加密的私钥”)

Congratulations, your private key transport is strong. 恭喜,您的私钥传输很强大。 Sadly, this requires the maximum amount of code to be written if you want to actually handle it. 可悲的是,如果您想实际处理它,则需要编写最大数量的代码。 You don't want to handle it. 您不想处理它。 You really, really, want to 你真的,真的,想要

  • Create a certificate for the key 创建密钥证书
  • Put the cert and key into a PFX file 将证书和密钥放入PFX文件
  • Load the PFX into an X509Certificate2 将PFX加载到X509Certificate2中
  • Use cert.GetRSAPrivateKey(), cert.GetDSAPrivateKey(), or cert.GetECDsaPrivateKey() (as appropriate) 根据需要使用cert.GetRSAPrivateKey(),cert.GetDSAPrivateKey()或cert.GetECDsaPrivateKey()

See How is a private key encrypted in a pem certificate? 请参阅如何在pem证书中加密私钥? , and then continue to the next section for the primer on the hard way. ,然后继续进行下一部分的入门。 You have a lot more work than it will talk about, though. 但是,您要做的工作比谈论的要多得多。 You need to read the file, understand the encryption scheme and parameters, decrypt the blob, then use CNG for reading the PKCS#8, or just keep diving down the rabbit hole and enjoy your file parser. 您需要阅读文件,了解加密方案和参数,解密Blob,然后使用CNG读取PKCS#8,或者只是继续钻探兔子洞并享受文件解析器的乐趣。

.NET Core 3.0+ PKCS#8 EncryptedPrivateKeyInfo .NET Core 3.0+ PKCS#8 EncryptedPrivateKeyInfo

The ImportEncryptedPkcs8PrivateKey method is declared on AsymmetricAlgorithm , and all asymmetric built-in types ( RSA , DSA , ECDsa , ECDiffieHellman ) support it. 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") PKCS#1 RSAPrivateKey(PEM“ BEGIN RSA PRIVATE KEY”)

You're at the unfortunate confluence of "relatively simple" and "relatively hard" that is known to math majors as "an exercise left to the reader". 不幸的是,“相对简单”和“相对困难”的融合被数学专业的学生称为“留给读者的练习”。

Strongly consider doing the PFX approach from EncryptedPrivateKeyInfo. 强烈考虑使用EncryptedPrivateKeyInfo的PFX方法。 Alternatively, you can do this in custom code. 或者,您可以在自定义代码中执行此操作。 Custom code? 自定义代码? Okay, let's do this. 好吧,让我们这样做。 The reference texts that you need at this point are 此时您需要的参考文本是

  1. ITU.T-REC X.680-201508 . ITU.T-REC X.680-201508
    • This defines the ASN.1 language, which tells you how to read the RSAPrivateKey (et al) object structure definition. 这定义了ASN.1语言,它告诉您如何读取RSAPrivateKey(等)对象结构定义。
    • For RSAPrivateKey this is mostly optional, since there aren't many nuances to SEQUENCE that it uses, and INTEGER is pretty straightforward. 对于RSAPrivateKey,这几乎是可选的,因为它使用的SEQUENCE的细微差别不大,并且INTEGER非常简单。
  2. ITU.T-REC X.690-201508 ITU.T-REC X.690-201508
    • This document describes the BER (and CER) and DER encoding rules for ASN.1. 本文档介绍了ASN.1的BER(和CER)和DER编码规则。
    • These key files are in DER. 这些密钥文件位于DER中。 (Unless they're in PEM, but we'll fix that soon) (除非它们处于PEM中,但是我们会尽快修复)
  3. The RFC appropriate to your object type. 适用于您的对象类型的RFC。

Okay, let's proceed. 好吧,让我们继续。

  1. If the file is PEM encoded ("-----BEGIN RSA PRIVATE KEY-----" or "-----BEGIN PRIVATE KEY-----", etc) you need to "un-PEM" it. 如果文件是PEM编码的(“ ----- BEGIN RSA PRIVATE KEY -----”或“ ----- BEGIN PRIVATE KEY -----”等),则需要“ un-PEM”它。
    • The PEM format is PEM格式为
      • (newline or beginning of file) (换行符或文件开头)
      • 5 hyphens, BEGIN, space, the type identifier, 5 hyphens, a newline 5个连字符,BEGIN,空格,类型标识符,5个连字符,换行符
      • a base64-encoded payload (with newlines after every 72 text characters) base64编码的有效负载(每72个文本字符后带有换行符)
      • a newline (unless you ended with a newline because you were a multiple of 72 text characters) 换行符(除非您以换行符结尾,因为您是72个文本字符的倍数)
      • 5 hyphens, END, the same type identifier as before, 5 hyphens 5个连字符,END,类型标识符与以前相同,5个连字符
    • The part we want is the payload. 我们想要的部分是有效载荷。 Run it through Convert.FromBase64String, and now we have the DER-encoded byte[] for the key object. 通过Convert.FromBase64String运行它,现在我们有了DER编码的byte[]作为键对象。
  2. Using the type definition and the ITU documents, write a parser for your key file format. 使用类型定义和ITU文档,为您的密钥文件格式编写一个解析器。
  3. Parse the key. 解析密钥。
  4. Convert the parsed key to an RSAParameters object (or DSAParameters, or ECParameters, as appropriate) 将已解析的密钥转换为RSAParameters对象(或DSAParameters或ECParameters,视情况而定)
  5. Call RSA.Create() (etc) 呼叫RSA.Create()(等)
  6. Load the key via the ImportParameters method. 通过ImportParameters方法加载密钥。
  7. Good to go. 好去。

For step 4, there are some things to be careful about. 对于步骤4,有些事情要小心。 Specifically, the ASN.1/DER INTEGER components have two rules that RSAParameters does not like. 具体来说,ASN.1 / DER INTEGER组件具有RSAParameters不喜欢的两个规则。

  • All leading 0x00 values are removed. 删除所有前导0x00值。
  • If the leading byte has the high bit set (>=0x80) but the number was supposed to be positive, insert a 0x00. 如果前导字节设置了高位(> = 0x80),但该数字应为正,则插入0x00。

.NET wants the values as big-endian byte arrays (which is the same byte order as the DER encoding) with the following relationship: .NET希望这些值成为具有以下关系的大端字节数组(与DER编码的字节顺序相同):

  • Exponent is as big as it needs to be, so long as it doesn't start with 0x00. 只要它不是以0x00开头,指数就等于需要的大小。
  • Modulus is as big as it needs to be, so long as it doesn't start with 0x00. 只要不以0x00开头,模量就等于需要的大小。
  • D must be the same size as modulus (insert 0x00 as necessary) D必须与模数大小相同(根据需要插入0x00)
  • P must be "half-round-up" the size of Modulus ((Modulus.Length + 1) / 2), insert 0x00 as necessary. P必须是“模数”((Modulus.Length + 1)/ 2)大小的“半舍入”,必要时插入0x00。
  • Q, DP, DQ, and InverseQ must have the same length as P. (Insert 0x00 as necessary). Q,DP,DQ和InverseQ必须具有与P相同的长度(根据需要插入0x00)。

.NET Core 3.0+ PKCS#1 RSAPrivateKey .NET Core 3.0+ PKCS#1 RSAPrivateKey

The ImportRSAPrivateKey method is declared on RSA , and since it parses data and calls ImportParameters it works for all RSA derived types (assuming they already supported parameter import). ImportRSAPrivateKey方法在RSA上声明,并且由于它解析数据并调用ImportParameters因此它适用于所有RSA派生类型(假设它们已经支持参数导入)。

using (RSA rsa = RSA.Create())
{
    rsa.ImportRSAPrivateKey(blob, out _);
    return rsa.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
}

Some other format 其他格式

Determine what RFC defines the ASN.1 structure for your key format, then keep that in mind and evaluate the RSAPrivateKey section. 确定哪种RFC为您的密钥格式定义了ASN.1结构,然后牢记这一点并评估RSAPrivateKey部分。

DSAParameters and ECParameters each have their own spatial expectations. DSAParameters和ECParameters每个都有自己的空间期望。

Further reading 进一步阅读

Some of these include not-always-elegant, but frequently functioning code: 其中一些包括并非总是优雅但经常运行的代码:

Microsoft provides a class SignedXML to sign files. Microsoft提供了一个SignedXML类来对文件进行签名。 To know more, checkout https://msdn.microsoft.com/en-us/library/system.security.cryptography.xml.signedxml(v=vs.110).aspx 要了解更多信息,请检出https://msdn.microsoft.com/zh-cn/library/system.security.cryptography.xml.signedxml(v=vs.110).aspx

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 使用BouncyCastle进行数字签名验证 - 带有SHA 256,C#的ECDSA - Digital Signature Verification using BouncyCastle - ECDSA with SHA 256, C# 如何在不使用SignedXml的情况下在C#中验证xml数字签名? - How to verify xml digital signature in c# without using SignedXml? C#中的数字签名 - Digital signature in C# 验证c#中的数字签名 - verifying digital signature in c# 使用C#为word文档添加数字签名 - Add digital signature to word document using C# 确定文件是否在c#中具有数字签名而不实际验证签名 - Determining if a file has a digital signature in c# without actually verifying the signature 在不验证签名的情况下确定 pdf 文件是否在 c# 中具有数字签名 - Determining if a pdf file has a digital signature in c# without verifying the signature 如何在 C# 中使用 Pkcs11Interop 创建 CAdES 格式的数字签名而无需数据或文档进行签名 - How to create Digital Signature with CAdES format using Pkcs11Interop in C# without data or document to sign 使用 BouncyCastle 使用 SHA-256withECDSA 的 C# 签名数据每次都会产生不同的签名 - C# Sign Data with SHA-256withECDSA using BouncyCastle produce different Signature everytime 如何使用BouncyCastle(C#)以ASN.1格式导入DSA签名 - How to import DSA signature in ASN.1 format using BouncyCastle (C#)
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM