Add-Type -AssemblyName System.Windows.Forms Remove-TypeData -TypeName SslHelper -ErrorAction SilentlyContinue Remove-Variable -Name xFieldPos -ErrorAction SilentlyContinue Remove-Variable -Name xField -ErrorAction SilentlyContinue Add-Type -AssemblyName System.Drawing # Classe simplifiée pour capturer les informations SSL Add-Type @" using System; using System.Net.Security; using System.Security.Cryptography.X509Certificates; using System.Text; public static class SslHelper { public static X509Certificate2 ServerCert; public static string ExpectedFQDN; public static X509Certificate2 ClientCert; public static StringBuilder SslLog = new StringBuilder(); public static bool ValidateServerCertificate( object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) { ServerCert = new X509Certificate2(certificate); SslLog.AppendLine("=== NÉGOCIATION SSL/TLS ==="); SslLog.AppendLine("Serveur: " + ServerCert.Subject); SslLog.AppendLine("Émetteur: " + ServerCert.Issuer); bool fqdnMatch = true; if (!string.IsNullOrEmpty(ExpectedFQDN)) { fqdnMatch = false; // Vérification du Subject if (ServerCert.Subject.IndexOf("CN=" + ExpectedFQDN, StringComparison.OrdinalIgnoreCase) >= 0) { fqdnMatch = true; } // Vérification du SAN foreach (X509Extension ext in ServerCert.Extensions) { if (ext.Oid.Value == "2.5.29.17") // Subject Alternative Name { string san = ext.Format(true); if (san.IndexOf(ExpectedFQDN, StringComparison.OrdinalIgnoreCase) >= 0) { fqdnMatch = true; } } } if (fqdnMatch) { SslLog.AppendLine("✅ FQDN attendu présent dans le certificat"); } else { SslLog.AppendLine("❌ MISMATCH: FQDN attendu non trouvé dans le certificat!"); } } SslLog.AppendLine("Valide du: " + ServerCert.NotBefore.ToString("yyyy-MM-dd HH:mm:ss")); SslLog.AppendLine("Valide jusqu'au: " + ServerCert.NotAfter.ToString("yyyy-MM-dd HH:mm:ss")); SslLog.AppendLine("Empreinte: " + ServerCert.Thumbprint); SslLog.AppendLine("Algorithme de signature: " + ServerCert.SignatureAlgorithm.FriendlyName); // Si FQDN attendu est présent, ignorer le warning RemoteCertificateNameMismatch if (fqdnMatch) { if (sslPolicyErrors == System.Net.Security.SslPolicyErrors.RemoteCertificateNameMismatch) sslPolicyErrors = System.Net.Security.SslPolicyErrors.None; } if (sslPolicyErrors == SslPolicyErrors.None) { SslLog.AppendLine("✅ Certificat serveur VALIDE"); } else { SslLog.AppendLine("⚠️ Problèmes de certificat: " + sslPolicyErrors.ToString()); } return fqdnMatch; // Bloque si le FQDN attendu n'est pas trouvé } public static X509Certificate SelectClientCertificate( object sender, string targetHost, X509CertificateCollection localCertificates, X509Certificate remoteCertificate, string[] acceptableIssuers) { if (localCertificates != null && localCertificates.Count > 0) { ClientCert = localCertificates[0] as X509Certificate2; SslLog.AppendLine("=== CERTIFICAT CLIENT ==="); SslLog.AppendLine("Client: " + ClientCert.Subject); SslLog.AppendLine("Émetteur: " + ClientCert.Issuer); SslLog.AppendLine("Empreinte: " + ClientCert.Thumbprint); return ClientCert; } SslLog.AppendLine("=== AUCUN CERTIFICAT CLIENT ==="); return null; } } "@ # Création du formulaire principal ### Modernisation de l'interface $form = New-Object System.Windows.Forms.Form $form.Text = "Test d'envoi de mail SMTP" $form.Size = New-Object System.Drawing.Size(800, 1000) # Fenêtre encore plus haute pour plus de place $form.StartPosition = "CenterScreen" $form.FormBorderStyle = 'FixedDialog' $form.MaximizeBox = $false $form.BackColor = [System.Drawing.Color]::WhiteSmoke # Fond moderne # Labels et TextBoxes # Initialisation grille d'alignement AVANT tous les champs $xLabel = 40 $xFieldPos = 200 $fieldWidth = 420 $rowHeight = 38 $y = 32 # Champ Serveur $lblServer = New-Object System.Windows.Forms.Label $lblServer.Location = New-Object System.Drawing.Point($xLabel, $y) $lblServer.Size = New-Object System.Drawing.Size(120, 24) $lblServer.Text = "Serveur :" $lblServer.Font = [System.Drawing.Font]::new('Segoe UI', 11, [System.Drawing.FontStyle]::Regular) $form.Controls.Add($lblServer) $txtServer = New-Object System.Windows.Forms.TextBox $txtServer.Location = New-Object System.Drawing.Point($xFieldPos, $y) $txtServer.Size = New-Object System.Drawing.Size($fieldWidth, 24) $txtServer.Font = [System.Drawing.Font]::new('Segoe UI', 11, [System.Drawing.FontStyle]::Regular) $form.Controls.Add($txtServer) $y += $rowHeight # Champ Port $lblPort = New-Object System.Windows.Forms.Label $lblPort.Location = New-Object System.Drawing.Point($xLabel, $y) $lblPort.Size = New-Object System.Drawing.Size(120, 24) $lblPort.Text = "Port :" $lblPort.Font = [System.Drawing.Font]::new('Segoe UI', 11, [System.Drawing.FontStyle]::Regular) $form.Controls.Add($lblPort) $txtPort = New-Object System.Windows.Forms.TextBox $txtPort.Location = New-Object System.Drawing.Point($xFieldPos, $y) $txtPort.Size = New-Object System.Drawing.Size(80, 24) $txtPort.Text = "25" $txtPort.Font = [System.Drawing.Font]::new('Segoe UI', 11, [System.Drawing.FontStyle]::Regular) $form.Controls.Add($txtPort) $y += $rowHeight # Champ Expéditeur $lblFrom = New-Object System.Windows.Forms.Label $lblFrom.Location = New-Object System.Drawing.Point($xLabel, $y) $lblFrom.Size = New-Object System.Drawing.Size(120, 24) $lblFrom.Text = "Expéditeur :" $lblFrom.Font = [System.Drawing.Font]::new('Segoe UI', 11, [System.Drawing.FontStyle]::Regular) $form.Controls.Add($lblFrom) $txtFrom = New-Object System.Windows.Forms.TextBox $txtFrom.Location = New-Object System.Drawing.Point($xFieldPos, $y) $txtFrom.Size = New-Object System.Drawing.Size($fieldWidth, 24) $txtFrom.Font = [System.Drawing.Font]::new('Segoe UI', 11, [System.Drawing.FontStyle]::Regular) $form.Controls.Add($txtFrom) $y += $rowHeight # Grille d'alignement # On continue la grille après Serveur et Port $lblTo = New-Object System.Windows.Forms.Label $lblTo.Location = New-Object System.Drawing.Point($xLabel, $y) $lblTo.Size = New-Object System.Drawing.Size(120, 24) $lblTo.Text = "Destinataire :" $lblTo.Font = [System.Drawing.Font]::new('Segoe UI', 11, [System.Drawing.FontStyle]::Regular) $form.Controls.Add($lblTo) $txtTo = New-Object System.Windows.Forms.TextBox $txtTo.Location = New-Object System.Drawing.Point($xFieldPos, $y) $txtTo.Size = New-Object System.Drawing.Size($fieldWidth, 24) $txtTo.Font = [System.Drawing.Font]::new('Segoe UI', 11, [System.Drawing.FontStyle]::Regular) $form.Controls.Add($txtTo) $y += $rowHeight $lblUsername = New-Object System.Windows.Forms.Label $lblUsername.Location = New-Object System.Drawing.Point($xLabel, $y) $lblUsername.Size = New-Object System.Drawing.Size(120, 24) $lblUsername.Text = "Utilisateur :" $lblUsername.Font = [System.Drawing.Font]::new('Segoe UI', 11, [System.Drawing.FontStyle]::Regular) $form.Controls.Add($lblUsername) $txtUsername = New-Object System.Windows.Forms.TextBox $txtUsername.Location = New-Object System.Drawing.Point($xFieldPos, $y) $txtUsername.Size = New-Object System.Drawing.Size(180, 24) $txtUsername.Font = [System.Drawing.Font]::new('Segoe UI', 11, [System.Drawing.FontStyle]::Regular) $form.Controls.Add($txtUsername) $y += $rowHeight $lblPassword = New-Object System.Windows.Forms.Label $lblPassword.Location = New-Object System.Drawing.Point($xLabel, $y) $lblPassword.Size = New-Object System.Drawing.Size(120, 24) $lblPassword.Text = "Mot de passe :" $lblPassword.Font = [System.Drawing.Font]::new('Segoe UI', 11, [System.Drawing.FontStyle]::Regular) $form.Controls.Add($lblPassword) $txtPassword = New-Object System.Windows.Forms.TextBox $txtPassword.Location = New-Object System.Drawing.Point($xFieldPos, $y) $txtPassword.Size = New-Object System.Drawing.Size(180, 24) $txtPassword.PasswordChar = "*" $txtPassword.Font = [System.Drawing.Font]::new('Segoe UI', 11, [System.Drawing.FontStyle]::Regular) $form.Controls.Add($txtPassword) $y += $rowHeight $lblSubject = New-Object System.Windows.Forms.Label $lblSubject.Location = New-Object System.Drawing.Point($xLabel, $y) $lblSubject.Size = New-Object System.Drawing.Size(120, 24) $lblSubject.Text = "Sujet :" $lblSubject.Font = [System.Drawing.Font]::new('Segoe UI', 11, [System.Drawing.FontStyle]::Regular) $form.Controls.Add($lblSubject) $txtSubject = New-Object System.Windows.Forms.TextBox $txtSubject.Location = New-Object System.Drawing.Point($xFieldPos, $y) $txtSubject.Size = New-Object System.Drawing.Size($fieldWidth, 24) $txtSubject.Text = "Test SMTP avec logs détaillés" $txtSubject.Font = [System.Drawing.Font]::new('Segoe UI', 11, [System.Drawing.FontStyle]::Regular) $form.Controls.Add($txtSubject) $y += $rowHeight $lblBody = New-Object System.Windows.Forms.Label $lblBody.Location = New-Object System.Drawing.Point($xLabel, $y) $lblBody.Size = New-Object System.Drawing.Size(120, 24) $lblBody.Text = "Message :" $lblBody.Font = [System.Drawing.Font]::new('Segoe UI', 11, [System.Drawing.FontStyle]::Regular) $form.Controls.Add($lblBody) $txtBody = New-Object System.Windows.Forms.TextBox $txtBody.Location = New-Object System.Drawing.Point($xFieldPos, $y) $txtBody.Size = New-Object System.Drawing.Size($fieldWidth, 60) $txtBody.Multiline = $true $txtBody.Text = "Test d'envoi SMTP avec logging détaillé.`nCertificats SSL/TLS analysés.`nHeure: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')" $txtBody.Font = [System.Drawing.Font]::new('Segoe UI', 11, [System.Drawing.FontStyle]::Regular) $form.Controls.Add($txtBody) $y += 70 $chkStartTLS = New-Object System.Windows.Forms.CheckBox $chkStartTLS.Location = New-Object System.Drawing.Point($xFieldPos, $y) $chkStartTLS.Size = New-Object System.Drawing.Size(150, 24) $chkStartTLS.Text = "Utiliser StartTLS" $chkStartTLS.Checked = $true $chkStartTLS.Font = [System.Drawing.Font]::new('Segoe UI', 11, [System.Drawing.FontStyle]::Regular) $form.Controls.Add($chkStartTLS) $y += $rowHeight $chkLogging = New-Object System.Windows.Forms.CheckBox $chkLogging.Location = New-Object System.Drawing.Point($xFieldPos, $y) $chkLogging.Size = New-Object System.Drawing.Size(180, 24) $chkLogging.Text = "Activer les logs SMTP" $chkLogging.Checked = $true $chkLogging.Font = [System.Drawing.Font]::new('Segoe UI', 11, [System.Drawing.FontStyle]::Regular) $form.Controls.Add($chkLogging) $y += $rowHeight $lblCert = New-Object System.Windows.Forms.Label $lblCert.Location = New-Object System.Drawing.Point($xLabel, $y) $lblCert.Size = New-Object System.Drawing.Size(120, 24) $lblCert.Text = "Certificat :" $lblCert.Font = [System.Drawing.Font]::new('Segoe UI', 11, [System.Drawing.FontStyle]::Regular) $form.Controls.Add($lblCert) $comboCert = New-Object System.Windows.Forms.ComboBox $comboCert.Location = New-Object System.Drawing.Point($xFieldPos, $y) $comboCert.Size = New-Object System.Drawing.Size($fieldWidth, 24) $comboCert.DropDownStyle = "DropDownList" $comboCert.Font = [System.Drawing.Font]::new('Segoe UI', 11, [System.Drawing.FontStyle]::Regular) $form.Controls.Add($comboCert) $y += $rowHeight $lblFQDN = New-Object System.Windows.Forms.Label $lblFQDN.Location = New-Object System.Drawing.Point($xLabel, $y) $lblFQDN.Size = New-Object System.Drawing.Size(120, 24) $lblFQDN.Text = "FQDN attendu :" $lblFQDN.Font = [System.Drawing.Font]::new('Segoe UI', 11, [System.Drawing.FontStyle]::Regular) $form.Controls.Add($lblFQDN) $txtFQDN = New-Object System.Windows.Forms.TextBox $txtFQDN.Location = New-Object System.Drawing.Point($xFieldPos, $y) $txtFQDN.Size = New-Object System.Drawing.Size($fieldWidth, 24) $txtFQDN.Text = "" $txtFQDN.Font = [System.Drawing.Font]::new('Segoe UI', 11, [System.Drawing.FontStyle]::Regular) $form.Controls.Add($txtFQDN) $y += $rowHeight $chkNoClientCert = New-Object System.Windows.Forms.CheckBox $chkNoClientCert.Location = New-Object System.Drawing.Point($xFieldPos, $y) $chkNoClientCert.Size = New-Object System.Drawing.Size($fieldWidth, 24) $chkNoClientCert.Text = "Ne pas utiliser de certificat client" $chkNoClientCert.Checked = $false $chkNoClientCert.Font = [System.Drawing.Font]::new('Segoe UI', 11, [System.Drawing.FontStyle]::Regular) $form.Controls.Add($chkNoClientCert) $chkNoClientCert.Add_CheckedChanged({ $comboCert.Enabled = -not $chkNoClientCert.Checked }) $y += $rowHeight $btnLoadCerts = New-Object System.Windows.Forms.Button $btnLoadCerts.Location = New-Object System.Drawing.Point($xFieldPos, $y) $btnLoadCerts.Size = New-Object System.Drawing.Size(180, 28) $btnLoadCerts.Text = "Charger certificats" $btnLoadCerts.Font = [System.Drawing.Font]::new('Segoe UI', 11, [System.Drawing.FontStyle]::Regular) $form.Controls.Add($btnLoadCerts) $y += $rowHeight+10 $btnSend = New-Object System.Windows.Forms.Button $btnSend.Location = New-Object System.Drawing.Point([int]$xFieldPos, $y) $btnSend.Size = New-Object System.Drawing.Size(120, 32) $btnSend.Text = "Envoyer" $btnSend.Font = [System.Drawing.Font]::new('Segoe UI', 11, [System.Drawing.FontStyle]::Bold) $form.Controls.Add($btnSend) # Place le bouton 'Effacer logs' sur la même ligne, à droite de 'Envoyer' $clearLogsX = [int]$xFieldPos + 140 $btnClearLogs = New-Object System.Windows.Forms.Button $btnClearLogs.Location = New-Object System.Drawing.Point($clearLogsX, $y) $btnClearLogs.Size = New-Object System.Drawing.Size(120, 32) $btnClearLogs.Text = "Effacer logs" $btnClearLogs.Font = [System.Drawing.Font]::new('Segoe UI', 11, [System.Drawing.FontStyle]::Bold) $form.Controls.Add($btnClearLogs) # Place le bouton 'Copier log' à droite de 'Effacer logs' $copyLogX = $clearLogsX + 140 $btnCopyLog = New-Object System.Windows.Forms.Button $btnCopyLog.Location = New-Object System.Drawing.Point($copyLogX, $y) $btnCopyLog.Size = New-Object System.Drawing.Size(120, 32) $btnCopyLog.Text = "Copier log" $btnCopyLog.Font = [System.Drawing.Font]::new('Segoe UI', 11, [System.Drawing.FontStyle]::Bold) $form.Controls.Add($btnCopyLog) # Ajout de l'événement pour le bouton 'Effacer logs' $btnClearLogs.Add_Click({ $webResult.DocumentText = "" }) $btnCopyLog.Add_Click({ try { $html = $webResult.DocumentText if ($html) { [System.Windows.Forms.Clipboard]::SetText($html) } } catch { [System.Windows.Forms.MessageBox]::Show("Erreur lors de la copie du log.", "Erreur", 'OK', 'Error') } }) $y += 35# Petit espace juste sous les boutons # Ajout du contrôle WebBrowser pour le log HTML (plus grand) $y += 15 # Décale la fenêtre de log un peu plus bas $webResult = New-Object System.Windows.Forms.WebBrowser $webResult.Location = New-Object System.Drawing.Point($xLabel, $y) $webResult.Size = New-Object System.Drawing.Size(650,280) $webResult.AllowWebBrowserDrop = $false $webResult.IsWebBrowserContextMenuEnabled = $false $webResult.WebBrowserShortcutsEnabled = $false $webResult.ScriptErrorsSuppressed = $true $form.Controls.Add($webResult) # Fonction pour charger les certificats $btnLoadCerts.Add_Click({ try { $comboCert.Items.Clear() $certs = Get-ChildItem -Path "Cert:\CurrentUser\My" | Where-Object { $_.HasPrivateKey } foreach ($cert in $certs) { $item = "$($cert.Subject) (Expires: $($cert.NotAfter.ToString('yyyy-MM-dd')))" $comboCert.Items.Add($item) } if ($comboCert.Items.Count -gt 0) { $comboCert.SelectedIndex = 0 } $webResult.DocumentText = "Certificats chargés: $($comboCert.Items.Count) trouvé(s)" } catch { $webResult.DocumentText = "Erreur lors du chargement des certificats: $($_.Exception.Message)" } }) # Fonction d'envoi de mail avec logging avancé $btnSend.Add_Click({ # Réinitialiser le callback AVANT l'envoi [System.Net.ServicePointManager]::ServerCertificateValidationCallback = $null # Réinitialiser les logs [SslHelper]::SslLog.Clear() [SslHelper]::ServerCert = $null [SslHelper]::ClientCert = $null if ($txtFQDN.Text -eq "") { [SslHelper]::ExpectedFQDN = $txtServer.Text } else { [SslHelper]::ExpectedFQDN = $txtFQDN.Text } $logHtml = "" if ($chkLogging.Checked) { $logHtml += "CONFIGURATION SMTP
" $logHtml += "Serveur : $($txtServer.Text)
" $logHtml += "Port : $($txtPort.Text)
" $logHtml += "StartTLS : $($chkStartTLS.Checked)
" if ($txtUsername.Text -ne "") { $logHtml += "Authentification : $($txtUsername.Text)
" } else { $logHtml += "Authentification : Aucune
" } if ($txtFQDN.Text -ne "") { $logHtml += "FQDN attendu : $($txtFQDN.Text)
" } $logHtml += "
" } # Configurer les callbacks SSL si StartTLS est activé if ($chkStartTLS.Checked) { [System.Net.ServicePointManager]::ServerCertificateValidationCallback = [SslHelper]::ValidateServerCertificate } # Utiliser SmtpClient standard pour éviter les conflits $smtp = New-Object System.Net.Mail.SmtpClient $smtp.Host = $txtServer.Text $smtp.Port = [int]$txtPort.Text $smtp.EnableSsl = $chkStartTLS.Checked # Configuration de l'authentification si fournie if ($txtUsername.Text -ne "" -and $txtPassword.Text -ne "") { $smtp.Credentials = New-Object System.Net.NetworkCredential($txtUsername.Text, $txtPassword.Text) $smtp.UseDefaultCredentials = $false } # Configuration du certificat client si sélectionné et si la case n'est pas cochée if (-not $chkNoClientCert.Checked -and $comboCert.SelectedIndex -ge 0) { $certs = Get-ChildItem -Path "Cert:\CurrentUser\My" | Where-Object { $_.HasPrivateKey } $selectedCert = $certs[$comboCert.SelectedIndex] $smtp.ClientCertificates.Add($selectedCert) if ($chkLogging.Checked) { $logHtml += "Certificat client configuré :
" } } $mail = New-Object System.Net.Mail.MailMessage $mail.From = $txtFrom.Text $mail.To.Add($txtTo.Text) $mail.Subject = $txtSubject.Text $mail.Body = $txtBody.Text if ($chkLogging.Checked) { $logHtml += "MESSAGE
" $logHtml += "De : $($txtFrom.Text)
" $logHtml += "À : $($txtTo.Text)
" $logHtml += "Sujet : $($txtSubject.Text)

" $logHtml += "CONNEXION SMTP
" $logHtml += "Tentative de connexion...

" } $sendError = $null try { $smtp.Send($mail) } catch { $sendError = $_ } if ($chkLogging.Checked) { # Ajouter les logs SSL/TLS if ($chkStartTLS.Checked) { $logHtml += "LOGS SSL/TLS
" $logHtml += "
$( [SslHelper]::SslLog.ToString() )
" } } if ($sendError) { if ($chkStartTLS.Checked -and $txtFQDN.Text -ne "" -and [SslHelper]::SslLog.ToString().IndexOf('❌ MISMATCH', [StringComparison]::OrdinalIgnoreCase) -ge 0) { $logHtml += "❌ Erreur : le FQDN attendu n'est pas présent dans le certificat serveur. Envoi bloqué.

" } else { $logHtml += "❌ ERREUR LORS DE L'ENVOI : $($sendError.Exception.Message)

" } } else { $logHtml += "✅ MAIL ENVOYÉ AVEC SUCCÈS !

" } # Afficher les informations de certificat utilisées if ($chkStartTLS.Checked -and [SslHelper]::ServerCert) { $logHtml += "CERTIFICATS UTILISÉS
" $logHtml += "Certificat Serveur :" if ([SslHelper]::ClientCert) { $logHtml += "Certificat Client :" } else { $logHtml += "Certificat Client : Aucun utilisé
" } } $logHtml += "" $webResult.DocumentText = $logHtml $mail.Dispose() $smtp.Dispose() # Réinitialiser le callback [System.Net.ServicePointManager]::ServerCertificateValidationCallback = $null }) # Affichage du formulaire $form.ShowDialog()