【问题标题】:Using MimeKit to cms sign and verify使用 MimeKit 进行 cms 签名和验证
【发布时间】:2020-10-11 15:16:26
【问题描述】:

我正在使用 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 在 pkcs7.Verify(out original) 行上不可用

 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;
        }

我可以遵循任何指南来验证数据并将数据返回到它的原始论坛,还是一个示例?

【问题讨论】:

    标签: c# bouncycastle mimekit


    【解决方案1】:

    默认情况下,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 中提取的内容完全相同,因此,它需要在 MIME 中格式。

    【讨论】:

    • 是的,你是对的,我实现了它,但它给了我另一个异常说 System.FormatException:无法解析 pkcs7.Verify 中的实体标头(ctx,out original)。知道我已经使用上面的 openssl 命令生成了签名文件,链接到我的代码 pastebin.com/WKBUVuKB
    • 知道为什么会这样吗?
    • 因为签名数据不是应有的 MIME?
    • 当我使用问题中提到的 openssl 命令生成签名数据时,这是如何发生的,这是 open ssl 签名文件的第一部分
    • MIME 版本:1.0 内容处置:附件; filename="smime.p7m" 内容类型:application/pkcs7-mime; smime 类型=签名数据; name="smime.p7m" 内容传输编码:base64 MIIQgAYJKoZIhvcNAQcCoIIQcTCCEG0CAQExDTALBglghkgBZQMEAgEwggkPBgkq....
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-09-22
    • 2014-03-22
    • 1970-01-01
    • 1970-01-01
    • 2021-02-05
    • 1970-01-01
    • 2021-10-07
    相关资源
    最近更新 更多