将 RSASSA-PSS 和 RSAES-OAEP 与 MailKit 结合使用

Using RSASSA-PSS and RSAES-OAEP with MailKit

我必须与一些业务合作伙伴交换加密和签名的电子邮件。需要具体的算法,比如:

我在使用 Mailkit 进行设置时遇到了问题,实际上在它的背后是 MailKit 和 BouncyCastle。 这是我到目前为止的位置:

用于解密&验签

解密正文没问题,在 windows 存储

中设置我的私钥后,我使用 WindowsSecureMimeContext 进行解密

验证签名不对

case MultipartSigned signedBody:
    try
    {
        using (var ctx = new WindowsSecureMimeContext(StoreLocation.LocalMachine))
        {
            var verifiedData = signedBody.Verify(ctx);
            return verifiedData.All(o => o.Verify());
        }
    }
    catch (Exception e)
    {
        throw new Exception("Error during signature verification.", e);
    }

发件人的证书由通用 CA 签名,因此我再次使用 WindowsSecureMimeContext,但是 verifiedData.All(o => o.Verify()) 抛出 DigitalSignatureVerifyException ("Failed to verify digital signature: Unknown error "-1073700864".")

签名加密

嗯,这看起来很难...

对于签名,我似乎在某处需要一个 BouncyCastle 的 PssSigner,我可以通过覆盖 DkimSigner 来获得它,尤其是 DigestSigner 属性

class TestSigner : DkimSigner
{
    protected TestSigner(string domain, string selector, DkimSignatureAlgorithm algorithm = DkimSignatureAlgorithm.RsaSha256) 
        : base(domain, selector, algorithm)
    {
    }

    public TestSigner(AsymmetricKeyParameter key, string domain, string selector, DkimSignatureAlgorithm algorithm = DkimSignatureAlgorithm.RsaSha256) 
        : base(key, domain, selector, algorithm)
    {
    }

    public TestSigner(string fileName, string domain, string selector, DkimSignatureAlgorithm algorithm = DkimSignatureAlgorithm.RsaSha256)
        : base(fileName, domain, selector, algorithm)
    {
    }

    public TestSigner(Stream stream, string domain, string selector, DkimSignatureAlgorithm algorithm = DkimSignatureAlgorithm.RsaSha256)
        : base(stream, domain, selector, algorithm)
    {
    }

    public override ISigner DigestSigner => SignerUtilities.GetSigner(PkcsObjectIdentifiers.IdRsassaPss);
}

但是我不知道在哪里使用它。也许在使用 MimeMessage.Sign() 时,但是我对方法签名中所需的参数有点迷失

对于加密,我可以找到 BouncyCastle 库中的 RsaesOaepParameters,但我不知道如何使用它。

非常感谢邮件专家的任何帮助!

A DkimSigner 用于生成 DKIM 签名,这不是您想做的。 DKIM 签名与 S/MIME.

没有任何关系

S/MIME 使用 RSASSA-PSS 签名

目前,WindowsSecureMimeContext(使用 System.Security 作为后端)不支持 RSASSA-PSS,因此您需要使用 Bouncy Castle 后端。

要使用 Bouncy Castle 后端,您将需要使用 BouncyCastleSecureMimeContext derivatives (or create your own). As a temporary solution for playing around with things, I might suggest using the TemporarySecureMimeContext, but for long-term use, I would suggest looking at the DefaultSecureMimeContext 之一 - 尽管您可能仍需要子class 以使其正常工作。

现在您正在使用 Bouncy Castle S/MIME 上下文,为了指定您想要使用 RSASSA-PSS 填充,您需要使用采用 CmsSigner parameter such as MultipartSigned.Create() or ApplicationPkcs7Mime.Sign() 的 API .

这是一个示例代码片段:

var signer = new CmsSigner ("certificate.pfx", "password");

// Specify that we want to use RSASSA-PSS
signer.RsaSignaturePaddingScheme = RsaSignaturePaddingScheme.Pss;

// Sign the message body
var signed = MultipartSigned.Create (ctx, signer, message.Body);

// replace the message body with the signed body
message.Body = signed;

S/MIME 使用 AES-128 CBC(或任何其他特定算法)和 RSAES-OAEP 进行加密

首先,要使用 S/MIME 进行加密,您需要使用 ApplicationPkcs7Mime.Encrypt() 方法之一。[2]

采用 MailboxAddress 的 Encrypt() 方法将自动创建 CmsRecipients and CmsRecipientCollection for you by doing certificate lookups based on the email address provided (or, if any of those mailboxes are actually a SecureMailboxAddress, the Fingerprint 代替,如果该用户在您的数据库中有超过 1 个证书或者您想成为特别确保 MimeKit 会选择正确的那个)。

当您向它提供 MailboxAddresses 列表时,MimeKit 会为您做的另一件事是,它会为该用户查找存储在数据库中的受支持的加密算法。

对于 WindowsSecureMimeContext,这涉及查看 S/MIME Capabilities X509 证书扩展属性和解码支持的加密算法。然而,根据我的经验,Windows 证书存储中的 X509 证书很多时候都没有这个扩展,因此 MimeKit 将不得不假设只支持 3DES CBC。

对于 DefaultSecureMimeContext,如果您已验证任何由所述收件人签署的 S/MIME 消息,则该用户的证书(链)和宣传的加密算法将存储在 MimeKit 的自定义 SQL 数据库(当您使用 S/MIME 签署消息时,客户端在 S/MIME 签名数据中包含 S/MIME Capabilities 属性是相当普遍的做法)。

现在您了解了它的工作原理,如果您想强制 使用 AES-128 CBC,方法是手动构建 CmsRecipientCollection 你自己。

当然,这涉及为每个收件人创建一个新的 CmsRecipient。要创建此 class,您真正需要的只是该收件人的 X509 证书。

var recipient = new CmsRecipient (certificate);

由于您想强制使用 AES-128 CBC,现在您只需覆盖此收件人支持的加密算法即可:

recipient.EncryptionAlgorithms = new EncryptionAlgorithm[] {
    EncryptionAlgorithm.Aes128
};

(默认情况下,EncryptionAlgorithms 属性 将设置为证书的 S/MIME Capabilities Extension 属性(按优先顺序)中列出的算法,如果存在,否则它'将仅包含 3DES CBC)。

如果您还想强制执行 RSAES-OAEP,则需要设置:

recipient.RsaEncryptionPadding = RsaEncryptionPadding.OaepSha1;

将每个 CmsRecipient 添加到您的 CmsRecipientCollection,然后将其传递给您首选的 Encrypt() 方法,哇哦,它将使用 AES-128 CBC 进行加密。

备注:

  1. MultipartSigned.Create() 将生成 multipart/signed MIME 部分,而 ApplicationPkcs7Mime.Sign() 将创建 application/pkcs7-mime MIME 部分。无论您想使用哪种形式都由您决定,请记住,您的选择可能会影响与您的收件人使用的任何客户端的兼容性(我认为大多数客户端都支持这两种形式,但您可能需要检查以确保)。
  2. 如果您已经使用 MimeKit 注册了您的自定义 SecureMimeContext class(如 README 中的简要描述),那么您可以随意使用各种 Encrypt/Decrypt/Sign/Verify/etc 方法不要采用加密上下文参数,因为 MimeKit 将为您实例化默认上下文。否则,您将需要向他们传递上下文。