160 lines
7.2 KiB
PowerShell
160 lines
7.2 KiB
PowerShell
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"
|