问题描述
我有一个PowerShell脚本,可以将电子邮件附件保存到本地磁盘。要获取电子邮件,请使用EWS托管API(下载here)。 从邮箱加载包含附件的电子邮件,然后将附件存储到本地磁盘。
如果是未签名的电子邮件,则脚本可以正常工作,并且附件已正确保存。但是,如果它是S / MIME签名的邮件,则会出现以下错误:
Exception calling "Decode" with "1" argument(s): "ASN1 bad tag value met.
"
At C:\Get-Attachments.ps1:47 char:2
+ $envelopObject.Decode($data);
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [],MethodInvocationException
+ FullyQualifiedErrorId : CryptographicException
我的实现基于this blog post的代码。
我正在将附件加载到流中,并从那里使用.GetBuffer()
获取内容。然后,我使用EnvelopedCms
对象对内容进行解码(这就是我得到错误的地方)。
在下面,您可以看到我的PowerShell脚本正在尝试对已签名的电子邮件进行解码。如果$envelopObject.Decode($data);
函数中的Decode-ToFile
被调用,我将收到上面显示的错误。
function Decode-ToFile([byte[]] $data)
{
[byte[]] $resultObject = $null;
[System.Security.Cryptography.Pkcs.EnvelopedCms]$envelopObject = [System.Security.Cryptography.Pkcs.EnvelopedCms]::new();
$envelopObject.Decode($data);
$resultObject = $envelopObject.ContentInfo.Content
return $resultObject
}
function Save-Attachment
(
[Microsoft.Exchange.WebServices.Data.FileAttachment] $attachment
)
{
$attachmentName = $attachment.Name;
if($attachmentName -eq "smime.p7m")
{
[System.IO.MemoryStream]$stream = [System.IO.MemoryStream]::new();
$attachment.Load($stream);
[System.IO.StreamReader]$streamReader = [System.IO.StreamReader]::new($stream);
$stream.Seek(0,[System.IO.SeekOrigin]::Begin);
[byte[]] $data = $stream.GetBuffer();
$decodedData = Decode-ToFile($data);
}
# Saving Attachment
…
…
}
我正在尝试获取S / MIME签名电子邮件的附件以保存它们。
如果您需要更多信息,请打给我,我会尽力为您提供所需的信息。
解决方法
我有同样的问题,找到了 MimeKit dotnet 框架 (http://www.mimekit.net/docs/html/Introduction.htm) 并让它像这样工作(因为我说的是荷兰语,注释/变量名/日志语句是荷兰语,但我'我相信你仍然可以弄清楚发生了什么。如果没有,请联系):
- 获取框架:
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
Install-PackageProvider -Name NuGet
Register-PackageSource -name NuGet -Location https://www.nuget.org/api/v2 -ProviderName NuGet -Trusted
Install-Package -SkipDependencies -Name MimeKit -ProviderName NuGet
Install-Package -Name System.Buffers -ProviderName NuGet
- 在您的 ps 代码中使用 dll:
$MimeKitDllPath = "C:\Program Files\PackageManagement\NuGet\Packages\MimeKit.2.11.0\lib\net46\MimeKit.dll"
$SystemBuffersDllPath = "C:\Program Files\PackageManagement\NuGet\Packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll"
Function LoadDll ($dll) {
[void][System.Reflection.Assembly]::LoadFile($dll)
}
LoadDll($MimeKitDllPath)
LoadDll($SystemBuffersDllPath)
- 使用框架解码 mime 消息:签名邮件作为一个大附件从 EWS 发送。但是在附件里面,有组成邮件正文+所有附件的部分,所以它有点像一个zip文件,需要解压里面的单独文件。 我通过一个单独的函数来做到这一点,该函数将一个附件保存到磁盘,但能够检测它是否是这个特殊的附件,然后解码/解压缩 smime.p7m 文件中的所有附件。这是执行此操作的函数:
Function AttachmentNaarDisk($attach) {
$filebasename = [System.IO.Path]::GetFileNameWithoutExtension($attach.Name.ToString())
$fileextension = [System.IO.Path]::GetExtension($attach.Name.ToString())
$LocalAttachmentFile = "$($TempDownloadDirectory)\$($filebasename)-$(Get-Date -Format 'yyyyMMddTHHmmss')$($fileextension)"
$LocalAttachmentFile = $LocalAttachmentFile.Replace('[','').Replace(']','') # De [] wildcard tekens moeten eruit...vervangen door backtick prefix helpt wel voor invoke-webrequest MAAR de RestAPI kan er ook niet mee overweg,dus weg ermee
Log " Bijlage downloaden en wegschrijven naar $($LocalAttachmentFile)"
$fiFile = new-object System.IO.FileStream($LocalAttachmentFile,[System.IO.FileMode]::Create)
$fiFile.Write($attach.Content,$attach.Content.Length)
$fiFile.Close()
# Check of het een signed message is
if ($attach.ContentType -eq $MailboxBijlageSigned) {
Log " Bijlage is signed message - decoderen"
$Bijlages = @()
## Hier dan de verwerking proberen te doen via MimeKit
$message = [MimeKit.MimeMessage]::Load($LocalAttachmentFile)
# Iterate over de onderdelen,haal de bijlages eruit
$iter = [MimeKit.MimeIterator]::new($message)
while ($iter.MoveNext()) {
$part = $iter.Current
Log " Dit deel ContentType=$($part.ContentType.MimeType)"
if ($null -ne $part -and $MailboxBijlageFilter -contains $part.ContentType.MimeType -and $part.ContentType.MimeType -ne $MailboxBijlageSigned -and $null -ne $part.FileName) {
Log " --> Goede bijlage gevonden..."
Log " Bijlage #$($BijlageTeller) type: $($part.ContentType.MimeType) - Grootte: $($part.Content.Stream.Length) Naam: $($part.FileName)"
$filebasename = [System.IO.Path]::GetFileNameWithoutExtension($part.FileName.ToString())
$fileextension = [System.IO.Path]::GetExtension($part.FileName.ToString())
$LocalAttachmentFileIter = "$($TempDownloadDirectory)\$($filebasename)-$(Get-Date -Format 'yyyyMMddTHHmmss')$($fileextension)"
Log " Bijlage downloaden en wegschrijven naar $($LocalAttachmentFileIter)"
$stream = [System.IO.File]::Create($LocalAttachmentFileIter)
$part.Content.DecodeTo($stream)
$stream.Close()
$Bijlages += @{Name=$part.FileName; FileName=$LocalAttachmentFileIter}
} else {
Log " --> Bijlage heeft niet gewenste content-type,overslaan!"
}
}
Log " Bijlage is signed message - decoderen klaar,verwijderen signed attachmentfile"
Remove-Item -Path $LocalAttachmentFile
} else {
$Bijlages = @{Name=$attach.name.ToString(); FileName=$LocalAttachmentFile}
}
return $Bijlages
}
它仅过滤掉我感兴趣的附件,这些附件位于名为 $MailboxBijlageFilter
的数组中,如下所示:
$MailboxBijlageFilter = @(
'application/xml','text/xml','application/pdf','image/png','image/jpeg','image/tiff','application/octet-stream','multipart/signed'
)
此外,$MailboxBijlageSigned
变量仅包含以下内容:
$MailboxBijlageSigned = 'multipart/signed'
- 最后,这是遍历每封邮件的附件的代码:
$BijlageTeller = 0
$Bijlages = @()
foreach ($attach in $MailItem.Attachments) {
try {
$BijlageTeller++
$attach.Load()
Log " Bijlage #$($BijlageTeller) type: $($attach.ContentType) - Grootte: $($attach.Size) Naam: $($attach.Name.ToString())"
if ( $MailboxBijlageFilter -contains $attach.ContentType ) { ## Alleen maar de gewenste bijlages doorsturen,de rest gewoon skippen
$Bijlages += AttachmentNaarDisk($attach)
} else { ## Ongewenste bijlages gewoon overslaan
Log " --> Bijlage heeft niet gewenste content-type,overslaan!"
}
} catch {
Log " Bijlage #$($BijlageTeller) probleem tijdens verwerking: Message=$($_.Exception.Message);ItemName=$($_.Exception.ItemName)"
}
}