feat(dev): add local TLS startup support
This commit is contained in:
@@ -0,0 +1,153 @@
|
||||
#!/usr/bin/env pwsh
|
||||
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(Mandatory)]
|
||||
[System.Net.IPAddress]$IPAddress,
|
||||
[string[]]$DnsName = @("localhost")
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
$outputDirectory = Join-Path $PSScriptRoot ".local\cmroubao-tls"
|
||||
$caCertificatePath = Join-Path $outputDirectory "ca.crt"
|
||||
$caPrivateKeyPath = Join-Path $outputDirectory "ca.key"
|
||||
$serverCertificatePath = Join-Path $outputDirectory "server.crt"
|
||||
$serverPrivateKeyPath = Join-Path $outputDirectory "server.key"
|
||||
|
||||
if ((Test-Path -LiteralPath $serverCertificatePath -PathType Leaf) -or
|
||||
(Test-Path -LiteralPath $serverPrivateKeyPath -PathType Leaf)) {
|
||||
throw "TLS server certificate already exists: $outputDirectory. Rotate it explicitly before regenerating."
|
||||
}
|
||||
if (-not (Test-Path -LiteralPath $outputDirectory)) {
|
||||
New-Item -ItemType Directory -Path $outputDirectory | Out-Null
|
||||
}
|
||||
|
||||
function Export-PemCertificate {
|
||||
param(
|
||||
[Parameter(Mandatory)]
|
||||
[System.Security.Cryptography.X509Certificates.X509Certificate2]$Certificate,
|
||||
[Parameter(Mandatory)]
|
||||
[string]$Path
|
||||
)
|
||||
|
||||
[System.IO.File]::WriteAllText(
|
||||
$Path,
|
||||
$Certificate.ExportCertificatePem(),
|
||||
[System.Text.UTF8Encoding]::new($false)
|
||||
)
|
||||
}
|
||||
|
||||
function Export-PemPrivateKey {
|
||||
param(
|
||||
[Parameter(Mandatory)]
|
||||
[System.Security.Cryptography.X509Certificates.X509Certificate2]$Certificate,
|
||||
[Parameter(Mandatory)]
|
||||
[string]$Path
|
||||
)
|
||||
|
||||
$key = [System.Security.Cryptography.X509Certificates.RSACertificateExtensions]::GetRSAPrivateKey($Certificate)
|
||||
try {
|
||||
[System.IO.File]::WriteAllText(
|
||||
$Path,
|
||||
$key.ExportPkcs8PrivateKeyPem(),
|
||||
[System.Text.UTF8Encoding]::new($false)
|
||||
)
|
||||
} finally {
|
||||
$key.Dispose()
|
||||
}
|
||||
}
|
||||
|
||||
$notBefore = [DateTimeOffset]::UtcNow.AddMinutes(-5)
|
||||
$notAfter = $notBefore.AddYears(2)
|
||||
$hashAlgorithm = [System.Security.Cryptography.HashAlgorithmName]::SHA256
|
||||
$signaturePadding = [System.Security.Cryptography.RSASignaturePadding]::Pkcs1
|
||||
|
||||
$caKey = [System.Security.Cryptography.RSA]::Create(4096)
|
||||
try {
|
||||
$caRequest = [System.Security.Cryptography.X509Certificates.CertificateRequest]::new(
|
||||
"CN=cmroubao local development CA",
|
||||
$caKey,
|
||||
$hashAlgorithm,
|
||||
$signaturePadding
|
||||
)
|
||||
$caRequest.CertificateExtensions.Add(
|
||||
[System.Security.Cryptography.X509Certificates.X509BasicConstraintsExtension]::new($true, $false, 0, $true)
|
||||
)
|
||||
$caRequest.CertificateExtensions.Add(
|
||||
[System.Security.Cryptography.X509Certificates.X509KeyUsageExtension]::new(
|
||||
[System.Security.Cryptography.X509Certificates.X509KeyUsageFlags]::KeyCertSign -bor
|
||||
[System.Security.Cryptography.X509Certificates.X509KeyUsageFlags]::CrlSign,
|
||||
$true
|
||||
)
|
||||
)
|
||||
$caRequest.CertificateExtensions.Add(
|
||||
[System.Security.Cryptography.X509Certificates.X509SubjectKeyIdentifierExtension]::new($caRequest.PublicKey, $false)
|
||||
)
|
||||
$caCertificate = $caRequest.CreateSelfSigned($notBefore, $notAfter)
|
||||
try {
|
||||
Export-PemCertificate -Certificate $caCertificate -Path $caCertificatePath
|
||||
Export-PemPrivateKey -Certificate $caCertificate -Path $caPrivateKeyPath
|
||||
|
||||
$serverKey = [System.Security.Cryptography.RSA]::Create(2048)
|
||||
try {
|
||||
$serverRequest = [System.Security.Cryptography.X509Certificates.CertificateRequest]::new(
|
||||
"CN=$IPAddress",
|
||||
$serverKey,
|
||||
$hashAlgorithm,
|
||||
$signaturePadding
|
||||
)
|
||||
$serverRequest.CertificateExtensions.Add(
|
||||
[System.Security.Cryptography.X509Certificates.X509BasicConstraintsExtension]::new($false, $false, 0, $false)
|
||||
)
|
||||
$serverRequest.CertificateExtensions.Add(
|
||||
[System.Security.Cryptography.X509Certificates.X509KeyUsageExtension]::new(
|
||||
[System.Security.Cryptography.X509Certificates.X509KeyUsageFlags]::DigitalSignature -bor
|
||||
[System.Security.Cryptography.X509Certificates.X509KeyUsageFlags]::KeyEncipherment,
|
||||
$true
|
||||
)
|
||||
)
|
||||
$serverAuth = [System.Security.Cryptography.OidCollection]::new()
|
||||
$serverAuth.Add([System.Security.Cryptography.Oid]::new("1.3.6.1.5.5.7.3.1")) | Out-Null
|
||||
$serverRequest.CertificateExtensions.Add(
|
||||
[System.Security.Cryptography.X509Certificates.X509EnhancedKeyUsageExtension]::new($serverAuth, $false)
|
||||
)
|
||||
$subjectAlternativeNames = [System.Security.Cryptography.X509Certificates.SubjectAlternativeNameBuilder]::new()
|
||||
$subjectAlternativeNames.AddIpAddress($IPAddress)
|
||||
foreach ($name in $DnsName) {
|
||||
if (-not [string]::IsNullOrWhiteSpace($name)) {
|
||||
$subjectAlternativeNames.AddDnsName($name.Trim())
|
||||
}
|
||||
}
|
||||
$serverRequest.CertificateExtensions.Add($subjectAlternativeNames.Build())
|
||||
$serverRequest.CertificateExtensions.Add(
|
||||
[System.Security.Cryptography.X509Certificates.X509SubjectKeyIdentifierExtension]::new($serverRequest.PublicKey, $false)
|
||||
)
|
||||
$serial = New-Object byte[] 16
|
||||
[System.Security.Cryptography.RandomNumberGenerator]::Fill($serial)
|
||||
$serverCertificate = $serverRequest.Create($caCertificate, $notBefore, $notAfter, $serial)
|
||||
$serverCertificateWithKey = [System.Security.Cryptography.X509Certificates.RSACertificateExtensions]::CopyWithPrivateKey(
|
||||
$serverCertificate,
|
||||
$serverKey
|
||||
)
|
||||
try {
|
||||
Export-PemCertificate -Certificate $serverCertificateWithKey -Path $serverCertificatePath
|
||||
Export-PemPrivateKey -Certificate $serverCertificateWithKey -Path $serverPrivateKeyPath
|
||||
} finally {
|
||||
$serverCertificateWithKey.Dispose()
|
||||
$serverCertificate.Dispose()
|
||||
}
|
||||
} finally {
|
||||
$serverKey.Dispose()
|
||||
}
|
||||
} finally {
|
||||
$caCertificate.Dispose()
|
||||
}
|
||||
} finally {
|
||||
$caKey.Dispose()
|
||||
}
|
||||
|
||||
Write-Host "Created local CA certificate: $caCertificatePath"
|
||||
Write-Host "Created TLS server certificate: $serverCertificatePath"
|
||||
Write-Host "Server SAN IP: $IPAddress"
|
||||
Write-Host "Install only ca.crt on the Debug phone. Never copy ca.key or server.key to the phone."
|
||||
Reference in New Issue
Block a user