使用 mimekit 解密带附件的电子邮件

Decrypt Email with Attachments using mimekit

我们有一个方案可以解密带有附件的电子邮件。我们正在使用 mimekit 库。我们还使用 mimekit 进行电子邮件加密,它工作正常。

在我们的例子中,加密的电子邮件只有一个附件,没有这样的电子邮件正文。 Azure Logic App 从 Oiifce365 邮箱(使用内置连接器)获取加密电子邮件,然后将详细信息发送到运行解密逻辑的 Azure Function App。解密证书存放在Azure Key Vault.

下面是我们试过的代码,它显示异常说

Unable to cast object of type 'Org.BouncyCastle.Asn1.DerApplicationSpecific' to type 'Org.BouncyCastle.Asn1.Asn1SequenceParser'.

[FunctionName("DecryptSMIME")]
public static async Task<IActionResult> Decrypt([HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req, ILogger log)
        {
            try
            {
                var temporarySecurityMimeContext = new TemporarySecureMimeContext();

                // get decryption Cert pfx
                var keyVaultClient = ServiceProvider.GetRequiredService<IKeyVaultClient>();
                var decryptionCertBundle = keyVaultClient.GetSecretAsync("https://my-key-vault.vault.azure.net/secrets/Decryption-Certificate-Base64/d7a84b415a494c1ebaseae88cff50028").Result;
                var decryptionCertBytes = Convert.FromBase64String(decryptionCertBundle.Value);
                log.LogInformation($"Decoded length of decryption certificate: '{decryptionCertBytes.Length}'");

                // get decryption Cert password
                var decryptionCertPasswordBundle = keyVaultClient.GetSecretAsync("https://my-key-vault.vault.azure.net/secrets/Decryption-Certificate-Pass/34judc9f575f467a96d9483dfc8kf467").Result;
                var decryptionCertPassword = decryptionCertPasswordBundle.Value;

                using var stream = new MemoryStream(decryptionCertBytes);
                temporarySecurityMimeContext.Import(stream, decryptionCertPassword);
                log.LogInformation("Imported The Decryption certificate as MemoryStream");

                using var encryptedContentStream = await GetMailAttachmentStreamAsync(req.Body, log) ;
                log.LogInformation("Loading pkcs7-mime entity.");
                ApplicationPkcs7Mime encryptedContent = (ApplicationPkcs7Mime)await MimeEntity.LoadAsync(ParserOptions.Default, ContentType.Parse(ParserOptions.Default, "application/pkcs7-mime; smime-type=enveloped-data; name=smime.p7m"), encryptedContentStream);

                log.LogInformation("Decrypting pkcs7-mime entity.");
                MimeEntity decryptedContent = encryptedContent.Decrypt();
                
                return new OkObjectResult("OK");
            }
            catch (Exception ex)
            {
                log.LogError(ex, "Failed to decrypt the secure mime part in the request body.");

                throw;
            }
        }

     private static async Task<MemoryStream> GetMailAttachmentStreamAsync(Stream attachmentObjectStream, ILogger log)
        {
            var memoryStream = new MemoryStream();
            await attachmentObjectStream.CopyToAsync(memoryStream);
            memoryStream.Seek(0, SeekOrigin.Begin);
            log.LogInformation($"Attachment Stream Processed. {memoryStream.Length} Bytes");
            return memoryStream;
        }

证书加载成功。电子邮件流也显示一些数据。但是运行解密时,总是报错。任何帮助都会有所帮助。

下面是您的代码并进行了一些修复(即删除像 MethodAsync(...).Result 这样的错误做法的代码)。

我还冒昧地添加了一条大评论,要求提供更多信息。

[FunctionName("DecryptSMIME")]
public static async Task<IActionResult> Decrypt([HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req, ILogger log)
{
    try
    {
        using var temporarySecurityMimeContext = new TemporarySecureMimeContext();

        // get decryption Cert pfx
        var keyVaultClient = ServiceProvider.GetRequiredService<IKeyVaultClient>();
        var decryptionCertBundle = await keyVaultClient.GetSecretAsync("https://my-key-vault.vault.azure.net/secrets/Decryption-Certificate-Base64/d7a84b415a494c1ebaseae88cff50028");
        var decryptionCertBytes = Convert.FromBase64String(decryptionCertBundle.Value);
        log.LogInformation($"Decoded length of decryption certificate: '{decryptionCertBytes.Length}'");

        // get decryption Cert password
        var decryptionCertPasswordBundle = await keyVaultClient.GetSecretAsync("https://my-key-vault.vault.azure.net/secrets/Decryption-Certificate-Pass/34judc9f575f467a96d9483dfc8kf467");
        var decryptionCertPassword = decryptionCertPasswordBundle.Value;

        using var stream = new MemoryStream(decryptionCertBytes);
        temporarySecurityMimeContext.Import(stream, decryptionCertPassword);
        log.LogInformation("Imported The Decryption certificate as MemoryStream");

        using var encryptedContentStream = await GetMailAttachmentStreamAsync(req.Body, log) ;
        log.LogInformation("Loading pkcs7-mime entity.");

        // Ideally, you would not use the MimeEntity.LoadAsync() method that takes a
        // forged ContentType parameter. This is a huge hack and *may* be the cause
        // of your problem. In other words, the content that MimeKit is trying to
        // decrypt may be in the wrong format. To know for certain, I would need to
        // know what the HttpRequest headers and Body look like.
        //
        // I would probably recommend that your code that sends this request be
        // modified to send the entire raw MIME (i.e. including headers) of the
        // application/pkcs7-mime part as the HTTP request body instead so that you
        // would not need to forge the Content-Type header.
        ApplicationPkcs7Mime encryptedContent = (ApplicationPkcs7Mime)await MimeEntity.LoadAsync(ParserOptions.Default, ContentType.Parse(ParserOptions.Default, "application/pkcs7-mime; smime-type=enveloped-data; name=smime.p7m"), encryptedContentStream);

        log.LogInformation("Decrypting pkcs7-mime entity.");
        MimeEntity decryptedContent = encryptedContent.Decrypt();
            
        return new OkObjectResult("OK");
    }
    catch (Exception ex)
    {
        log.LogError(ex, "Failed to decrypt the secure mime part in the request body.");

        throw;
    }
}

private static async Task<MemoryStream> GetMailAttachmentStreamAsync(Stream attachmentObjectStream, ILogger log)
{
    var memoryStream = new MemoryStream();
    await attachmentObjectStream.CopyToAsync(memoryStream);
    memoryStream.Seek(0, SeekOrigin.Begin);
    log.LogInformation($"Attachment Stream Processed. {memoryStream.Length} Bytes");
    return memoryStream;
}