2017-07-25 4 views
2

Je cherche à créer un script PowerShell qui:Comment signer et chiffrer un message en utilisant S/MIME dans PowerShell

  • Construire un message
  • Signer le message en utilisant mon certificat privé S/MIME
  • Chiffrer le message en utilisant le cert publique S/MIME du destinataire
  • Envoyer l'e-mail qui a été signé et chiffré

I ont inclus le script complet ci-dessous, mais ont changé d'adresse e-mail, de nom de certificat, etc.

Le certificat privé a été importé sur la machine à l'aide d'Internet Explorer. Il est ensuite référencé dans le répertoire C:\Users\xxx\AppData\Roaming\Microsoft\SystemCertificates\My\Certificates\

Le problème est que lorsque j'envoie l'email en utilisant le script, il est crypté mais pas signé.

Toutefois, si je ne crypte pas le message et que j'inclue $SignedMessageBytes lors de la création du flux de mémoire (voir la première ligne à l'étape 4 du script), l'e-mail est correctement signé lors de l'envoi. Cela suggère que le message est correctement signé avant le cryptage.

Pour une raison quelconque, le script n'inclura pas la signature lors du cryptage du message.

Que dois-je faire pour que la signature soit incluse lorsque le message est crypté?

$SMTPServer = "localhost" 
$Recipient = "[email protected]" 
$From = "[email protected]" 
$RecipientCertificatePath = "C:\[email protected]" 
$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) 

Répondre

1

Merci pour votre travail de terrain.

Je l'ai travail en ajoutant une couche supplémentaire mime:

# 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() 

Je ne sais pas si le code ci-dessus fonctionne hors de la boîte puisque mes noms de variables peuvent être différentes de la vôtre.

Le code ci-dessus est uniquement testé avec Outlook2016.

+0

@abil C'est fantastique. Merci pour ça. Y a-t-il une chance que vous puissiez convertir pour qu'il crypte d'abord et ensuite signe (plutôt que de signer et crypter). J'ai essayé mais j'ai fini avec plusieurs couches de cryptage et de signature et cela n'a pas fonctionné. – gerard