【问题标题】:Using RSASSA-PSS and RSAES-OAEP with MailKit将 RSASSA-PSS 和 RSAES-OAEP 与 MailKit 一起使用
【发布时间】:2017-12-22 19:01:30
【问题描述】:

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

  • 用于签名,RSASSA-PSS作为签名算法,
  • 用于加密,RSAES-OAEP 用于密钥加密,AES-128 CBC 用于内容加密

我在使用 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,但 verifyData.All(o => o.Verify()) 抛出 DigitalSignatureVerifyException(“无法验证数字签名:未知错误"-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,但我不知道如何使用它。

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

【问题讨论】:

  • 您的验证异常看起来可能来自 System.Security(您使用的是 WindowsSecureMimeContext 吗?)。如果是,请尝试升级到我昨天刚刚发布到 nuget.org 的 MimeKit/MailKit 2.0。我对 2.0 版的 WindowsSecureMimeContext 做了很多改进。我不确定该异常是由 MimeKit 中的错误引起的,还是只是 System.Security 层中的错误)。
  • 不,这没有帮助。调试显示这是由 System.Security 触发的,同时检查 SignerInfo 的签名here。我改用了 BouncyCastleSecureMimeContext,它成功验证了签名
  • FWIW,我刚刚在 MimeKit 中实现了 RSAES-OAEP(一旦发布,将包含在 v2.5.0 中)。还支持 RSASSA-PSS(从 MimeKit v2.3.1 开始)。

标签: c# cryptography mimekit


【解决方案1】:

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

使用 RSASSA-PSS 进行 S/MIME 签名

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

要使用 Bouncy Castle 后端,您需要使用 BouncyCastleSecureMimeContext 衍生产品之一(或创建您自己的)。作为玩弄事物的临时解决方案,我可能会建议使用TemporarySecureMimeContext,但对于长期使用,我建议查看DefaultSecureMimeContext - 尽管您可能仍然希望对其进行子类化以使其正常工作。

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

这是一个示例代码sn-p:

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() 方法将通过根据提供的电子邮件地址(或者,如果这些邮箱中的任何一个实际上是 @ 987654331@,使用 Fingerprint 代替,如果该用户在您的数据库中拥有超过 1 个证书,或者您想更加确定 MimeKit 选择正确的证书,这将非常有用)。

当您向 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。要创建此类,您真正需要的是该收件人的 X509 证书。

var recipient = new CmsRecipient (certificate);

既然你想强制使用 AES-128 CBC,现在你只需要覆盖这个接收者支持的加密算法:

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

(默认情况下,EncryptionAlgorithms 属性将设置为证书的 S/MIME 功能扩展属性中列出的算法(按优先顺序),如果存在,否则它将仅包含 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 类(如 README 中的简要描述),那么您可以随意使用各种不采用加密的 Encrypt/Decrypt/Sign/Verify/etc 方法context 参数作为 MimeKit 将为您实例化默认上下文。否则,您将需要向他们传递上下文。

【讨论】:

  • 嗨 jstedfast,感谢您的出色回答。如果我理解正确,我需要使用 RSA-PSS 私钥(我想 OpenSSL 可以)。对于加密,我确实需要强制使用 AES-128,谢谢你的技巧。但是 RSAES-OAEP 在哪里设置?
  • 没有办法在 MimeKit 中指定密钥加密算法,我不知道 System.Security 或 BouncyCastle 的默认算法是什么。我也不知道如何将它们指定给这些库中的任何一个。我很乐意接受补丁。
  • 我会看看的,虽然不会在接下来的几天里
  • 我已经设法弄清楚如何使用 BouncyCastle 进行 RSAES-OAEP,因此我向 CmsRecipient 添加了一个新属性以允许设置该填充方案,但我还没有制作使用此功能发布。当它发布时,它会有一个 MimeKit 2.5.0 版本。
猜你喜欢
  • 1970-01-01
  • 2021-01-18
  • 1970-01-01
  • 1970-01-01
  • 2014-08-07
  • 2021-03-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多