【问题标题】:Sign an array of bytes with X509 Certificate in .Net Core在 .Net Core 中使用 X509 证书对字节数组进行签名
【发布时间】:2017-04-23 06:45:02
【问题描述】:

我想用X509Certificate2 对一个字节数组进行签名,

这是.Net Framework 4.7 的示例,但对于 .Net Core,我需要相同的示例:

 var argCertFirmante = new X509Certificate2(file, pass);
 var infoContenido = new ContentInfo(argBytesMsg);
 var cmsFirmado = new SignedCms(infoContenido);
 var cmsFirmante = new CmsSigner(argCertFirmante)
  { IncludeOption = X509IncludeOption.EndCertOnly };
 cmsFirmado.ComputeSignature(cmsFirmante, true);
 return cmsFirmado.Encode();

我想要与此等价的:

  • openssl smime -sign -signer ebookingv4.crt -inkey ebookingv4.key -out ticket.xml.cms -in ticket.xml -outform DER -nodetach
  • openssl base64 -in ticket.xml.cms -out ticket.xml.cms.base64

Cryptographic Message Syntax (CMS) 使用 Net Core 2.0 和这个 nuget 包,我工作得很好:

<PackageReference Include="System.Security.Cryptography.Algorithms"
Version="4.4.0-beta-24913-01" /> <PackageReference
Include="System.Security.Cryptography.Pkcs"
Version="4.5.0-preview1-26119-06" /> <PackageReference
Include="System.Security.Cryptography.X509Certificates"
Version="4.4.0-beta-24913-01" />

【问题讨论】:

  • 您想将它与 .net-core 一起用于跨平台还是仅用于 Windows?
  • 在 .net-core 中支持跨平台

标签: c# cryptography .net-core sign pkcs#12


【解决方案1】:

SignedCms 在 .NET Core 1.0 或 1.1 中不可用;也不会出现在 2.0 中。 (编辑:它将在即将发布的 2.1 版本中提供)。

如果您只关心写入数据(这比读取数据容易得多),您可以仅使用 RSA.SignData 来实现它的有限形式。

SignedCms 生成一个 DER 编码的 CMS 签名数据值 (RFC 5652, section 5),即

SignedData ::= SEQUENCE {
    version CMSVersion,
    digestAlgorithms DigestAlgorithmIdentifiers,
    encapContentInfo EncapsulatedContentInfo,
    certificates [0] IMPLICIT CertificateSet OPTIONAL,
    crls [1] IMPLICIT RevocationInfoChoices OPTIONAL,
    signerInfos SignerInfos
}

DigestAlgorithmIdentifiers ::= SET OF DigestAlgorithmIdentifier
SignerInfos ::= SET OF SignerInfo

要编写一个,您需要了解如何根据可分辨编码规则 (DER) 编写数据,即 ITU-T X.690(尽管它在 ASN.1 上构建了很多内容,并引用了 ASN.1。 1,即ITU-T X.680)。

假设您想使用 SHA-2-256 / RSA+SHA-2-256 签署“Hello”。当然,我们在密码学中没有字符串,所以这是字节序列48 65 6C 6C 6F

// SEQUENCE (SignedData)
30 xa [ya [za]]
   // INTEGER (Version=1)
   02 01 01
   // SET (OF DigestAlgorithmIdentifier (digestAlgorithms))
   31 xb [yb [zb]]
      // SEQUENCE (DigestAlgorithmIdentifier ::= AlgorithmIdentifier)
      30 xc [yc [zc]]
         // OBJECT IDENTIFIER (2.16.840.1.101.3.4.2.1 == SHA-2-256)
         06 09 60 86 48 01 65 03 04 02 01
   // SEQUENCE (EncapsulatedContentInfo)
   30 xd [yd [zd]]
      // OBJECT IDENTIFIER (1.2.840.113549.1.7.1 == pkcs7-data)
      06 09 2A 86 48 86 F7 0D 01 07 01
      // CONTEXT SPECIFIC 0 - CONSTRUCTED
      A0 xe [ye [ze]]
         // OCTET STRING (the data goes here)
         04 05 48 65 6C 6C 6F // "Hello"
   // CONTEXT SPECIFIC 0 - CONSTRUCTED (CertificateSet (certificates))
   A0 xf [yf [zf]]
      [cert.RawData goes here, which is already DER encoded]
      [do you have an intermediate you want to share?
        okay, write intermediate.RawData here; repeat]
   // skip the crls.
   // SET (OF SignerInfo (singerInfos))
   31 xg [yg [zg]]
      // SEQUENCE (SignerInfo)
      30 xh [yh [zh]]
         // INTEGER (Version=1)
         02 01 01
         // SEQUENCE (IssuerAndSerialNumber)
         30 xi [yi [zi]]
            // SEQUENCE (Issuer)
            [cert.IssuerName.RawData]
            // OCTECT STRING (SerialNumber)
            02 xj [yj [zj]]
               [cert.GetSerialNumberBytes() (see note "j")]
         // SEQUENCE (DigestAlgorithm (digestAlgorithm))
         30 xk [yk [zk]]
            // OBJECT IDENTIFIER (2.16.840.1.101.3.4.2.1 == SHA-2-256)
            06 09 60 86 48 01 65 03 04 02 01
         // skip signedAttrs
         // SEQUENCE (DigestEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier)
         30 xl [yl [zl]]
            // OBJECT IDENTIFIER (1.2.840.113549.1.1.1 == rsaEncryption)
            06 09 2A 86 48 86 F7 0D 01 01 01 
            // NULL (rsaEncryption says parameters must be explicit NULL)
            05 00
         // OCTECT STRING (signature)
         04 xm [ym [zm]]
            [rsa.SignData(
                 new byte[] { 0x48, 0x65, 0x6C, 0x6C, 0x6F },
                 HashAlgorithmName.SHA256,
                 RSASignaturePadding.Pkcs1)]
         // skip unsignedAttrs

现在我们完成了,我们可以关闭所有缺失的长度。签名大小是 RSA 密钥的函数。假设它是一个 2048 位的密钥,它生成一个 2048 位的签名,即 256 个字节。 256 是 0x100,比 0x7F 大,所以我们要把它分成两个长度字节和一个长度长度字节:所以“m”系列字节是80 01 00

“l”系列是完整的,它包含 13 个字节,所以 0D(没有 y 或 z 字节)。

“k”在 11 个字节处完成 (0B)。

“j”取决于序列号的长度。我的证书有序列号9B 5D E6 C1 51 26 A5 8B,但你不应该把它写成负数(第一个字节设置了高位),所以它需要一个填充字节,使内容00 9B 5D E6 C1 51 26 A5 8B,因此长度 9 (09)。

“i”取决于发行者名称的长度。我的是一个 141 字节的数组(已经 DER 编码),加上我们的序列号(9 字节 + 标签 + 长度 == 11 字节)=> 152 字节(0x98)。由于 0x98 大于 0x7F,我们必须为其添加长度前缀:81 98

现在“h”完成了。 (3 + (1 + 2 + 152) + (1 + 1 + 11) + (1 + 1 + 13) + (1 + 3 + 256) => 446 = 0x1BE => 82 01 BE.

“g”是 (1 + 3 + 446) => 450 = 0x1C2 82 01 C2

"f" 是您编码的所有证书的总和。我的结果是 683 = 0x2AB (82 02 AB)

“e”是 7 (07)

“d”是 11 + (1 + 1 + 7) = 20 = 0x14 (14)

“c”是 11 (0B)

“b”是 (1 + 1 + 11) = 13 (0D)

“a”是 3 + (1 + 1 + 13) + (1 + 1 + 11) + (1 + 3 + 683) + (1 + 3 + 450) = 1172 = 0x494 (82 04 94)。

30 82 04 94  02 01 01 31    0D 30 0B 06  09 60 86 48
01 65 03 04  02 01 30 14    06 09 2A 86  48 86 F7 0D
01 07 01 A0  07 04 05 65    6C 6C 6F A0  82 02 AB ...
...cert.RawData...
31 82 01 C2  30 82 01 BE    02 01 01 30  81 98 ...
...cert.IssuerName.RawData...
02 09 00 9B  5D E6 C1 51    26 A5 8B 30  0B 06 09 60
86 48 01 65  03 04 02 01    30 0D 06 09  2A 86 48 86
F7 0D 01 01  01 05 00 04    80 01 00 ... signature ...

如果您走这条路,您将得到 openssl asn1parse -i -dump -inform DER &lt; your.signed.cmsASN.1 Editor 等工具或其他此类 DER 读取器/渲染工具的帮助。

【讨论】:

    【解决方案2】:

    根据Apisof.NetSignedCmsCmsSigner 两个类都不会移植到 .Net Core。您可以在这里做两件事:

    • 等待 .Net Core 2.0 版本,这样您就可以使用PrivateKey property,并使用AsymmetricAlgorithm 对象对数据进行签名
    • 或者你可以使用extension methodGetRSAPrivateKey(X509Certificate2):

      public byte[] Sign(string message)
      {
          using (var key = certificate.GetRSAPrivateKey())
          {
              return key.SignData(Encoding.UTF8.GetBytes(message),
                HashAlgorithmName.SHA256,
                RSASignaturePadding.Pkcs1);
          }
      }
      

    HashAlgorithmName 具有用于算法的静态名称,RSASignaturePadding 具有用于填充的默认对象。

    【讨论】:

      猜你喜欢
      • 2014-01-06
      • 1970-01-01
      • 2013-06-28
      • 2015-06-29
      • 2018-07-19
      • 2016-02-26
      • 2015-03-03
      • 2016-02-27
      • 2018-04-19
      相关资源
      最近更新 更多