CmvrControlStation/scripts/install_msquic.ps1

160 lines
7.2 KiB
PowerShell
Raw Normal View History

2026-09-18 16:22:20 +08:00
param(
[switch]$Force,
[string]$OpenSslPath = "",
[string]$ServerIp = "127.0.0.1",
[string]$CaCertificatePath = "",
[string]$CaPrivateKeyPath = ""
)
$ErrorActionPreference = "Stop"
$version = "2.5.9"
$packageHash = "FEE9A664C0052DEEE66D40CED2E19BFE20BC423DE5AE5234CFC0CEBC22CA3F5E"
$dllHash = "C17C65813FE007EB486A5270D244F3659261355795B27ABB99BD19FF77539263"
$projectRoot = Split-Path -Parent $PSScriptRoot
$downloads = Join-Path $projectRoot "third_party\downloads"
$packages = Join-Path $projectRoot "third_party\packages"
$installRoot = Join-Path $projectRoot "third_party\install\msquic"
$packageFile = Join-Path $downloads "msquic-openssl-$version.nupkg"
$archiveFile = "$packageFile.zip"
$packageRoot = Join-Path $packages "msquic-openssl-$version"
$packageNative = Join-Path $packageRoot "build\native"
$url = "https://api.nuget.org/v3-flatcontainer/microsoft.native.quic.msquic.openssl/$version/microsoft.native.quic.msquic.openssl.$version.nupkg"
New-Item -ItemType Directory -Force $downloads, $packages, $installRoot | Out-Null
$downloadRequired = $Force -or -not (Test-Path $packageFile)
if (-not $downloadRequired) {
$actualHash = (Get-FileHash $packageFile -Algorithm SHA256).Hash
$downloadRequired = $actualHash -ne $packageHash
}
if ($downloadRequired) {
Write-Host "Downloading MsQuic $version (OpenSSL)..."
Invoke-WebRequest -Uri $url -OutFile $packageFile
}
if ((Get-FileHash $packageFile -Algorithm SHA256).Hash -ne $packageHash) {
throw "MsQuic NuGet SHA256 verification failed: $packageFile"
}
if ($Force -or -not (Test-Path (Join-Path $packageNative "include\msquic.h"))) {
if (Test-Path $packageRoot) {
Remove-Item -LiteralPath $packageRoot -Recurse -Force
}
Copy-Item -LiteralPath $packageFile -Destination $archiveFile -Force
Expand-Archive -LiteralPath $archiveFile -DestinationPath $packageRoot -Force
}
$binDir = Join-Path $installRoot "bin"
$includeDir = Join-Path $installRoot "include"
$licenseDir = Join-Path $installRoot "licenses"
$certDir = Join-Path $installRoot "certs"
New-Item -ItemType Directory -Force $binDir, $includeDir, $licenseDir, $certDir | Out-Null
Copy-Item -LiteralPath (Join-Path $packageNative "bin\x64\msquic.dll") -Destination $binDir -Force
Copy-Item -LiteralPath (Join-Path $packageNative "include\msquic.h") -Destination $includeDir -Force
Copy-Item -LiteralPath (Join-Path $packageNative "include\msquic_winuser.h") -Destination $includeDir -Force
Copy-Item -LiteralPath (Join-Path $packageRoot "LICENSE") -Destination $licenseDir -Force
$installedDll = Join-Path $binDir "msquic.dll"
if ((Get-FileHash $installedDll -Algorithm SHA256).Hash -ne $dllHash) {
throw "Installed msquic.dll SHA256 verification failed: $installedDll"
}
if ([string]::IsNullOrWhiteSpace($OpenSslPath)) {
$openSslCandidates = @(
"C:\Program Files\Git\mingw64\bin\openssl.exe",
"C:\Program Files\Git\usr\bin\openssl.exe",
"C:\Strawberry\c\bin\openssl.exe"
)
$OpenSslPath = $openSslCandidates | Where-Object { Test-Path $_ } | Select-Object -First 1
}
if (-not $OpenSslPath -or -not (Test-Path $OpenSslPath)) {
throw "OpenSSL was not found. Pass -OpenSslPath to generate the QUIC test certificate."
}
$parsedServerIp = $null
if (-not [System.Net.IPAddress]::TryParse($ServerIp, [ref]$parsedServerIp)) {
throw "ServerIp must be a numeric IPv4 or IPv6 address: $ServerIp"
}
$externalCaRequested = -not [string]::IsNullOrWhiteSpace($CaCertificatePath) -or
-not [string]::IsNullOrWhiteSpace($CaPrivateKeyPath)
if ($externalCaRequested -and
([string]::IsNullOrWhiteSpace($CaCertificatePath) -or
[string]::IsNullOrWhiteSpace($CaPrivateKeyPath))) {
throw "CaCertificatePath and CaPrivateKeyPath must be specified together."
}
$exportedCaCertificate = Join-Path $certDir "quic-test-ca.crt"
if ($externalCaRequested) {
if (-not (Test-Path -LiteralPath $CaCertificatePath)) {
throw "CA certificate was not found: $CaCertificatePath"
}
if (-not (Test-Path -LiteralPath $CaPrivateKeyPath)) {
throw "CA private key was not found: $CaPrivateKeyPath"
}
$signingCaCertificate = (Resolve-Path -LiteralPath $CaCertificatePath).Path
$signingCaPrivateKey = (Resolve-Path -LiteralPath $CaPrivateKeyPath).Path
Copy-Item -LiteralPath $signingCaCertificate -Destination $exportedCaCertificate -Force
} else {
$signingCaCertificate = $exportedCaCertificate
$signingCaPrivateKey = Join-Path $certDir "quic-test-ca.key.pem"
if ($Force -or
-not (Test-Path -LiteralPath $signingCaCertificate) -or
-not (Test-Path -LiteralPath $signingCaPrivateKey)) {
& $OpenSslPath req -x509 -newkey rsa:3072 -sha256 -nodes -days 3650 `
-subj "/CN=CMVR QUIC Test CA" `
-addext "basicConstraints=critical,CA:TRUE" `
-addext "keyUsage=critical,keyCertSign,cRLSign" `
-keyout $signingCaPrivateKey -out $signingCaCertificate
if ($LASTEXITCODE -ne 0) { throw "OpenSSL failed to create the QUIC test CA." }
}
}
$certificate = Join-Path $certDir "quic-test-server.pfx"
$temporaryKey = Join-Path $certDir "quic-test-server.key.pem"
$temporaryRequest = Join-Path $certDir "quic-test-server.csr.pem"
$serverCertificate = Join-Path $certDir "quic-test-server.crt"
$extensionFile = Join-Path $certDir "quic-test-server.ext"
$serialFile = Join-Path $certDir "quic-test-ca.srl"
@(
"authorityKeyIdentifier=keyid,issuer"
"basicConstraints=critical,CA:FALSE"
"keyUsage=critical,digitalSignature,keyEncipherment"
"extendedKeyUsage=serverAuth"
"subjectAltName=DNS:localhost,IP:127.0.0.1,IP:$ServerIp"
) | Set-Content -LiteralPath $extensionFile -Encoding Ascii
& $OpenSslPath req -new -newkey rsa:2048 -sha256 -nodes `
-subj "/CN=$ServerIp" -keyout $temporaryKey -out $temporaryRequest
if ($LASTEXITCODE -ne 0) { throw "OpenSSL failed to create the QUIC server certificate request." }
$signArguments = @(
"x509", "-req", "-in", $temporaryRequest,
"-CA", $signingCaCertificate, "-CAkey", $signingCaPrivateKey,
"-CAserial", $serialFile, "-days", "825", "-sha256",
"-extfile", $extensionFile, "-out", $serverCertificate
)
if (-not (Test-Path -LiteralPath $serialFile)) {
$signArguments += "-CAcreateserial"
}
& $OpenSslPath @signArguments
if ($LASTEXITCODE -ne 0) { throw "OpenSSL failed to sign the QUIC server certificate." }
& $OpenSslPath pkcs12 -export -out $certificate -inkey $temporaryKey `
-in $serverCertificate -certfile $exportedCaCertificate `
-name "cmvr-quic-test-server" -password "pass:cmvr-test"
if ($LASTEXITCODE -ne 0) { throw "OpenSSL failed to export the QUIC PKCS#12 certificate." }
& $OpenSslPath verify -CAfile $exportedCaCertificate $serverCertificate
if ($LASTEXITCODE -ne 0) { throw "The generated QUIC server certificate does not verify against the selected CA." }
& $OpenSslPath x509 -in $serverCertificate -noout -checkip $ServerIp
if ($LASTEXITCODE -ne 0) { throw "The generated QUIC server certificate does not contain IP SAN $ServerIp." }
Remove-Item -LiteralPath $temporaryKey, $temporaryRequest, $extensionFile -Force
Write-Host "MsQuic $version installed: $installedDll"
Write-Host "QUIC server certificate: $certificate (password: cmvr-test)"
Write-Host "QUIC client CA certificate: $exportedCaCertificate"
Write-Host "Certificate IP SAN: $ServerIp"