PoshCode Logo PowerShell Code Repository

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

Authenticode 2.1 for PowerShell 2.0: Added Start-AutoSign function :) see my blog post

Don’t forget to put your default cert thumbprint or path in a ModuleInfo file; it will work without that, but it will have to prompt you for a certificate.

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

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