繁体   English   中英

使用 MimeKit 进行 cms 签名和验证

[英]Using MimeKit to cms sign and verify

我正在使用 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_mode: 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_dec_unsigned.zip

我试过了,但我得到了异常 System.NotSupportedException: 'SQLite is not available on pkcs7.Verify(out original) line

 public byte[] Verify(byte[] data, byte[] signCert, byte[] privateKey)
        {
            bool valid = false;
            // 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);

            MimeMessage message = MimeMessage.Load(new MemoryStream(data));
            // Create BouncyCastle Secure MimeContext 
            MimeEntity original;
            ApplicationPkcs7Mime pkcs7 = message.Body as ApplicationPkcs7Mime;
            if (pkcs7 != null && pkcs7.SecureMimeType == SecureMimeType.SignedData)
            {
                foreach (var signature in pkcs7.Verify(out original))
                {
                    try
                    {
                        valid = signature.Verify();
                    }
                    catch (DigitalSignatureVerifyException)
                    {
                        // There was an error verifying the signature.
                    }
                }
            }


            byte[] res = { 0 };
            return res;
        }

我可以遵循任何指南来验证并将数据返回到其原始论坛或示例吗?

默认情况下,MimeKit 会尝试根据其 SQLite 后端来实例化 S/MIME 验证上下文以进行证书存储。

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

MimeKit 有 2 个选项:

一旦您决定使用哪些证书,您将需要将您的证书导入上下文(这会将它们导入适当的后端存储位置)。

然后,在那之后,你可以这样做:

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 sign ... )对内容进行签名时,它会生成如下所示的内容:

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 中提取的内容完全相同openssl cms sign ...因此,它需要采用 MIME 格式。

暂无
暂无

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

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM