C# 在.Net Core中使用X509证书对字节数组进行签名

C# 在.Net Core中使用X509证书对字节数组进行签名,c#,cryptography,.net-core,sign,pkcs#12,C#,Cryptography,.net Core,Sign,Pkcs#12,我想用X509Certificate2为字节数组签名 以下是的示例,但我需要与.Net Core相同的示例: var argCertFirmante = new X509Certificate2(file, pass); var infoContenido = new ContentInfo(argBytesMsg); var cmsFirmado = new SignedCms(infoContenido); var cmsFirmante = new CmsSigner(argCer

我想用
X509Certificate2
为字节数组签名

以下是的示例,但我需要与.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
有了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" />

根据Apisof.Net的
Apisof.Net
,这两个类都不会被移植到.Net内核。在这里,您可以做两件事:

  • 等待.Net Core的2.0版本,以便您能够使用,并使用
    非对称gorithm
    对象对数据进行签名
  • 或者您可以使用:


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

根据Apisof.Net,这两个类都是类,不会被移植到.Net核心。在这里,您可以做两件事:

  • 等待.Net Core的2.0版本,以便您能够使用,并使用
    非对称gorithm
    对象对数据进行签名
  • 或者您可以使用:


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

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

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

SignedCms生成DER编码的CMS签名数据值(),该值为

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)编写数据,即(尽管它构建了许多ASN.1,并引用了ASN.1,即)

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

// 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(
820494


如果你走这条路,你会得到像
openssl asn1parse-i-dump-inform DER
或其他类似的DER阅读器/渲染工具的帮助。

SignedCms在.NET Core 1.0或1.1中不可用;在2.0中也不可用。(Edit:它将在即将发布的2.1版本中可用)

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

SignedCms生成DER编码的CMS签名数据值(),该值为

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)编写数据,即(尽管它构建了许多ASN.1,并引用了ASN.1,即)

假设您想用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)+