Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions PowerShellBuild/Private/Import-PSBuildX509Certificate.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
function Import-PSBuildX509Certificate {
<#
.SYNOPSIS
Construct an X509Certificate2 from raw PFX bytes or from a PFX file on disk.
.DESCRIPTION
Get-PSBuildCertificate loads a certificate from two places that are not the Windows
certificate store: a Base64 payload held in an environment variable, and a PFX file on
disk. Both go through the X509Certificate2 constructor, and a constructor is a .NET call
rather than a command, so nothing downstream of it could be tested without a certificate
that the running machine happened to have.

Naming the load as a command gives it a seam. With the load replaced, the validation
Get-PSBuildCertificate applies afterwards -- private key, expiry, and the Code Signing
extended key usage -- can be driven against a certificate of the caller's choosing on any
platform. That validation had never executed before psake/PowerShellBuild#216.

No validation happens here, and nothing is written to a certificate store. Whatever the
constructor throws is left to propagate: its message about malformed input is more
specific than anything this function could add.
.PARAMETER RawData
The decoded PFX bytes to construct the certificate from.
.PARAMETER Password
Password protecting the PFX bytes. Omit it when the payload carries no password; an
empty string and no password at all are not the same thing to the loader.
.PARAMETER FilePath
Path to the PFX or P12 file to construct the certificate from.
.PARAMETER FilePassword
Password protecting the PFX file, as a SecureString. Omit it when the file carries no
password.
.EXAMPLE
PS> Import-PSBuildX509Certificate -RawData $decodedBytes -Password $password

Construct a certificate from the bytes of a Base64-encoded PFX held in a CI secret.
.EXAMPLE
PS> Import-PSBuildX509Certificate -FilePath ./signing-certificate.pfx -FilePassword $securePassword

Construct a certificate from a PFX file on disk.
.OUTPUTS
System.Security.Cryptography.X509Certificates.X509Certificate2
#>
[CmdletBinding(DefaultParameterSetName = 'RawData')]
[OutputType([System.Security.Cryptography.X509Certificates.X509Certificate2])]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute(
'PSAvoidUsingPlainTextForPassword',
'Password',
Justification = 'The X509Certificate2 overload that takes raw bytes takes the password as a string, and the caller reads it from an environment variable that is already a string.'
)]
param(
[Parameter(Mandatory, ParameterSetName = 'RawData')]
[byte[]]$RawData,

[Parameter(ParameterSetName = 'RawData')]
[AllowEmptyString()]
[string]$Password,

[Parameter(Mandatory, ParameterSetName = 'FilePath')]
[string]$FilePath,

[Parameter(ParameterSetName = 'FilePath')]
[securestring]$FilePassword

Check warning on line 60 in PowerShellBuild/Private/Import-PSBuildX509Certificate.ps1

View workflow job for this annotation

GitHub Actions / CI / Run Linters

Unknown word (securestring) Suggestions: (secreting, sequestering)
)

if ($PSCmdlet.ParameterSetName -eq 'RawData') {
[System.Security.Cryptography.X509Certificates.X509Certificate2]::new($RawData, $Password)
} else {
[System.Security.Cryptography.X509Certificates.X509Certificate2]::new($FilePath, $FilePassword)
}
}
12 changes: 10 additions & 2 deletions PowerShellBuild/Public/Get-PSBuildCertificate.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,10 @@
.PARAMETER CertificateEnvVar
Name of the environment variable holding the Base64-encoded PFX certificate.
Used by the EnvVar source and by Auto as the presence-detection key.
Default: SIGNCERTIFICATE.

Check warning on line 45 in PowerShellBuild/Public/Get-PSBuildCertificate.ps1

View workflow job for this annotation

GitHub Actions / CI / Run Linters

Unknown word (SIGNCERTIFICATE)
.PARAMETER CertificatePasswordEnvVar
Name of the environment variable holding the PFX password. Used by EnvVar source.
Default: CERTIFICATEPASSWORD.

Check warning on line 48 in PowerShellBuild/Public/Get-PSBuildCertificate.ps1

View workflow job for this annotation

GitHub Actions / CI / Run Linters

Unknown word (CERTIFICATEPASSWORD)
.PARAMETER PfxFilePath
File system path to a PFX/P12 certificate file. Required when CertificateSource is PfxFile.
.PARAMETER PfxFilePassword
Expand All @@ -66,7 +66,7 @@
.EXAMPLE
PS> $cert = Get-PSBuildCertificate

Resolve automatically: use the SIGNCERTIFICATE env var when present, otherwise search

Check warning on line 69 in PowerShellBuild/Public/Get-PSBuildCertificate.ps1

View workflow job for this annotation

GitHub Actions / CI / Run Linters

Unknown word (SIGNCERTIFICATE)
the current user's certificate store.
.EXAMPLE
PS> $cert = Get-PSBuildCertificate -CertificateSource Store
Expand All @@ -83,7 +83,7 @@
Decode a PFX certificate stored in a CI/CD secret environment variable.
.EXAMPLE
PS> $pass = Read-Host -Prompt 'Certificate password' -AsSecureString
PS> $cert = Get-PSBuildCertificate -CertificateSource PfxFile -PfxFilePath './codesign.pfx' -PfxFilePassword $pass

Check warning on line 86 in PowerShellBuild/Public/Get-PSBuildCertificate.ps1

View workflow job for this annotation

GitHub Actions / CI / Run Linters

Unknown word (codesign) Suggestions: (codein, cosign, consign, cohesion, codebig)

Load a code-signing certificate from a PFX file on disk.
#>
Expand All @@ -102,9 +102,9 @@

[string]$Thumbprint,

[string]$CertificateEnvVar = 'SIGNCERTIFICATE',

Check warning on line 105 in PowerShellBuild/Public/Get-PSBuildCertificate.ps1

View workflow job for this annotation

GitHub Actions / CI / Run Linters

Unknown word (SIGNCERTIFICATE)

[string]$CertificatePasswordEnvVar = 'CERTIFICATEPASSWORD',

Check warning on line 107 in PowerShellBuild/Public/Get-PSBuildCertificate.ps1

View workflow job for this annotation

GitHub Actions / CI / Run Linters

Unknown word (CERTIFICATEPASSWORD)

[string]$PfxFilePath,

Expand Down Expand Up @@ -215,11 +215,19 @@
throw "Environment variable '$CertificateEnvVar' does not contain a valid Base64-encoded PFX value."
}
$password = [System.Environment]::GetEnvironmentVariable($CertificatePasswordEnvVar)
$cert = [System.Security.Cryptography.X509Certificates.X509Certificate2]::new($buffer, $password)

# GetEnvironmentVariable returns $null when the variable is not set, and to the PFX
# loader no password at all is not the same thing as an empty one, so the parameter
# is supplied only when the variable held something.
$certificateParameter = @{ RawData = $buffer }
if ($null -ne $password) {
$certificateParameter['Password'] = $password
}
$cert = Import-PSBuildX509Certificate @certificateParameter
Write-Verbose ($LocalizedData.CertificateResolvedFromEnvVar -f $CertificateEnvVar)
}
'PfxFile' {
$cert = [System.Security.Cryptography.X509Certificates.X509Certificate2]::new($PfxFilePath, $PfxFilePassword)
$cert = Import-PSBuildX509Certificate -FilePath $PfxFilePath -FilePassword $PfxFilePassword
Write-Verbose ($LocalizedData.CertificateResolvedFromPfxFile -f $PfxFilePath)
}
}
Expand Down
Loading
Loading