如何在PowerShell中使用S/MIME对消息进行签名和加密 [英] How to sign and encrypt a message using S/MIME in PowerShell

查看:64
本文介绍了如何在PowerShell中使用S/MIME对消息进行签名和加密的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试创建一个PowerShell脚本,该脚本将:

I am attempting to create a PowerShell script that will:

  • 创建一条消息
  • 使用我的私人S/MIME证书签名邮件
  • 使用收件人的S/MIME公共证书对邮件进行加密
  • 发送已签名和加密的电子邮件

我在下面包括了完整的脚本,但是更改了电子邮件地址,证书名称等.

I have included the full script below but changed email addresses, cert names, etc.

已使用Internet Explorer将私有证书导入到计算机中.然后在目录 C:\ Users \ xxx \ AppData \ Roaming \ Microsoft \ SystemCertificates \ My \ Certificates \

The private cert has been imported onto the machine using Internet Explorer. It is then referenced within the directory C:\Users\xxx\AppData\Roaming\Microsoft\SystemCertificates\My\Certificates\

问题在于,当我使用脚本发送电子邮件时,该电子邮件已加密但未签名.

The problem is that when I send the email using the script it is being encrypted but not signed.

但是,如果我不加密消息,而是在构建内存流时包括 $ SignedMessageBytes (请参见脚本的第4行),则在发送电子邮件时会正确签名.这表明邮件在加密之前已正确签名.

However, if I don't encrypt the message and instead include $SignedMessageBytes when building the memory stream (see first line in step 4 of script) the email is signed correctly when being sent. This would suggest that the message is being correctly signed before the encryption occurs.

由于某种原因,脚本在加密邮件时不会包含签名.

For some reason the script won't include the signature when encrypting the message.

我应该怎么做,以便在对邮件进行加密时包括签名?

What must I do so that the signature is included when the message is encrypted?

$SMTPServer = "localhost"
$Recipient = "recipient@emailaddress.com"
$From = "sender@emailaddress.com"
$RecipientCertificatePath = "C:\recipient@emailaddress.com.cer"
$SignerCertificatePath = "C:\Users\xxx\AppData\Roaming\Microsoft\SystemCertificates\My\Certificates\xxxx"

Add-Type -assemblyName "System.Security"
$MailClient = New-Object System.Net.Mail.SmtpClient $SMTPServer
$Message = New-Object System.Net.Mail.MailMessage
$Message.To.Add($Recipient)
$Message.From = $From
$Body = $null
$File= get-item -Path "C:\CONTRL__9911837000009_4045399000008_20170704_ELE00207.TXT"
$Message.Subject = $File.Name

# STEP 1: Capture Message Body
$MIMEMessage = New-Object system.Text.StringBuilder
$MIMEMessage.AppendLine("MIME-Version: 1.0") | Out-Null
$MIMEMessage.AppendLine("Content-Type: multipart/mixed; boundary=unique-boundary-1") | Out-Null
$MIMEMessage.AppendLine() | Out-Null
$MIMEMessage.AppendLine("This is a multi-part message in MIME format.") | Out-Null
$MIMEMessage.AppendLine("--unique-boundary-1") | Out-Null
$MIMEMessage.AppendLine("Content-Type: text/plain") | Out-Null
$MIMEMessage.AppendLine("Content-Transfer-Encoding: 7Bit") | Out-Null
$MIMEMessage.AppendLine() | Out-Null
$MIMEMessage.AppendLine($Body) | Out-Null
$MIMEMessage.AppendLine() | Out-Null
$MIMEMessage.AppendLine("--unique-boundary-1") | Out-Null
$MIMEMessage.AppendLine("Content-Type: application/octet-stream; name="+ $file.Name) | Out-Null
$MIMEMessage.AppendLine("Content-Transfer-Encoding: base64") | Out-Null
$MIMEMessage.AppendLine("Content-Disposition: attachment; filename="+ $file.Name) | Out-Null
$MIMEMessage.AppendLine() | Out-Null

[Byte[]] $binaryData = [System.IO.File]::ReadAllBytes($File)
[string] $base64Value = [System.Convert]::ToBase64String($binaryData, 0, $binaryData.Length)
[int] $position = 0
while($position -lt $base64Value.Length)
{
    [int] $chunkSize = 100
    if (($base64Value.Length - ($position + $chunkSize)) -lt 0)
    {
        $chunkSize = $base64Value.Length - $position
    }
    $MIMEMessage.AppendLine($base64Value.Substring($position, $chunkSize)) | Out-Null
    $MIMEMessage.AppendLine() | Out-Null
    $position += $chunkSize;
}

$MIMEMessage.AppendLine("--unique-boundary-1--") | Out-Null
[Byte[]] $MessageBytes = [System.Text.Encoding]::ASCII.GetBytes($MIMEMessage.ToString())


# STEP 2: Sign
$ci = New-Object System.Security.Cryptography.Pkcs.ContentInfo(,$MessageBytes)
$signedCms = New-Object System.Security.Cryptography.Pkcs.SignedCms($ci)

$SignerCertificate = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2($SignerCertificatePath)
$Signer = New-Object System.Security.Cryptography.Pkcs.CmsSigner( $SignerCertificate )
$timeAttribute = New-Object -TypeName System.Security.Cryptography.Pkcs.Pkcs9SigningTime
$null = $signer.SignedAttributes.Add($timeAttribute)
$sha2_oid = New-Object System.Security.Cryptography.Oid("2.16.840.1.101.3.4.2.1")
$Signer.DigestAlgorithm = $sha2_oid

Write-Host "-----------------------------------------------------"
Write-Host "Cert friendly name: " $Signer.Certificate.FriendlyName
Write-Host "Cert subject      : " $Signer.Certificate.Subject
Write-Host "Cert thumbprint   : " $Signer.Certificate.Thumbprint
Write-Host "Digest algorithm  : " $Signer.DigestAlgorithm.FriendlyName
Write-Host "Sign Time         : " $Signer.SignedAttributes.Values.SigningTime

$signedCms.ComputeSignature($Signer)
$SignedMessageBytes = $signedCms.Encode()


# STEP 3: Encrypt
$ContentInfo = New-Object System.Security.Cryptography.Pkcs.ContentInfo (,$SignedMessageBytes)
$CMSRecipient = New-Object System.Security.Cryptography.Pkcs.CmsRecipient $RecipientCertificatePath
$algo_id = New-Object System.Security.Cryptography.Pkcs.AlgorithmIdentifier("2.16.840.1.101.3.4.1.42")
$EnvelopedCMS = New-Object System.Security.Cryptography.Pkcs.EnvelopedCms( $ContentInfo , $algo_id )

$EnvelopedCMS.Encrypt($CMSRecipient)

Write-Host "Key length       : " $EnvelopedCMS.ContentEncryptionAlgorithm.KeyLength
Write-Host "OID friendly name: " $EnvelopedCMS.ContentEncryptionAlgorithm.Oid.FriendlyName
Write-Host "OID value        : " $EnvelopedCMS.ContentEncryptionAlgorithm.Oid.Value
Write-Host "Parameters       : " $EnvelopedCMS.ContentEncryptionAlgorithm.Parameters

[Byte[]] $EncryptedBytes = $EnvelopedCMS.Encode()


# STEP 4: Create and send mail
$MemoryStream = New-Object System.IO.MemoryStream @(,$EncryptedBytes)
$AlternateView = New-Object System.Net.Mail.AlternateView($MemoryStream, "application/x-pkcs7-mime; smime-type=enveloped-data;name=smime.p7m")
$Message.AlternateViews.Add($AlternateView)
$MailClient.Send($Message)

推荐答案

感谢您的基础工作.

我通过添加一个额外的mime层使其工作:

I got it working by adding an additional mime layer:

# STEP 3: Encrypt
$OID = New-Object System.Security.Cryptography.Oid 2.16.840.1.101.3.4.1.42
$AId = New-Object System.Security.Cryptography.Pkcs.AlgorithmIdentifier ($OID, 256)

$SignatureBytes = $SignedCMS.Encode()
$MIMEMessage2 = New-Object system.Text.StringBuilder 
$MIMEMessage2.AppendLine('Content-Type: application/pkcs7-mime; smime-type=enveloped-data;name=smime.p7m') | Out-Null 
$MIMEMessage2.AppendLine('Content-Transfer-Encoding: base64') | Out-Null 
$MIMEMessage2.AppendLine() | Out-Null 
$MIMEMessage2.AppendLine([Convert]::ToBase64String($SignedMessageBytes)) | Out-Null

Byte[]] $BodyBytes = [System.Text.Encoding]::UTF8.GetBytes($MIMEMessage2.ToString())

ContentInfo = New-Object System.Security.Cryptography.Pkcs.ContentInfo (,$BodyBytes)

$CMSRecipient = New-Object System.Security.Cryptography.Pkcs.CmsRecipient $ChosenCertificate 
$EnvelopedCMS = New-Object System.Security.Cryptography.Pkcs.EnvelopedCms( $ContentInfo, $AId)
$EnvelopedCMS.Encrypt($CMSRecipient) 
[Byte[]] $EncryptedBytes = $EnvelopedCMS.Encode() 

我不确定上面的代码是否可以直接使用,因为我的变量名可能与您的变量名不同.

I'm not sure if the code above will work out of the box since my variable names may be different from yours.

上面的代码仅在Outlook2016中经过测试.

The code above is only tested with Outlook2016.

这篇关于如何在PowerShell中使用S/MIME对消息进行签名和加密的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆