PoshCode Logo PowerShell Code Repository

Get/Set Signature 2.0 by Joel Bennett 25 months ago (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/1580"></script>download | new post

Authenticode 2.2 Improved automatic cert picking, and serializing the selected cert as the default cert so you don’t have to pick it each time.

Includes the Start-AutoSign function (see my blog post)

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, and default to using timestamping!

After installing and importing for the first time, just run Get-AuthenticodeCertificate to initialize the default certificate.

  1. #Requires -version 2.0
  2. ## Authenticode.psm1 updated for PowerShell 2.0 (with time stamping)
  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-AuthenticodeSigned -Mine -Broken | Set-AuthenticodeSignature
  12. ##    Re-sign anything you signed before that has changed
  13. ##
  14. ## ls | Select-AuthenticodeSigned -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. ## 2.2 - Added sorting and filtering the displayed certs, and the option to save your choice
  20. ## 2.1 - Added some extra exports and aliases, and included my Start-AutoSign script...
  21. ## 2.0 - Updated to work with PowerShell 2.0 RTM and add -TimeStampUrl support
  22. ## 1.7 - Modified the reading of certs to better support people who only have one :)
  23. ## 1.6 - Converted to work with CTP 3, and added function help comments
  24. ## 1.5 - Moved the default certificate setting into the module info Authenticode.psd1 file
  25. ##       Note: If you get this off PoshCode, you'll have to create it yourself, see below:
  26. ## 1.4 - Moved the default certificate setting into an external psd1 file.
  27. ## 1.3 - Fixed some bugs in If-Signed and renamed it to Select-AuthenticodeSigned
  28. ##     - Added -MineOnly and -NotMineOnly switches to Select-AuthenticodeSigned
  29. ## 1.2 - Added a hack workaround to make it appear as though we can sign and check PSM1 files
  30. ##       It's important to remember that the signatures are NOT checked by PowerShell yet...
  31. ## 1.1 - Added a filter "If-Signed" that can be used like: ls | If-Signed
  32. ##     - With optional switches: ValidOnly, InvalidOnly, BrokenOnly, TrustedOnly, UnsignedOnly
  33. ##     - commented out the default Certificate which won't work for "you"
  34. ## 1.0 - first working version, includes wrappers for Get and Set
  35. ##
  36. ####################################################################################################
  37. ####################################################################################################
  38. function Get-UserCertificate {
  39. <#.SYNOPSIS
  40.  Gets the user's default signing certificate so we don't have to ask them over and over...
  41. .DESCRIPTION
  42.  The Get-UserCertificate function retrieves and returns a certificate from the user. It also stores the certificate so it can be reused without re-querying for the location and/or password ...
  43. .RETURNVALUE
  44.  An X509Certificate2 suitable for code-signing
  45. #>
  46. [CmdletBinding()]
  47. Param()
  48.    Write-Debug "PrivateData: $($ExecutionContext.SessionState.Module | fl * | Out-String)"
  49.    $UserCertificate = Get-AuthenticodeCertificate $ExecutionContext.SessionState.Module.PrivateData
  50.    $ExecutionContext.SessionState.Module.PrivateData = $UserCertificate.Thumbprint
  51.    return $UserCertificate
  52. }
  53.  
  54. function Get-AuthenticodeCertificate {
  55. [CmdletBinding()]
  56. PARAM (
  57.    $Name = $ExecutionContext.SessionState.Module.PrivateData
  58. )
  59.  
  60. END {
  61.    trap {
  62.       Write-Host "The authenticode module requires a code-signing certificate, and can't find yours!"
  63.       Write-Host
  64.       Write-Host "If this is the first time you've seen this error, please run Get-AuthenticodeCertificate by hand and specify the full path to your PFX file, or the Thumbprint of a cert in your OS Cert store -- and then answer YES to save that cert in the 'PrivateData' of the Authenticode Module metadata."
  65.       Write-Host
  66.       Write-Host "If you have seen this error multiple times, you may need to manually create a module manifest for this module with the path to your cert, and/or specify the certificate name each time you use it."
  67.       Write-Error $_
  68.       return      
  69.    }
  70.    # Import-LocalizedData -bindingVariable CertificatePath -EA "STOP"
  71.    if(!$Name) {
  72.       $certs = Get-ChildItem Cert:\ -Recurse -CodeSign | Sort NotAfter
  73.       if($certs.Count) {
  74.          Write-Host "You have $($certs.Count) certs in your local cert storage which you can specify by Thumbprint:" -fore cyan
  75.          $certs | Out-Host
  76.       }
  77.       $Name = $(Read-Host "Please specify a user certificate (wildcards allowed)")
  78.    }
  79.    if($Name -isnot [System.Security.Cryptography.X509Certificates.X509Certificate2]) {
  80.       Write-Debug "CertificatePath: $Name"
  81.       $ResolvedPath = $Null
  82.       $ResolvedPath = Resolve-Path $Name -ErrorAction "SilentlyContinue"
  83.       if(!$ResolvedPath -or !(Test-Path $ResolvedPath -ErrorAction "SilentlyContinue")) {
  84.          Write-Debug "Not a full path: $ResolvedPath"
  85.          $ResolvedPath = Resolve-Path (Join-Path $PsScriptRoot $Name -ErrorAction "SilentlyContinue") -ErrorAction "SilentlyContinue"
  86.       }
  87.       if(!$ResolvedPath -or !(Test-Path $ResolvedPath -ErrorAction "SilentlyContinue")) {
  88.          Write-Debug "Not a file Path: $ResolvedPath"
  89.          $ResolvedPath = Get-ChildItem Cert:\ -Recurse -CodeSign | Where {$_.ThumbPrint -like $Name } | Select -Expand PSPath
  90.          Write-Debug "ResolvedPath: $ResolvedPath"
  91.       }
  92.      
  93.       if(@($ResolvedPath).Count -gt 1) {
  94.          throw "You need to specify enough of the name to narrow it to a single certificate"
  95.       }
  96.  
  97.       $Certificate = get-item $ResolvedPath -ErrorAction "SilentlyContinue"
  98.       if($Certificate -is [System.IO.FileInfo]) {
  99.          $Certificate = Get-PfxCertificate $Certificate -ErrorAction "SilentlyContinue"
  100.       }
  101.       Write-Verbose "Certificate: $($Certificate | Out-String)"
  102.       $Name = $Certificate
  103.    }
  104.    
  105.    if(!$ExecutionContext.SessionState.Module.PrivateData -and $Name) {
  106.       if($Host.UI -and $Host.UI.PromptForChoice -and (0 -eq
  107.          $Host.UI.PromptForChoice("Keep this certificate for future sessions?", $Name,
  108.          [Management.Automation.Host.ChoiceDescription[]]@("&Yes","&No"), 0))
  109.       ) {
  110.          $mVersion = $PSCmdlet.MyInvocation.MyCommand.Module.Version
  111.          if(!$MVersion) { $MVersion = 2.2 }
  112.          
  113.          New-ModuleManifest $PSScriptRoot\Authenticode.psd1                         `
  114.                     -ModuleToProcess $PSScriptRoot\Authenticode.psm1                `
  115.                     -Author 'Joel Bennett' -Company 'HuddledMasses.org'             `
  116.                     -ModuleVersion  $MVersion                                       `
  117.                     -PowerShellVersion '2.0'                                        `
  118.                     -Copyright '(c) 2008-2010, Joel Bennett'                        `
  119.                     -Desc 'Function wrappers for Authenticode Signing cmdlets'      `
  120.                     -Types @() -Formats @() -RequiredModules @()                    `
  121.                     -RequiredAssemblies @() -FileList @() -NestedModules @()        `
  122.                     -PrivateData $Name.Thumbprint
  123.          $null = Sign $PSScriptRoot\Authenticode.psd1 -Cert $Name
  124.       }
  125.  
  126.       $ExecutionContext.SessionState.Module.PrivateData = $Name
  127.    }
  128.  
  129.    return $Name
  130. }
  131. }
  132.  
  133. ####################################################################################################
  134. function Test-AuthenticodeSignature {
  135. <#.SYNOPSIS
  136.  Tests a script signature to see if it is valid, or at least unaltered.
  137. .DESCRIPTION
  138.  The Test-AuthenticodeSignature function processes the output of Get-AuthenticodeSignature to determine if it
  139.  is Valid, OR **unaltered** and signed by the user's certificate
  140. .NOTES
  141.  Test-AuthenticodeSignature returns TRUE even if the root CA certificate can't be verified, as long as the signing certificate's thumbnail matches the one specified by Get-UserCertificate.
  142. .EXAMPLE
  143.    ls *.ps1 | Get-AuthenticodeSignature | Where {Test-AuthenticodeSignature $_}
  144.  To get the signature reports for all the scripts that we consider safely signed.
  145. .EXAMPLE
  146.    ls | ? { gas $_ | Test-AuthenticodeSignature }
  147.  List all the valid signed scripts (or scripts signed by our cert)
  148. .INPUTTYPE
  149.   System.Management.Automation.Signature
  150. .PARAMETER Signature
  151.   Specifies the signature object to test. This should be the output of Get-AuthenticodeSignature.
  152. .PARAMETER ForceValid
  153.   Switch parameter, forces the signature to be valid -- otherwise, even if the certificate chain can't be verified, we will accept the cert which matches the "user" certificate (see Get-UserCertificate).
  154.   Aliases                      Valid
  155. .RETURNVALUE
  156.    Boolean value representing whether the script's signature is valid, or YOUR certificate
  157. ##################################################################################################
  158. #>
  159. [CmdletBinding()]
  160. PARAM (
  161.    [Parameter(Position=0, Mandatory=$true, ValueFromPipeline=$true)]
  162. #  The signature to test.
  163.    $Signature
  164. ,
  165.    [Alias("Valid")]
  166.    [Switch]$ForceValid
  167. )
  168.  
  169. return ( $Signature.Status -eq "Valid" -or
  170.       ( !$ForceValid -and
  171.          ($Signature.Status -eq "UnknownError") -and
  172.          ($_.SignerCertificate.Thumbprint -eq $(Get-UserCertificate).Thumbprint)
  173.       ) )
  174. }
  175.  
  176. ####################################################################################################
  177. function Set-AuthenticodeSignature {
  178. <#.SYNOPSIS
  179.    Adds an Authenticode signature to a Windows PowerShell script or other file.
  180. .DESCRIPTION
  181.    The Set-AuthenticodeSignature function adds an Authenticode signature to any file that supports Subject Interface Package (SIP).
  182.  
  183.    In a Windows PowerShell script file, the signature takes the form of a block of text that indicates the end of the instructions that are executed in the script. If there is a signature  in the file when this cmdlet runs, that signature is removed.
  184. .NOTES
  185.    After the certificate has been validated, but before a signature is added to the file, the function checks the value of the $SigningApproved preference variable. If this variable is not set, or has a value other than TRUE, you are prompted to confirm the signing of the script.
  186.  
  187.    When specifying multiple values for a parameter, use commas to separate the values. For example, "<parameter-name> <value1>, <value2>".
  188. .EXAMPLE
  189.    ls *.ps1 | Set-AuthenticodeSignature -Certificate $Certificate
  190.    
  191.    To sign all of the files with the specified certificate
  192. .EXAMPLE
  193.    ls *.ps1,*.psm1,*.psd1 | Get-AuthenticodeSignature | Where {!(Test-AuthenticodeSignature $_ -Valid)} | gci | Set-AuthenticodeSignature
  194.  
  195.    List all the script files, and get and test their signatures, and then sign all of the ones that are not valid, using the user's default certificate.
  196. .INPUTTYPE
  197.    String. You can pipe a file path to Set-AuthenticodeSignature.
  198. .PARAMETER FilePath
  199.    Specifies the path to a file that is being signed.
  200.    Aliases                      Path, FullName
  201. .PARAMETER Certificate
  202.    Specifies the certificate that will be used to sign the script or file. Enter a variable that stores an object representing the certificate or an expression that gets the certificate.
  203.  
  204.    To find a certificate, use Get-PfxCertificate or use the Get-ChildItem cmdlet in the Certificate (Cert:) drive. If the certificate is not valid or does not have code-signing authority, the command fails.
  205. .PARAMETER Force
  206.    Allows the cmdlet to append a signature to a read-only file. Even using the Force parameter, the cmdlet cannot override security restrictions.
  207. .Parameter HashAlgorithm
  208.    Specifies the hashing algorithm that Windows uses to compute the digital signature for the file. The default is SHA1, which is the Windows default hashing algorithm.
  209.  
  210.    Files that are signed with a different hashing algorithm might not be recognized on other systems.
  211. .PARAMETER IncludeChain
  212.    Determines which certificates in the certificate trust chain are included in the digital signature. "NotRoot" is the default.
  213.  
  214.    Valid values are:
  215.  
  216.    -- Signer: Includes only the signer's certificate.
  217.  
  218.    -- NotRoot: Includes all of the certificates in the certificate chain, except for the root authority.
  219.  
  220.    --All: Includes all the certificates in the certificate chain.
  221.  
  222. .PARAMETER TimestampServer
  223.    Uses the specified time stamp server to add a time stamp to the signature. Type the URL of the time stamp server as a string.
  224.    Defaults to Verisign's server: http://timestamp.verisign.com/scripts/timstamp.dll
  225.  
  226.    The time stamp represents the exact time that the certificate was added to the file. A time stamp prevents the script from failing if the certificate expires because users and programs can verify that the certificate was valid atthe time of signing.
  227. .RETURNVALUE
  228.    System.Management.Automation.Signature
  229. ###################################################################################################>
  230. [CmdletBinding()]
  231. PARAM (
  232.    [Parameter(Position=1, Mandatory=$true, ValueFromPipelineByPropertyName=$true, ValueFromPipeline=$true)]
  233.    [Alias("FullName","Path")]
  234.    [ValidateScript({
  235.       if((resolve-path $_).Provider.Name -ne "FileSystem") {
  236.          throw "Specified Path is not in the FileSystem: '$_'"
  237.       }
  238.       return $true
  239.    })]
  240.    [string[]]
  241.    $FilePath
  242. ,  
  243.    [Parameter(Position=2, Mandatory=$false)]
  244.    $Certificate = $(Get-UserCertificate)
  245. ,
  246.    [Switch]$Force
  247. ,
  248.    [ValidateSet("SHA","MD5","SHA1","SHA256","SHA384","SHA512")]
  249.    [String]$HashAlgorithm #="SHA1"
  250. ,
  251.    [ValidateSet("Signer","NotRoot","All")]
  252.    [String]$IncludeChain #="NotRoot"
  253. ,
  254.    [String]$TimestampServer = "http://timestamp.verisign.com/scripts/timstamp.dll"
  255. )
  256. BEGIN {
  257.    if($Certificate -isnot [System.Security.Cryptography.X509Certificates.X509Certificate2]) {
  258.       $Certificate = Get-AuthenticodeCertificate $Certificate
  259.    }
  260.    $PSBoundParameters["Certificate"] = $Certificate
  261. }
  262. PROCESS {
  263.    Write-Verbose "Set Authenticode Signature on $FilePath with $($Certificate | Out-String)"
  264.    $PSBoundParameters["FilePath"] = $FilePath = $(Resolve-Path $FilePath)
  265.    if(Test-Path $FilePath -Type Leaf) {
  266.       Microsoft.PowerShell.Security\Set-AuthenticodeSignature @PSBoundParameters
  267.    } else {
  268.       Write-Warning "Cannot sign folders: '$FilePath'"
  269.    }
  270. }
  271. }
  272.  
  273. ####################################################################################################
  274. function Get-AuthenticodeSignature {
  275. <#.SYNOPSIS
  276.    Gets information about the Authenticode signature in a file.
  277. .DESCRIPTION
  278.    The Get-AuthenticodeSignature function gets information about the Authenticode signature in a file. If the file is not signed, the information is retrieved, but the fields are blank.
  279. .NOTES
  280.    For information about Authenticode signatures in Windows PowerShell, type "get-help About_Signing".
  281.  
  282.    When specifying multiple values for a parameter, use commas to separate the values. For example, "-<parameter-name> <value1>, <value2>".
  283. .EXAMPLE
  284.    Get-AuthenticodeSignature script.ps1
  285.    
  286.    To get the signature information about the script.ps1 script file.
  287. .EXAMPLE
  288.    ls *.ps1,*.psm1,*.psd1 | Get-AuthenticodeSignature
  289.    
  290.    Get the signature information for all the script and data files
  291. .EXAMPLE
  292.    ls *.ps1,*.psm1,*.psd1 | Get-AuthenticodeSignature | Where {!(Test-AuthenticodeSignature $_ -Valid)} | gci | Set-AuthenticodeSignature
  293.  
  294.    This command gets information about the Authenticode signature in all of the script and module files, and tests the signatures, then signs all of the ones that are not valid.
  295. .INPUTTYPE
  296.    String. You can pipe the path to a file to Get-AuthenticodeSignature.
  297. .PARAMETER FilePath
  298.    The path to the file being examined. Wildcards are permitted, but they must lead to a single file. The parameter name ("-FilePath") is optional.
  299.    Aliases                      Path, FullName
  300. .RETURNVALUE
  301.    System.Management.Automation.Signature
  302. ###################################################################################################>
  303. [CmdletBinding()]
  304. PARAM (
  305.    [Parameter(Position=1, Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
  306.    [Alias("FullName","Path")]
  307.    [ValidateScript({
  308.       if((resolve-path $_).Provider.Name -ne "FileSystem") {
  309.          throw "Specified Path is not in the FileSystem: '$_'"
  310.       }
  311.       if(!(Test-Path -PathType Leaf $_)) {
  312.          throw "Specified Path is not a File: '$_'"
  313.       }
  314.       return $true
  315.    })]
  316.    [string[]]
  317.    $FilePath
  318. )
  319.  
  320. PROCESS {
  321.    Microsoft.PowerShell.Security\Get-AuthenticodeSignature -FilePath $FilePath
  322. }
  323. }
  324.  
  325. ####################################################################################################
  326. function Select-AuthenticodeSigned {
  327. <#.SYNOPSIS
  328.    Select files based on the status of their Authenticode Signature.
  329. .DESCRIPTION
  330.    The Select-AuthenticodeSigned function filters files on the pipeline based on the state of their authenticode signature.
  331. .NOTES
  332.    For information about Authenticode signatures in Windows PowerShell, type "get-help About_Signing".
  333.  
  334.    When specifying multiple values for a parameter, use commas to separate the values. For example, "-<parameter-name> <value1>, <value2>".
  335. .EXAMPLE
  336.    ls *.ps1,*.ps[dm]1 | Select-AuthenticodeSigned
  337.    
  338.    To get the signature information about the script.ps1 script file.
  339. .EXAMPLE
  340.    ls *.ps1,*.psm1,*.psd1 | Get-AuthenticodeSignature
  341.    
  342.    Get the signature information for all the script and data files
  343. .EXAMPLE
  344.    ls *.ps1,*.psm1,*.psd1 | Get-AuthenticodeSignature | Where {!(Test-AuthenticodeSignature $_ -Valid)} | gci | Set-AuthenticodeSignature
  345.  
  346.    This command gets information about the Authenticode signature in all of the script and module files, and tests the signatures, then signs all of the ones that are not valid.
  347. .INPUTTYPE
  348.    String. You can pipe the path to a file to Get-AuthenticodeSignature.
  349. .PARAMETER FilePath
  350.    The path to the file being examined. Wildcards are permitted, but they must lead to a single file. The parameter name ("-FilePath") is optional.
  351.    Aliases                      Path, FullName
  352. .RETURNVALUE
  353.    System.Management.Automation.Signature
  354. ###################################################################################################>
  355. [CmdletBinding()]
  356. PARAM (
  357.    [Parameter(Position=1, Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
  358.    [Alias("FullName")]
  359.    [ValidateScript({
  360.       if((resolve-path $_).Provider.Name -ne "FileSystem") {
  361.          throw "Specified Path is not in the FileSystem: '$_'"
  362.       }
  363.       return $true
  364.    })]
  365.    [string[]]
  366.    $FilePath
  367. ,
  368.    [Parameter()]
  369.    # Return only files that are signed with the users' certificate (as returned by Get-UserCertificate).
  370.    [switch]$MineOnly
  371. ,
  372.    [Parameter()]
  373.    # Return only files that are NOT signed with the users' certificate (as returned by Get-UserCertificate).
  374.    [switch]$NotMineOnly
  375. ,
  376.    [Parameter()]
  377.    [Alias("HashMismatch")]
  378.    # Return only files with signatures that are broken (where the file has been edited, and the hash doesn't match).
  379.    [switch]$BrokenOnly
  380. ,
  381.    [Parameter()]
  382.    # Returns the files that are Valid OR signed with the users' certificate (as returned by Get-UserCertificate).
  383.    #
  384.    # That is, TrustedOnly returns files returned by -ValidOnly OR -MineOnly (if you specify both parameters, you get only files that are BOTH -ValidOnly AND -MineOnly)
  385.    [switch]$TrustedOnly
  386. ,
  387.    [Parameter()]
  388.    # Return only files that are "Valid": This means signed with any cert where the certificate chain is verifiable to a trusted root certificate.  This may or may not include files signed with the user's certificate.
  389.    [switch]$ValidOnly
  390. ,
  391.    [Parameter()]
  392.    # Return only files that doesn't have a "Valid" signature, which includes files that aren't signed, or that have a hash mismatch, or are signed by untrusted certs (possibly including the user's certificate).
  393.    [switch]$InvalidOnly
  394. ,
  395.    [Parameter()]
  396.    # Return only signable files that aren't signed at all. That is, only files that support Subject Interface Package (SIP) but aren't signed.
  397.    [switch]$UnsignedOnly
  398.  
  399. )
  400. PROCESS {
  401.    if(!(Test-Path -PathType Leaf $FilePath)) {
  402.       # if($ErrorAction -ne "SilentlyContinue") {
  403.       #    Write-Error "Specified Path is not a File: '$FilePath'"
  404.       # }
  405.    } else {
  406.  
  407.       foreach($sig in Get-AuthenticodeSignature -FilePath $FilePath) {
  408.      
  409.       # Broken only returns ONLY things which are HashMismatch
  410.       if($BrokenOnly   -and $sig.Status -ne "HashMismatch")
  411.       {
  412.          Write-Debug "$($sig.Status) - Not Broken: $FilePath"
  413.          return
  414.       }
  415.      
  416.       # Trusted only returns ONLY things which are Valid
  417.       if($ValidOnly    -and $sig.Status -ne "Valid")
  418.       {
  419.          Write-Debug "$($sig.Status) - Not Trusted: $FilePath"
  420.          return
  421.       }
  422.      
  423.       # AllValid returns only things that are SIGNED and not HashMismatch
  424.       if($TrustedOnly  -and (($sig.Status -ne "HashMismatch") -or !$sig.SignerCertificate) )
  425.       {
  426.          Write-Debug "$($sig.Status) - Not Valid: $FilePath"
  427.          return
  428.       }
  429.      
  430.       # InvalidOnly returns things that are Either NotSigned OR HashMismatch ...
  431.       if($InvalidOnly  -and ($sig.Status -eq "Valid"))
  432.       {
  433.          Write-Debug "$($sig.Status) - Valid: $FilePath"
  434.          return
  435.       }
  436.      
  437.       # Unsigned returns only things that aren't signed
  438.       # NOTE: we don't test using NotSigned, because that's only set for .ps1 or .exe files??
  439.       if($UnsignedOnly -and $sig.SignerCertificate )
  440.       {
  441.          Write-Debug "$($sig.Status) - Signed: $FilePath"
  442.          return
  443.       }
  444.      
  445.       # Mine returns only things that were signed by MY CertificateThumbprint
  446.       if($MineOnly     -and (!($sig.SignerCertificate) -or ($sig.SignerCertificate.Thumbprint -ne $((Get-UserCertificate).Thumbprint))))
  447.       {
  448.          Write-Debug "Originally signed by someone else, thumbprint: $($sig.SignerCertificate.Thumbprint)"
  449.          Write-Debug "Does not match your default certificate print: $((Get-UserCertificate).Thumbprint)"
  450.          Write-Debug "     $FilePath"
  451.          return
  452.       }
  453.  
  454.       # NotMine returns only things that were NOT signed by MY CertificateThumbprint
  455.       if($NotMineOnly  -and (!($sig.SignerCertificate) -or ($sig.SignerCertificate.Thumbprint -eq $((Get-UserCertificate).Thumbprint))))
  456.       {
  457.          if($sig.SignerCertificate) {
  458.             Write-Debug "Originally signed by you, thumbprint: $($sig.SignerCertificate.Thumbprint)"
  459.             Write-Debug "Matches your default certificate print: $((Get-UserCertificate).Thumbprint)"
  460.             Write-Debug "     $FilePath"
  461.          }
  462.          return
  463.       }
  464.      
  465.       if(!$BrokenOnly  -and !$TrustedOnly -and !$ValidOnly -and !$InvalidOnly -and !$UnsignedOnly -and !($sig.SignerCertificate) )
  466.       {
  467.          Write-Debug "$($sig.Status) - Not Signed: $FilePath"
  468.          return
  469.       }
  470.      
  471.       get-childItem $sig.Path
  472.    }}
  473. }
  474. }
  475.  
  476.  
  477. function Start-AutoSign {
  478. # .Synopsis
  479. #     Start a FileSystemWatcher to automatically sign scripts when you save them
  480. # .Description
  481. #     Create a FileSystemWatcher with a scriptblock that uses the Authenticode script Module to sign anything that changes
  482. # .Parameter Path
  483. #     The path to the folder you want to monitor
  484. # .Parameter Filter
  485. #     A filter to select only certain files: by default, *.ps*  (because we can only sign .ps1, .psm1, .psd1, and .ps1xml
  486. # .Parameter Recurse
  487. #     Whether we should also watch autosign files in subdirectories
  488. # .Parameter CertPath
  489. #     The path or name of a certain certificate, to override the defaults from the Authenticode Module
  490. # .Parameter NoNotify
  491. #     Whether wo should avoid using Growl to notify the user each time we sign something.
  492. # .NOTE
  493. #     Don't run this on a location where you're going to be generating dozens or hundreds of files ;)
  494. PARAM($Path=".", $Filter= "*.ps*", [Switch]$Recurse, $CertPath, [Switch]$NoNotify)
  495.  
  496. if(!$NoNotify -and (Get-Module Growl -ListAvailable -ErrorAction 0)) {
  497.    Import-Module Growl
  498.    Register-GrowlType AutoSign "Signing File" -ErrorAction 0
  499. } else { $NoNotify = $false }
  500.  
  501. $realItem = Get-Item $Path -ErrorAction Stop
  502. if (-not $realItem) { return }
  503.  
  504. $Action = {
  505.    ## Files that can't be signed show up as "UnknownError" with this message:
  506.    $InvalidForm = "The form specified for the subject is not one supported or known by the specified trust provider"
  507.    ## Files that are signed with a cert we don't trust also show up as UnknownError, but with different messages:
  508.    # $UntrustedCert  = "A certificate chain could not be built to a trusted root authority"
  509.    # $InvalidCert = "A certificate chain processed, but terminated in a root certificate which is not trusted by the trust provider"
  510.    # $ExpiredCert = "A required certificate is not within its validity period when verifying against the current system clock or the timestamp in the signed file"
  511.    
  512.    ForEach($file in Get-ChildItem $eventArgs.FullPath | Get-AuthenticodeSignature |
  513.       Where-Object { $_.Status -ne "Valid" -and $_.StatusMessage -ne $invalidForm } |
  514.       Select-Object -ExpandProperty Path )
  515.    {
  516.       if(!$NoNotify) {
  517.          Send-Growl AutoSign "Signing File" "File $($eventArgs.ChangeType), signing:" "$file"
  518.       }
  519.       if($CertPath) {
  520.          Set-AuthenticodeSignature -FilePath $file -Certificate $CertPath
  521.       } else {
  522.          Set-AuthenticodeSignature -FilePath $file
  523.       }
  524.    }
  525. }
  526. $watcher = New-Object IO.FileSystemWatcher $realItem.Fullname, $filter -Property @{ IncludeSubdirectories = $Recurse }
  527. Register-ObjectEvent $watcher "Created" "AutoSignCreated$($realItem.Fullname)" -Action $Action > $null
  528. Register-ObjectEvent $watcher "Changed" "AutoSignChanged$($realItem.Fullname)" -Action $Action > $null
  529. Register-ObjectEvent $watcher "Renamed" "AutoSignChanged$($realItem.Fullname)" -Action $Action > $null
  530.  
  531. }
  532.  
  533. Set-Alias gas          Get-AuthenticodeSignature -Description "Authenticode Module Alias"
  534. Set-Alias sas          Set-AuthenticodeSignature -Description "Authenticode Module Alias"
  535. Set-Alias slas         Select-AuthenticodeSigned -Description "Authenticode Module Alias"
  536. Set-Alias sign         Set-AuthenticodeSignature -Description "Authenticode Module Alias"
  537.  
  538. Export-ModuleMember -Alias gas,sas,slas,sign -Function Set-AuthenticodeSignature, Get-AuthenticodeSignature, Test-AuthenticodeSignature, Select-AuthenticodeSigned, Get-UserCertificate, Get-AuthenticodeCertificate, Start-AutoSign
  539.  
  540. # SIG # Begin signature block
  541. # MIIIPAYJKoZIhvcNAQcCoIIILTCCCCkCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB
  542. # gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR
  543. # AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQU0tI8MdRYrPgGbk0OfxXiog9F
  544. # IJigggVaMIIFVjCCBD6gAwIBAgIQSX2g7+3+PL1oSJra3tEBIzANBgkqhkiG9w0B
  545. # AQUFADCBlTELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0
  546. # IExha2UgQ2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYD
  547. # VQQLExhodHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xHTAbBgNVBAMTFFVUTi1VU0VS
  548. # Rmlyc3QtT2JqZWN0MB4XDTA5MTAxNjAwMDAwMFoXDTEwMTAxNjIzNTk1OVowgcQx
  549. # CzAJBgNVBAYTAlVTMQ4wDAYDVQQRDAUxNDYyMzERMA8GA1UECAwITmV3IFlvcmsx
  550. # EjAQBgNVBAcMCVJvY2hlc3RlcjEUMBIGA1UECQwLTVMgMDgwMS0xN0ExGjAYBgNV
  551. # BAkMETEzNTAgSmVmZmVyc29uIFJkMRowGAYDVQQKDBFYZXJveCBDb3Jwb3JhdGlv
  552. # bjEUMBIGA1UECwwLU0VFRFUgVG9vbHMxGjAYBgNVBAMMEVhlcm94IENvcnBvcmF0
  553. # aW9uMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA+BtkDUSRsr9qshYW
  554. # 6mBLwe62RdoKaBiCZh1whKQbAqN58lmbN3fP2Qf6eheR03EyqPXq5gGQO8SpeeOn
  555. # tDmlgR47dKFfawWKLEM4H189FJ6VHKD8Hmk1+/OAv/6F45HvbXOrGmisUgKplmzl
  556. # tmot9uv04nyFlRXMPbHwKxNlWOdSSs1N9Y+q7y3kuSBm4bCqquqEFnm7gAzHt/A1
  557. # 1CkT8f/0UP0ODKj0Cs/hc+b/xOCCL06SALYOYtMdBFwluBs85p2Bv1WcdUIELprz
  558. # jnOjgoF4vk29UIUNkDZ5j2d0W0FCNd61ukm9faJMuU2/yw348sxclSoT18yOeC+Z
  559. # vKc4lQIDAQABo4IBbzCCAWswHwYDVR0jBBgwFoAU2u1kdBScFDyr3ZmpvVsoTYs8
  560. # ydgwHQYDVR0OBBYEFAu07ifjpHK32XeSDsdjIK4/R25LMA4GA1UdDwEB/wQEAwIH
  561. # gDAMBgNVHRMBAf8EAjAAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMBEGCWCGSAGG+EIB
  562. # AQQEAwIEEDBGBgNVHSAEPzA9MDsGDCsGAQQBsjEBAgEDAjArMCkGCCsGAQUFBwIB
  563. # Fh1odHRwczovL3NlY3VyZS5jb21vZG8ubmV0L0NQUzBCBgNVHR8EOzA5MDegNaAz
  564. # hjFodHRwOi8vY3JsLnVzZXJ0cnVzdC5jb20vVVROLVVTRVJGaXJzdC1PYmplY3Qu
  565. # Y3JsMDQGCCsGAQUFBwEBBCgwJjAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuY29t
  566. # b2RvY2EuY29tMCEGA1UdEQQaMBiBFkpvZWwuQmVubmV0dEBYZXJveC5jb20wDQYJ
  567. # KoZIhvcNAQEFBQADggEBAG5+It5AFsnqx1GDTuHxuExJGtpRYH6OatSs3eV507zz
  568. # 5tyKbtCros6j92rvJOUfefk38saqbk81o5vMvEyQ/lQ7tp06NV0QBdt5WtoOnUZk
  569. # u2lG4NcxiSz5vxQRF4+3mUZ77K3kRu6zY+zrauTTONmRPOdhYMMbKfZ67kePVNNv
  570. # gnueXBxhd+xj5FJUVTW0qlks6+uYMKNM+MLvRxV83WUYwpnfcQyyvVILsVdw56qp
  571. # OzOxIsQDc2flhqYVktPjL80fOXAPWzs88VXxoEkO3fvqHXi3VlWxMVV/861RORzk
  572. # 5aleuMEp/2Ue1dJG279ATD1QDCHiG11Vz34Pdptf3FYxggJMMIICSAIBATCBqjCB
  573. # lTELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug
  574. # Q2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho
  575. # dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xHTAbBgNVBAMTFFVUTi1VU0VSRmlyc3Qt
  576. # T2JqZWN0AhBJfaDv7f48vWhImtre0QEjMAkGBSsOAwIaBQCgeDAYBgorBgEEAYI3
  577. # AgEMMQowCKACgAChAoAAMBkGCSqGSIb3DQEJAzEMBgorBgEEAYI3AgEEMBwGCisG
  578. # AQQBgjcCAQsxDjAMBgorBgEEAYI3AgEVMCMGCSqGSIb3DQEJBDEWBBRfh5JdQ29J
  579. # 9Uc79ppuJEJLRp5tCTANBgkqhkiG9w0BAQEFAASCAQBGarkiFDUgoQJBLuOyfY6k
  580. # LZzx3XBcHzaon1DiGXR2llvEVdglgOUsyXhXnckBwV4nD44PYmCAXLIZze92oHN4
  581. # ytxkl/wSwhxoZ3NYG7XXVg+X7CVL7PKXBKkEYofneeFYjF2F6UlkutjiObsos4Cv
  582. # LYEMGk/K6hU5DSu2yJY9aCKMhms30nhGc1ov/wBI5b9HhQYVYrOdw9Bq3LpyDpXL
  583. # 2C0kDMrZYHudp50q1l9JufzeWDdhGm7Oc9RcdV96enBFrlDPLDuMRzXCuNavsgva
  584. # lHCK2G7L8QiF/eMrlxn1jIfXTCH11NuQOAZ+FPDZGtrpxlS/MQ3eZRX+O2FqLW8F
  585. # SIG # End signature block

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