[CmdletBinding()] param( [string]$PublisherName = "MrSphay", [string]$RootSubject = "CN=MrSphay Local Trust Root", [string]$SigningSubject = "CN=MrSphay Code Signing", [string]$PublicCertificateDirectory = ".\assets\certificates", [string]$PrivateDirectory = ".\private", [int]$ValidYears = 5, [switch]$SkipPfxExport ) $ErrorActionPreference = "Stop" function Resolve-FullPath { param([Parameter(Mandatory)][string]$Path) $executionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($Path) } if ($ValidYears -lt 1 -or $ValidYears -gt 20) { throw "ValidYears must be between 1 and 20." } $publicDirectory = Resolve-FullPath $PublicCertificateDirectory $privateDirectory = Resolve-FullPath $PrivateDirectory New-Item -ItemType Directory -Force -Path $publicDirectory | Out-Null if (-not $SkipPfxExport) { New-Item -ItemType Directory -Force -Path $privateDirectory | Out-Null } $rootCertPath = Join-Path $publicDirectory "$PublisherName-LocalTrust-Root.cer" $signingCertPath = Join-Path $publicDirectory "$PublisherName-CodeSigning.cer" $pfxPath = Join-Path $privateDirectory "$PublisherName-CodeSigning.pfx" Write-Host "Creating local trust root certificate: $RootSubject" $rootCertificate = New-SelfSignedCertificate ` -Type Custom ` -Subject $RootSubject ` -KeyAlgorithm RSA ` -KeyLength 4096 ` -HashAlgorithm SHA256 ` -KeyExportPolicy Exportable ` -KeyUsage CertSign, CRLSign, DigitalSignature ` -TextExtension @("2.5.29.19={critical}{text}ca=1&pathlength=1") ` -CertStoreLocation "Cert:\CurrentUser\My" ` -NotAfter (Get-Date).AddYears($ValidYears) Write-Host "Creating code-signing certificate: $SigningSubject" $signingCertificate = New-SelfSignedCertificate ` -Type CodeSigningCert ` -Subject $SigningSubject ` -Signer $rootCertificate ` -KeyAlgorithm RSA ` -KeyLength 4096 ` -HashAlgorithm SHA256 ` -KeyExportPolicy Exportable ` -CertStoreLocation "Cert:\CurrentUser\My" ` -NotAfter (Get-Date).AddYears($ValidYears) Export-Certificate -Cert $rootCertificate -FilePath $rootCertPath -Force | Out-Null Export-Certificate -Cert $signingCertificate -FilePath $signingCertPath -Force | Out-Null if (-not $SkipPfxExport) { $password = Read-Host "Enter a strong password for the private code-signing PFX" -AsSecureString Export-PfxCertificate -Cert $signingCertificate -FilePath $pfxPath -Password $password -Force | Out-Null } Write-Host "" Write-Host "Created public root certificate:" Write-Host " $rootCertPath" Write-Host "Root thumbprint:" Write-Host " $($rootCertificate.Thumbprint)" Write-Host "" Write-Host "Created public signing certificate:" Write-Host " $signingCertPath" Write-Host "Signing thumbprint:" Write-Host " $($signingCertificate.Thumbprint)" Write-Host "" if ($SkipPfxExport) { Write-Host "Skipped private PFX export." Write-Host "Use this thumbprint for signing from the Windows certificate store:" Write-Host " $($signingCertificate.Thumbprint)" } else { Write-Host "Created private PFX:" Write-Host " $pfxPath" Write-Host "" Write-Warning "Do not commit, publish, or share the private .pfx file or its password." }