在.NET Core中签署带有X509证书的字节数组



我想用 X509Certificate2

签署字节数组

这是.NET Framework 4.7的示例,但是我需要.NET核心:

 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.xml.cms -in ticket.xml -outform der -nodetach
  • openssl base64 -in ticket.xml.cms -out ticket.xml.cms.base64

加密消息语法(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 1.0或1.1中不可用签名。它也不会在2.0中。( edit :它将在即将发布的2.1版本中使用(。

如果您只关心编写数据(这比阅读要容易得多(,则可以使用RSA.Signdata实现有限的形式。

SignedCMS产生一个编码的CMS签名数据值(RFC 5652,第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,即ITU-T X.680(。

想象您想签名"您好"使用SHA-2-256/RSA SHA-2-256。当然,我们没有密码学中的字符串,所以这是字节序列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&quot"系列字节是80 01 00

" l&quot"系列完成,包含13个字节,因此0D(没有Y或Z字节(。

" k&quot"完成为11个字节(0B(。

" J&quot"取决于序列号的时间。我的证书具有序列号9B 5D E6 C1 51 26 A5 8B,但是您不应该将其写入负数(首先字节具有高钻头(,因此它需要一个填充字节,使内容00 9B 5D E6 C1 51 26 A5 8B,因此长度为9(09((。

" i i&quot"取决于发行人名称的长度。我的序列阵列(已经编码了(,加上我们的序列号(9个字节 tag 长度== 11个字节(=&gt;152字节(0x98(。由于0x98大于0x7f,因此我们必须长度回顾: 81 98

现在" h"已经完成了。(3 (1 2 152( (1 1 11( (1 1 13( (1 3 256(=&gt; 446 = 0x1be =&gt; 82 01 BE

" g&quot"IS(1 3 446(=&gt;450 = 0x1C2 82 01 C2

" f&quot"是您编码的所有证书的总和。我的出现到683 = 0x2ab(82 02 AB(

'e&quot是7(07(

'd&quot是11 (1 1 7(= 20 = 0x14(14(

" c&quot"是11(0B(

" b&quot"IS(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 < your.signed.cms或ASN.1编辑器或其他此类der读取器/渲染工具等工具的协助。

根据Apisof.NetSignedCmsCmsSigner的两个类都不会移植到.NET Core。您可以做的是两件事:

  • 等待2.0版的.NET Core,因此您可以使用PrivateKey属性,并使用AsymmetricAlgorithm对象签署数据
  • ,或者您可以使用extension方法GetRSAPrivateKey(X509Certificate2)

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

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

最新更新