C# 使用MimeKit对cms进行签名和验证

C# 使用MimeKit对cms进行签名和验证,c#,bouncycastle,mimekit,C#,Bouncycastle,Mimekit,我正在使用Mimekit做rsa pss cms签名,来模拟这个openssl命令 openssl cms-sign-in keys.zip-binary-nodetach-signer selfsigned.crt-inkey keypair.pem-out keys.zip.signed-keyopt rsa_padding_模式:pss public byte[] Sign(byte[] data, byte[] signCert, byte[] privateKey, CmsKe

我正在使用Mimekit做rsa pss cms签名,来模拟这个openssl命令 openssl cms-sign-in keys.zip-binary-nodetach-signer selfsigned.crt-inkey keypair.pem-out keys.zip.signed-keyopt rsa_padding_模式:pss

    public byte[] Sign(byte[] data, byte[] signCert, byte[] privateKey, CmsKeyOpt cmsKeyOpt)
            {
                MimeMessage message = new MimeMessage
                {
                    Body = new MimePart()
                    {
                        Content = new MimeContent(new MemoryStream(data)),
                        ContentTransferEncoding = ContentEncoding.Binary,
                    },
                };
    
                // Load private key from byte array
                StreamReader stream = new StreamReader(new MemoryStream(privateKey), Encoding.Default);
                AsymmetricCipherKeyPair keyPair = (AsymmetricCipherKeyPair)new PemReader(stream).ReadObject();
    
                // load certifacte from byte array
                X509CertificateParser parser = new X509CertificateParser();
                X509Certificate certificate = parser.ReadCertificate(signCert);
    
                // Create RSA PSS CMS signer
                CmsSigner signer = new CmsSigner(certificate, keyPair.Private)
                {
                    RsaSignaturePadding = RsaSignaturePadding.Pss,
                    DigestAlgorithm = DigestAlgorithm.Sha256,
                };
    
                // Create BouncyCastle Secure MimeContext 
                BouncyCastleSecureMimeContext ctx = new TemporarySecureMimeContext();
                ctx.EncapsulatedSign(signer, new MemoryStream(data));
    
                // Get signed message body and override it
                message.Body = MultipartSigned.Create(ctx, signer, message.Body);
    
                byte[] singedData;
                using (var memory = new MemoryStream())
                {
                    message.WriteTo(memory);
                    singedData = memory.ToArray();
                }
                return singedData;
            } 
一切正常,我的问题是如何通过Mimekit/BouncyCastle实现验证,以模拟这个openssl命令 openssl cms-verify-in keys.zip.signed.dec-CAfile selfsigned.crt-out keys\u dec\u unsigned.zip

我尝试了这个方法,但得到了异常System.NotSupportedException:“SQLite在pkcs7上不可用。请验证(原始)行


是否有任何我可以遵循的指南来验证数据并将其返回到其原始论坛,或者举个例子?

默认情况下,MimeKit尝试基于其SQLite后端实例化s/MIME验证上下文以用于证书存储

如果没有安装SQLite和/或没有将证书存储在MimeKit将为您维护的SQLite数据库中,那么您需要为证书存储/检索注册不同的后端,或者实例化您自己的S/MIME上下文(就像您为签名所做的那样)

MimeKit具有两个选项:

  • 它将证书存储在内存中,并且是您用来在上面的代码段中对消息进行签名的
  • 它使用Windows OS X.509证书存储
一旦决定使用哪种证书,您将需要将证书导入上下文(上下文将证书导入相应的后端存储位置)

然后,在此之后,您可以执行以下操作:

using (BouncyCastleSecureMimeContext ctx = new TemporarySecureMimeContext()) {
    ctx.Import (...);

    foreach (var signature in pkcs7.Verify(ctx, out original)) {
        // ...
    }
}
更新:

这导致了openssl cms sign命令的误用,以及无法使用MimeKit验证签名,因为MimeKit希望封装的签名数据采用MIME格式,因为它应该符合S/MIME规范

交易如下:

openssl cms sign命令可用于对任意数据进行签名,相应的openssl cms verify命令可用于验证此类签名输出。但是,只有当openssl cms sign命令签名的原始内容是MIME格式时,它才是有效的S/MIME

MimeKit需要有效的S/MIME,因为它是。。。惊喜,惊喜。。。MIME库

您可能已经注意到,Verify()方法有一个输出参数(
out MimeEntity originalEntity
)。这意味着Verify()方法从签名数据中提取封装的内容,并将其解析为MIME实体,然后以所述输出参数的形式将其返回给调用方

如果封装的内容不是MIME格式,那么很明显,解析器将无法解析它

CMS签名工作原理的其他背景:

当您使用CMS签名例程(例如,
openssl CMS签名…
)对内容进行签名时,它会生成类似以下内容的内容:

MIME-Version: 1.0
Content-Disposition: attachment; filename="smime.p7m"
Content-Type: application/pkcs7-mime; smime-type=signed-data; name="smime.p7m"
Content-Transfer-Encoding: base64 

MIIQgAYJKoZIhvcNAQcCoIIQcTCCEG0CAQExDTALBglghkgBZQMEAgEwggkPBgkq...
上述MIME部分中的base64内容包括原始内容和CMS签名数据(即签名列表)

因此,MimeKit的Verify()方法需要在base64解码后将签名与原始内容分离。然后,MimeKit将返回给您、调用者、原始内容(它希望是MIME格式)和签名列表,然后您可以独立验证签名的真实性


当我不断地重复自己,当你签署内容时,
file.bin
文件的格式很重要,我并不是说openssl或MimeKit在验证签名时需要解析
file.bin
,我是说,
file.bin
的内容与从openssl cms sign生成的S/MIME输出中的base64 blob中提取的内容完全相同…,因此,它需要采用MIME格式。

是的,你是对的,我实现了它,但它给了我另一个异常,即System.FormatException:未能解析pkcs7.Verify(ctx,out original)中的实体头。知道我已经用上面的openssl命令生成了签名文件后,请以任何方式链接到我的代码。为什么会发生这种情况?因为签名数据不是应该的MIME?当我用问题中提到的openssl命令生成签名数据时,如何会发生这种情况,这是OpenSSLMIME版本签署文件的第一部分:1.0内容配置:附件;filename=“smime.p7m”内容类型:application/pkcs7 mime;smime类型=有符号数据;name=“smime.p7m”内容传输编码:base64 miiqgayjkozihvcnaqccoiqctcceg0caqexdtalbglghkgbzqmeagewggpbgkq。。。。
MIME-Version: 1.0
Content-Disposition: attachment; filename="smime.p7m"
Content-Type: application/pkcs7-mime; smime-type=signed-data; name="smime.p7m"
Content-Transfer-Encoding: base64 

MIIQgAYJKoZIhvcNAQcCoIIQcTCCEG0CAQExDTALBglghkgBZQMEAgEwggkPBgkq...