Skip to content

Instantly share code, notes, and snippets.

@JPRuskin
Created June 17, 2025 15:46
Show Gist options
  • Save JPRuskin/08690b5024d8f98b52b9a5cc8ef8cec3 to your computer and use it in GitHub Desktop.
Save JPRuskin/08690b5024d8f98b52b9a5cc8ef8cec3 to your computer and use it in GitHub Desktop.
Functions used to validate a Chocolatey license.
function Test-LicenseSignature {
<#
.Synopsis
Verifies a license is valid for usage, e.g. hasn't been mangled by a filter.
.Description
Loads the XML file and checks the signature without a private key.
.Link
https://learn.microsoft.com/en-us/dotnet/standard/security/how-to-verify-the-digital-signatures-of-xml-documents
.Example
Test-LicenseSignature -Path ~\Downloads\chocolatey.license.xml.txt
#>
[CmdletBinding(DefaultParameterSetName = "Path")]
param(
# The path to a Chocolatey license file
[Parameter(Mandatory, ParameterSetName = "Path", ValueFromPipelineByPropertyName, ValueFromPipeline)]
[ValidateScript({
if (-not (Test-Path $_ -PathType Leaf)) {throw "'$_' is not a file"}
if (-not ((Get-Content $_) -as [xml])) {throw "'$_' is not a valid XML file"}
$true
})]
[ArgumentCompleter({
if (Test-Path $env:ChocolateyInstall\license\chocolatey.license.xml) {
Convert-Path $env:ChocolateyInstall\license\chocolatey.license.xml
}
(Get-ChildItem -Filter "chocolatey.license.*").FullName
})]
[Alias("PSPath")]
[string]$Path,
# An XML document, hopefully a Chocolatey license
[Parameter(Mandatory, ParameterSetName = "Doc")]
[ArgumentCompleter({
Get-Variable | Where-Object {
$_.Value.PSObject.TypeNames -contains 'System.Xml.XmlDocument' -and
$_.Value.license
} | ForEach-Object {
"`$$($_.Name)"
}
})]
[Xml]$XmlDoc
)
begin {
# Load the correct assemblies, if in PowerShell 5 (or they're not available yet)
if (-not ("System.Security.Cryptography.Xml.SignedXml" -as [type])) {
Add-Type -AssemblyName System.Security
}
# Create an RSA Key definition to validate against the public key defined at
# https://github.com/chocolatey/choco/blob/develop/src/chocolatey/infrastructure/licensing/LicenseValidation.cs#L28
$RsaKey = [System.Security.Cryptography.RSACryptoServiceProvider]::new()
$RsaKey.FromXmlString(@'
<RSAKeyValue>
<Modulus>rznyhs3OslLqL7A7qav9bSHYGQmgWVsP/L47dWU7yF3EHsiYZuJNLlq8tQkPql/LB1FfLihiGsOKKUF1tmxihcRUrDaYkK1IYY3A+uJWkBglDUOUjnoDboI1FgF3wmXSb07JC8JCVYWjchq+h6MV9aDZaigA5MqMKNj9FE14f68=</Modulus>
<Exponent>AQAB</Exponent>
</RSAKeyValue>
'@)
}
process {
# Load the Xml Document (Chocolatey License)
if ($PSCmdlet.ParameterSetName -eq 'Path') {
$XmlDoc = [xml](Get-Content $Path)
}
# Load the Xml document as SignedXml
$SignedXml = [System.Security.Cryptography.Xml.SignedXml]::new($XmlDoc)
# Check all signatures in the document
$Results = foreach ($Signature in $XmlDoc.GetElementsByTagName("Signature")) {
$SignedXml.LoadXml($Signature)
$SignedXml.CheckSignature($RsaKey)
}
# Return overall success per file
$false -notin $Results
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment