[CmdletBinding()] param( [Parameter(Mandatory = $false)] [string]$File, [Parameter(Mandatory = $false)] [switch]$Run ) $ErrorActionPreference = "Stop" $ProgressPreference = "SilentlyContinue" $ManifestUrl = "https://outilsia.fr/static/downloads/local-cockpit/release.json" $ManifestSignatureUrl = "$ManifestUrl.sig" $ReleaseKeyId = "outilsia-release-rsa3072-ba72b701ced66996" $ReleaseSignatureSchema = "outilsia-release-signature/v1" $ReleaseSignatureAlgorithm = "RSASSA-PKCS1-v1_5-SHA256" $ReleaseModulusBase64 = "5lHZOwodHdl+Xak+o53joLLe4ZHsFfrH8BQWGS2jqk4E/UOxZPQ6P5vgukkVrdTRQyl58PNhZ0LiAdepTJU6C3uBUhgauzQUcUOfAKOVq9e++qjIbQrfnP1dvUxDmZVwZoDzlOBRTtXfbEGxDo7pgVclubDL4Qbm7PlhIN6bK0F6oSvRqSwJm//MSdOQhkhOyg1rMmYEtq7n/WhMInl36gY7bwahHom+i416dDUTintCHly3Un/00PTNs+HQxpoiTfg6JOeCXKn1Rx49E5gA5Xa62oZPRzXRz3fdIukXNeZ7B76RISZfIvftl2kxMFOvsmjPDlltLU18a4kaZ3uu5DJW5/BjovXf9dqIAKkSTYaqYIqdibWd9qmOVGFcje2yGuJHGX3KiUdY4RQe+uZxIjoPcRbzrJOxVaIzV36PuKkp5Hmv3NzeolG/+wwh0I6vgr/Tvtv5HNwXiBPrYWcvb2vlwF+lfB6/p4GGNQaHhPkKCrCL/emsaeHrUWbEMsWR" $ReleaseExponentBase64 = "AQAB" function Stop-Blocked([string]$Reason) { Write-Host "" Write-Host "BLOQUE - NE PAS EXECUTER" -ForegroundColor Red Write-Host $Reason -ForegroundColor Red exit 23 } function Get-BytesSha256Hex([byte[]]$Bytes) { $sha = [System.Security.Cryptography.SHA256]::Create() try { return ([BitConverter]::ToString($sha.ComputeHash($Bytes))).Replace("-", "").ToLowerInvariant() } finally { $sha.Dispose() } } function Get-VerifiedManifest { $manifestClient = New-Object System.Net.WebClient $signatureClient = New-Object System.Net.WebClient try { $manifestClient.Headers["Cache-Control"] = "no-cache" $signatureClient.Headers["Cache-Control"] = "no-cache" [byte[]]$manifestBytes = $manifestClient.DownloadData($ManifestUrl) [byte[]]$signatureBytes = $signatureClient.DownloadData($ManifestSignatureUrl) } finally { $manifestClient.Dispose() $signatureClient.Dispose() } $receipt = [Text.Encoding]::UTF8.GetString($signatureBytes) | ConvertFrom-Json if ($receipt.ok -ne $true -or $receipt.schema -cne $ReleaseSignatureSchema -or $receipt.key_id -cne $ReleaseKeyId -or $receipt.algorithm -cne $ReleaseSignatureAlgorithm) { Stop-Blocked "La signature du manifeste officiel est absente ou invalide." } $manifestHash = Get-BytesSha256Hex $manifestBytes if ($receipt.manifest_sha256 -cne $manifestHash) { Stop-Blocked "Le manifeste officiel a ete modifie apres sa signature." } try { [byte[]]$signature = [Convert]::FromBase64String([string]$receipt.signature_base64) $parameters = New-Object System.Security.Cryptography.RSAParameters $parameters.Modulus = [Convert]::FromBase64String($ReleaseModulusBase64) $parameters.Exponent = [Convert]::FromBase64String($ReleaseExponentBase64) $rsa = [System.Security.Cryptography.RSA]::Create() $rsa.ImportParameters($parameters) $valid = $rsa.VerifyData( $manifestBytes, $signature, [System.Security.Cryptography.HashAlgorithmName]::SHA256, [System.Security.Cryptography.RSASignaturePadding]::Pkcs1 ) $rsa.Dispose() } catch { Stop-Blocked "La verification cryptographique du manifeste a echoue." } if (-not $valid) { Stop-Blocked "La signature cryptographique du manifeste ne correspond pas." } return [Text.Encoding]::UTF8.GetString($manifestBytes) | ConvertFrom-Json } try { if ([string]::IsNullOrWhiteSpace($File)) { Add-Type -AssemblyName System.Windows.Forms $dialog = New-Object System.Windows.Forms.OpenFileDialog $dialog.Title = "Choisir un installateur OutilsIA a verifier" $dialog.Filter = "Installateurs OutilsIA (*.exe;*.msi)|*.exe;*.msi" $dialog.Multiselect = $false if ($dialog.ShowDialog() -ne [System.Windows.Forms.DialogResult]::OK) { Stop-Blocked "Aucun fichier selectionne." } $File = $dialog.FileName } $item = Get-Item -LiteralPath $File -ErrorAction Stop if ($item.PSIsContainer) { Stop-Blocked "Le chemin selectionne est un dossier." } $extension = $item.Extension.ToLowerInvariant() if ($extension -notin @(".exe", ".msi")) { Stop-Blocked "Seuls les fichiers EXE et MSI sont autorises par ce garde Windows." } [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 $manifest = Get-VerifiedManifest if ($manifest.ok -ne $true -or $null -eq $manifest.files) { Stop-Blocked "Le manifeste officiel est absent ou invalide." } $matches = @($manifest.files | Where-Object { $_.name -ceq $item.Name }) if ($matches.Count -ne 1) { Stop-Blocked "Le fichier est inconnu, perime ou ambigu dans le manifeste courant." } $expected = $matches[0] $expectedHash = ([string]$expected.sha256).ToLowerInvariant() if ($expectedHash -notmatch '^[a-f0-9]{64}$') { Stop-Blocked "L'empreinte officielle est mal formee." } if ([int64]$expected.size_bytes -ne [int64]$item.Length) { Stop-Blocked "La taille du fichier ne correspond pas au manifeste officiel." } $firstHash = (Get-FileHash -LiteralPath $item.FullName -Algorithm SHA256).Hash.ToLowerInvariant() if ($firstHash -cne $expectedHash) { Stop-Blocked "Le SHA-256 du fichier ne correspond pas au manifeste officiel." } # Recompute immediately before launch so a changed file cannot reuse an old verdict. $item = Get-Item -LiteralPath $item.FullName -ErrorAction Stop $secondHash = (Get-FileHash -LiteralPath $item.FullName -Algorithm SHA256).Hash.ToLowerInvariant() if ($secondHash -cne $expectedHash -or [int64]$expected.size_bytes -ne [int64]$item.Length) { Stop-Blocked "Le fichier a change pendant la verification." } Write-Host "" Write-Host "FICHIER OFFICIEL INTACT" -ForegroundColor Green Write-Host "Nom : $($item.Name)" Write-Host "Taille : $($item.Length) octets" Write-Host "SHA-256 : $secondHash" Write-Host "Manifeste: $ManifestUrl" Write-Host "Signature: valide ($ReleaseKeyId)" if (-not $Run) { Write-Host "" Write-Host "Verification terminee. Aucun programme n'a ete lance." -ForegroundColor Yellow exit 0 } Write-Host "Lancement autorise par le garde OutilsIA." -ForegroundColor Green if ($extension -eq ".msi") { Start-Process -FilePath "msiexec.exe" -ArgumentList @("/i", ('"' + $item.FullName + '"')) } else { Start-Process -FilePath $item.FullName } exit 0 } catch { Stop-Blocked "La verification a echoue. Aucun fichier n'a ete lance." }