使用MimeKit进行CMS签名和验证

问题描述

我正在使用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异常:'pkites7.Verify(原始行)上没有sqlite

 public byte[] Verify(byte[] data,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需要解析{{1} }验证签名时,我说的是file.binfile.bin因此产生的S / MIME输出中从base64 blob提取的内容完全相同。 >,它必须为MIME格式。