简体   繁体   中英

Decrypt Email with Attachments using mimekit

We have a scenario to decrypt emails which has attachments. We are using mimekit library for the same. We also use mimekit for the email encryption and it works properly.

In our case the encrypted email is only having an attachment, no such email body . There is an Azure Logic App which gets the encrypted email from an Oiifce365 mailbox (using the built-in connector) and then it send the details to an Azure Function App which runs the decryption logic. The decryption certificate is stored in Azure Key Vault .

Below is the code we tried, and it shows exception saying

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

The certifcate is loading successfully. Also the email stream shows some data. However while running the decryption, it alsways shows the error. Any help will be helpful.

Below is your code with some fixes (namely getting rid of code like MethodAsync(...).Result which is bad practice).

I've also taken the liberty to add a big comment asking for more information.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM