PoshCode Logo PowerShell Code Repository

Get/Set Signature (CTP2) (modification of post by Joel Bennett view diff)
View followups from Joel Bennett | diff | embed code: <script type="text/javascript" src="http://PoshCode.org/embed/749"></script>download | new post

VERSION 1.5 for CTP3 — The default cert should be put in the ModuleInfo file.

Description: Wrappers for the Get-AuthenticodeSignature and Set-AuthenticodeSignature which properly parse paths and don’t kill your pipeline and script when you hit a folder by accident…

  1. #Requires -version 2.0
  2. ## Authenticode.psm1 updated for CTP 3
  3. ####################################################################################################
  4. ## Wrappers for the Get-AuthenticodeSignature and Set-AuthenticodeSignature cmdlets
  5. ## These properly parse paths, so they don't kill your pipeline and script if you include a folder
  6. ##
  7. ## Usage:
  8. ## ls | Get-AuthenticodeSignature
  9. ##    Get all the signatures
  10. ##
  11. ## ls | Select-Signed -Mine -Broken | Set-AuthenticodeSignature
  12. ##    Re-sign anything you signed before that has changed
  13. ##
  14. ## ls | Select-Signed -NotMine -ValidOnly | Set-AuthenticodeSignature
  15. ##    Re-sign scripts that are hash-correct but not signed by me or by someone trusted
  16. ##
  17. ####################################################################################################
  18. ## History:
  19. ## 1.5 - Moved the default certificate setting into the module info Authenticode.psd1 file
  20. ##       Note: If you get this off PoshCode, you'll have to create it yourself, see below:
  21. ## 1.4 - Moved the default certificate setting into an external psd1 file.
  22. ## 1.3 - Fixed some bugs in If-Signed and renamed it to Select-Signed
  23. ##     - Added -MineOnly and -NotMineOnly switches to Select-Signed
  24. ## 1.2 - Added a hack workaround to make it appear as though we can sign and check PSM1 files
  25. ##       It's important to remember that the signatures are NOT checked by PowerShell yet...
  26. ## 1.1 - Added a filter "If-Signed" that can be used like: ls | If-Signed
  27. ##     - With optional switches: ValidOnly, InvalidOnly, BrokenOnly, TrustedOnly, UnsignedOnly
  28. ##     - commented out the default Certificate which won't work for "you"
  29. ## 1.0 - first working version, includes wrappers for Get and Set
  30. ##
  31. ####################################################################################################
  32. ## README! README! README! README! #################################################################
  33. ## README! README! README! README! #################################################################
  34. ##
  35. ## You should set the location to your default signing certificate. The permanent way to do that is
  36. ## to modify (or create) the .psd1 file to set the PrivateData member variable. Otherwise you'll be
  37. ## prompted to provide a cert path whenever you try to sign a script without passing a certificate.
  38. ##
  39. ## The PrivateData variable should point at your code-signing certificate either with a full path
  40. ## or with the THUMBPRINT of a certificate you have available in your Cert:\CurrentUser\My\ provider
  41. ##
  42. ## EG:
  43. ##      F05F583BB5EA4C90E3B9BF1BDD0B657701245BD5
  44. ## OR:
  45. ##      "Cert:\CurrentUser\My\F05F583BB5EA4C90E3B9BF1BDD0B657701245BD5"
  46. ## OR a file name:
  47. ##      "C:\Users\Joel\Documents\WindowsPowerShell\PoshCerts\Joel-Bennett_Code-Signing.pfx"
  48. ##
  49. ## The simplest thing is to just create a new PSD1
  50. ##
  51. ##    New-ModuleManifest .\Authenticode.psd1 -Nested .\Authenticode.psm1 `
  52. ##    -Author "" -COmpany "" -Copy "" -Desc "" `
  53. ##    -Types @() -Formats @() -RequiredMod @() -RequiredAs @() -Other @() `
  54. ##    -PrivateData F05F583BB5EA4C90E3B9BF1BDD0B657701245BD5
  55. ##
  56. ####################################################################################################
  57.  
  58. function Get-UserCertificate {
  59. [CmdletBinding()]
  60. PARAM ()
  61. PROCESS {
  62.    trap {
  63.       Write-Host "The authenticode script module requires configuration to function fully!"
  64.       Write-Host
  65.       Write-Host "You must put the path to your default signing certificate in the module metadata"`
  66.                  "file before you can use the module's Set-Authenticode cmdlet or to the 'mine'"`
  67.                  "feature of the Select-Signed or Test-Signature. To set it up, you can do this:"
  68.       Write-Host
  69.       Write-Host "PrivateData = 'C:\Users\${Env:UserName}\Documents\WindowsPowerShell\PoshCerts\Code-Signing.pfx'"
  70.       Write-Host
  71.       Write-Host "If you load your certificate into your 'CurrentUser\My' store, or put the .pfx file"`
  72.                  "into the folder with the Authenthenticode module script, you can just specify it's"`
  73.                  "thumprint or filename, respectively. Otherwise, it should be a full path."
  74.       Write-Error $_
  75.       return      
  76.    }
  77.    # Import-LocalizedData -bindingVariable CertificatePath -EA "STOP"
  78.    if(!$ExecutionContext.SessionState.Module.PrivateData) {
  79.       $ExecutionContext.SessionState.Module.PrivateData = $(Read-Host "Please specify a user certificate")
  80.    }
  81.    # Write-Host "CertificatePath: $ExecutionContext.SessionState.Module.PrivateData" -fore cyan
  82.    $ResolvedPath = $Null
  83.    $ResolvedPath = Resolve-Path $ExecutionContext.SessionState.Module.PrivateData -ErrorAction "SilentlyContinue"
  84.    if(!$ResolvedPath -or !(Test-Path $ResolvedPath -ErrorAction "SilentlyContinue")) {
  85.       # Write-Host "ResolvedPath: $ResolvedPath" -fore green
  86.       $ResolvedPath = Resolve-Path (Join-Path $PsScriptRoot $ExecutionContext.SessionState.Module.PrivateData -ErrorAction "SilentlyContinue") -ErrorAction "SilentlyContinue"
  87.    }
  88.    if(!$ResolvedPath -or !(Test-Path $ResolvedPath -ErrorAction "SilentlyContinue")) {
  89.       # Write-Host "ResolvedPath: $ResolvedPath" -fore yellow
  90.       $ResolvedPath = Resolve-Path (Join-Path "Cert:\CurrentUser\My" $ExecutionContext.SessionState.Module.PrivateData -ErrorAction "SilentlyContinue") -ErrorAction "SilentlyContinue"
  91.    }
  92.  
  93.    $Certificate = get-item $ResolvedPath -ErrorAction "SilentlyContinue"
  94.    if($Certificate -is [System.IO.FileInfo]) {
  95.       $Certificate = Get-PfxCertificate $Certificate -ErrorAction "SilentlyContinue"
  96.    }
  97.    Write-Verbose "Certificate: $($Certificate | Out-String)"
  98.    return $Certificate
  99. }
  100. }
  101.  
  102. function Test-Signature {
  103. [CmdletBinding()]
  104. PARAM (
  105.    [Parameter(Position=0, Mandatory=$true, ValueFromPipeline=$true)]
  106. #  We can't actually require the type, or we won't be able to check the fake ones
  107. #   [System.Management.Automation.Signature]
  108.    $Signature
  109. ,
  110.    [Alias("Valid")]
  111.    [Switch]$ForceValid
  112. )
  113.  
  114. return ( $Signature.Status -eq "Valid" -or
  115.       ( !$ForceValid -and
  116.          ($Signature.Status -eq "UnknownError") -and
  117.          ($_.SignerCertificate.Thumbprint -eq $(Get-UserCertificate).Thumbprint)
  118.       ) )
  119. }
  120.  
  121. function Set-AuthenticodeSignature{
  122. [CmdletBinding()]
  123. PARAM (
  124.    [Parameter(Position=1, Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
  125.    [Alias("FullName")]
  126.    [ValidateScript({
  127.       if((resolve-path $_).Provider.Name -ne "FileSystem") {
  128.          throw "Specified Path is not in the FileSystem: '$_'"
  129.       }
  130.       if(!(Test-Path -PathType Leaf $_)) {
  131.          throw "Specified Path is not a File: '$_'"
  132.       }
  133.       return $true
  134.    })]
  135.    [string]
  136.    $Path
  137. ,  ## TODO: you should CHANGE THIS to a method which gets *your* default certificate
  138.    [Parameter(Position=2, Mandatory=$false)]
  139.    $Certificate = $(Get-UserCertificate)
  140. )
  141.  
  142. PROCESS {
  143.    Write-Verbose "Set Authenticode Signature on $Path with $($Certificate | Out-String)"
  144.    Microsoft.PowerShell.Security\Set-AuthenticodeSignature -Certificate $Certificate -FilePath $Path  
  145. }
  146. }
  147.  
  148. New-Alias sign Set-AuthenticodeSignature
  149.  
  150. function Get-AuthenticodeSignature {
  151. [CmdletBinding()]
  152. PARAM (
  153.    [Parameter(Position=1, Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
  154.    [Alias("FullName")]
  155.    [ValidateScript({
  156.       if((resolve-path $_).Provider.Name -ne "FileSystem") {
  157.          throw "Specified Path is not in the FileSystem: '$_'"
  158.       }
  159.       if(!(Test-Path -PathType Leaf $_)) {
  160.          throw "Specified Path is not a File: '$_'"
  161.       }
  162.       return $true
  163.    })]
  164.    [string]
  165.    $Path
  166. )
  167.  
  168. PROCESS {
  169.    Microsoft.PowerShell.Security\Get-AuthenticodeSignature -FilePath $Path
  170. }
  171. }
  172.  
  173. function Select-Signed {
  174. [CmdletBinding()]
  175. PARAM (
  176.    [Parameter(Position=1, Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
  177.    [Alias("FullName")]
  178.    [ValidateScript({
  179.       if((resolve-path $_).Provider.Name -ne "FileSystem") {
  180.          throw "Specified Path is not in the FileSystem: '$_'"
  181.       }
  182.       return $true
  183.    })]
  184.    [string]
  185.    $Path
  186. ,
  187.    [Parameter()]
  188.    [switch]$MineOnly
  189. ,
  190.    [Parameter()]
  191.    [switch]$NotMineOnly
  192. ,
  193.    [Parameter()]
  194.    [switch]$BrokenOnly
  195. ,
  196.    [Parameter()]
  197.    [switch]$TrustedOnly
  198. ,
  199.    [Parameter()]
  200.    [switch]$ValidOnly
  201. ,
  202.    [Parameter()]
  203.    [switch]$InvalidOnly
  204. ,
  205.    [Parameter()]
  206.    [switch]$UnsignedOnly
  207.  
  208. )
  209.  
  210.    if(!(Test-Path -PathType Leaf $Path)) {
  211.       # if($ErrorAction -ne "SilentlyContinue") {
  212.       #    Write-Error "Specified Path is not a File: '$Path'"
  213.       # }
  214.    } else {
  215.  
  216.       $sig = Get-AuthenticodeSignature $Path
  217.      
  218.       # Broken only returns ONLY things which are HashMismatch
  219.       if($BrokenOnly   -and $sig.Status -ne "HashMismatch")
  220.       {
  221.          Write-Debug "$($sig.Status) - Not Broken: $Path"
  222.          return
  223.       }
  224.      
  225.       # Trusted only returns ONLY things which are Valid
  226.       if($TrustedOnly  -and $sig.Status -ne "Valid")
  227.       {
  228.          Write-Debug "$($sig.Status) - Not Trusted: $Path"
  229.          return
  230.       }
  231.      
  232.       # AllValid returns only things that are SIGNED and not HashMismatch
  233.       if($ValidOnly    -and (($sig.Status -ne "HashMismatch") -or !$sig.SignerCertificate) )
  234.       {
  235.          Write-Debug "$($sig.Status) - Not Valid: $Path"
  236.          return
  237.       }
  238.      
  239.       # NOTValid returns only things that are SIGNED and not HashMismatch
  240.       if($InvalidOnly  -and ($sig.Status -eq "Valid"))
  241.       {
  242.          Write-Debug "$($sig.Status) - Valid: $Path"
  243.          return
  244.       }
  245.      
  246.       # Unsigned returns only things that aren't signed
  247.       # NOTE: we don't test using NotSigned, because that's only set for .ps1 or .exe files??
  248.       if($UnsignedOnly -and $sig.SignerCertificate )
  249.       {
  250.          Write-Debug "$($sig.Status) - Signed: $Path"
  251.          return
  252.       }
  253.      
  254.       # Mine returns only things that were signed by MY CertificateThumbprint
  255.       if($MineOnly     -and (!($sig.SignerCertificate) -or ($sig.SignerCertificate.Thumbprint -ne $((Get-UserCertificate).Thumbprint))))
  256.       {
  257.          Write-Debug "Originally signed by someone else, thumbprint: $($sig.SignerCertificate.Thumbprint)"
  258.          Write-Debug "Does not match your default certificate print: $((Get-UserCertificate).Thumbprint)"
  259.          Write-Debug "     $Path"
  260.          return
  261.       }
  262.  
  263.       # NotMine returns only things that were signed by MY CertificateThumbprint
  264.       if($NotMineOnly  -and (!($sig.SignerCertificate) -or ($sig.SignerCertificate.Thumbprint -eq $((Get-UserCertificate).Thumbprint))))
  265.       {
  266.          if($sig.SignerCertificate) {
  267.             Write-Debug "Originally signed by you, thumbprint: $($sig.SignerCertificate.Thumbprint)"
  268.             Write-Debug "Matches your default certificate print: $((Get-UserCertificate).Thumbprint)"
  269.             Write-Debug "     $Path"
  270.          }
  271.          return
  272.       }
  273.      
  274.       if(!$BrokenOnly  -and !$TrustedOnly -and !$ValidOnly -and !$InvalidOnly -and !$UnsignedOnly -and !($sig.SignerCertificate) )
  275.       {
  276.          Write-Debug "$($sig.Status) - Not Signed: $Path"
  277.          return
  278.       }
  279.      
  280.       get-childItem $sig.Path
  281.    }
  282. }
  283. Export-ModuleMember Set-AuthenticodeSignature, Get-AuthenticodeSignature, Test-Signature, Select-Signed, Get-UserCertificate

Submit a correction or amendment below (
click here to make a fresh posting)
After submitting an amendment, you'll be able to view the differences between the old and new posts easily.

Syntax highlighting:


Remember me