PoshCode Logo PowerShell Code Repository

Get/Set Signature (CTP2) by Joel Bennett 3 years 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/464"></script>download | new post

VERSION 1.4 — The default cert setting must be put in an external .PSD1 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
  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.4 - Moved the default certificate setting into an external psd1 file.
  20. ## 1.3 - Fixed some bugs in If-Signed and renamed it to Select-Signed
  21. ##     - Added -MineOnly and -NotMineOnly switches to Select-Signed
  22. ## 1.2 - Added a hack workaround to make it appear as though we can sign and check PSM1 files
  23. ##       It's important to remember that the signatures are NOT checked by PowerShell yet...
  24. ## 1.1 - Added a filter "If-Signed" that can be used like: ls | If-Signed
  25. ##     - With optional switches: ValidOnly, InvalidOnly, BrokenOnly, TrustedOnly, UnsignedOnly
  26. ##     - commented out the default Certificate which won't work for "you"
  27. ## 1.0 - first working version, includes wrappers for Get and Set
  28. ##
  29. ####################################################################################################
  30. ## README! README! README! README! #################################################################
  31. ## README! README! README! README! #################################################################
  32. ## YOU MUST set the location to your default signing certificate, by either modifiying this script,
  33. ## or by creating a language resource file! The second is easier: For english, create a subdirectory
  34. ## next to this file called 'en' (for English) and in that folder, create a 'Authenticode.psd1' file
  35. ## EG: Set-Content .\en\Authenticode.psd1 $CertificatePath
  36. ## Basically, the file should contain the path to your code-signing certificate, preferably from the
  37. ## CERT:\ PsProvider, but alternately, from a PFX file...
  38. ## EG:
  39. ##      "Cert:\CurrentUser\My\F05F583BB5EA4C90E3B9BF1BDD0B657701245BD5"
  40. ## OR:
  41. ##      "C:\Users\Joel\Documents\WindowsPowerShell\PoshCerts\Joel-Bennett_Code-Signing.pfx"
  42. ## See: http://huddledmasses.org/misusing-powershell-localizable-data/
  43. ####################################################################################################
  44.  
  45. CMDLET Get-UserCertificate -snapin Huddled.Authenticode {
  46.    trap {
  47.       Write-Host "The authenticode script module requires a configuration file to function fully!"
  48.       Write-Host
  49.       Write-Host "You must put the path to your default signing certificate in the configuration"`
  50.                  "file before you can use the module's Set-Authenticode cmdlet or to the 'mine'"`
  51.                  "feature of the Select-Signed or Test-Signature. To set it up, you can do this:"
  52.       Write-Host
  53.       Write-Host "MkDir $(Join-Path $PsScriptRoot $(Get-Culture)) |"
  54.       Write-Host "   Join-Path -Path {`$_} 'Authenticode.psd1'    |"
  55.       Write-Host "   New-Item  -Path {`$_} -Type File -Value '`"ThePathToYourCertificate`"'"
  56.       Write-Host
  57.       Write-Host "If you load your certificate into your 'CurrentUser\My' store, or put the .pfx file"`
  58.                  "into the folder with the Authenthenticode module script, you can just specify it's"`
  59.                  "thumprint or filename, respectively. Otherwise, it should be a full path."
  60.       return
  61.    }  
  62.    Import-LocalizedData -bindingVariable CertificatePath -EA "STOP"
  63.    
  64.    $ResolvedPath = $Null
  65.    $ResolvedPath = Resolve-Path $CertificatePath -ErrorAction "SilentlyContinue"
  66.    if(!$ResolvedPath) {
  67.       $ResolvedPath = Resolve-Path (Join-Path $PsScriptRoot $CertificatePath -ErrorAction "SilentlyContinue") -ErrorAction "SilentlyContinue"
  68.    }
  69.    if(!$ResolvedPath) {
  70.       $ResolvedPath = Resolve-Path (Join-Path "Cert:\CurrentUser\My" $CertificatePath -ErrorAction "SilentlyContinue") -ErrorAction "SilentlyContinue"
  71.    }
  72.  
  73.    $Certificate = get-item $ResolvedPath -ErrorAction "SilentlyContinue"
  74.    if($Certificate -is [System.IO.FileInfo]) {
  75.       $Certificate = Get-PfxCertificate $Certificate -ErrorAction "SilentlyContinue"
  76.    }
  77.    return $Certificate
  78. }
  79.  
  80. $DefaultCertificate = Get-UserCertificate
  81. $CertificateThumbprint = $DefaultCertificate.Thumbprint
  82.  
  83.  
  84. CMDLET Test-Signature -snapin Huddled.Authenticode {
  85. PARAM (
  86.    [Parameter(Position=0, Mandatory=$true, ValueFromPipeline=$true)]
  87. #  We can't actually require the type, or we won't be able to check the fake ones
  88. #   [System.Management.Automation.Signature]
  89.    $Signature
  90. ,
  91.    [Alias("Valid")]
  92.    [Switch]$ForceValid
  93. )
  94.  
  95. return ( $Signature.Status -eq "Valid" -or
  96.       ( !$ForceValid -and
  97.          ($Signature.Status -eq "UnknownError") -and
  98.          ($_.SignerCertificate.Thumbprint -eq $CertificateThumbprint)
  99.       ) )
  100. }
  101.  
  102. CMDLET Set-AuthenticodeSignature -snapin Huddled.Authenticode {
  103. PARAM (
  104.    [Parameter(Position=1, Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
  105.    [Alias("FullName")]
  106.    [ValidateScript({
  107.       if((resolve-path $_).Provider.Name -ne "FileSystem") {
  108.          throw "Specified Path is not in the FileSystem: '$_'"
  109.       }
  110.       if(!(Test-Path -PathType Leaf $_)) {
  111.          throw "Specified Path is not a File: '$_'"
  112.       }
  113.       return $true
  114.    })]
  115.    [string]
  116.    $Path
  117. ,  ## TODO: you should CHANGE THIS to a method which gets *your* default certificate
  118.    [Parameter(Position=2, Mandatory=$false)]
  119.    $Certificate = $DefaultCertificate
  120. )
  121.  
  122. PROCESS {
  123.    if( ".psm1" -eq [IO.Path]::GetExtension($Path) ) {
  124.    # function setpsm1sig($Path) {
  125.       $ps1Path = "$Path.ps1"
  126.       Rename-Item $Path (Split-Path $ps1Path -Leaf)
  127.       $sig = Microsoft.PowerShell.Security\Set-AuthenticodeSignature -Certificate $Certificate -FilePath $ps1Path | Select *
  128.       Rename-Item $ps1Path (Split-Path $Path -Leaf)
  129.       $sig.PSObject.TypeNames.Insert( 0, "System.Management.Automation.Signature" )
  130.       $sig.Path = $Path
  131.       $sig
  132.    } else {
  133.       Microsoft.PowerShell.Security\Set-AuthenticodeSignature -Certificate $Certificate -FilePath $Path  
  134.    }
  135. }
  136. }
  137.  
  138. CMDLET Get-AuthenticodeSignature -snapin Huddled.Authenticode {
  139. PARAM (
  140.    [Parameter(Position=1, Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
  141.    [Alias("FullName")]
  142.    [ValidateScript({
  143.       if((resolve-path $_).Provider.Name -ne "FileSystem") {
  144.          throw "Specified Path is not in the FileSystem: '$_'"
  145.       }
  146.       if(!(Test-Path -PathType Leaf $_)) {
  147.          throw "Specified Path is not a File: '$_'"
  148.       }
  149.       return $true
  150.    })]
  151.    [string]
  152.    $Path
  153. )
  154.  
  155. PROCESS {
  156.    if( ".psm1" -eq [IO.Path]::GetExtension($Path) ) {
  157.    # function getpsm1sig($Path) {
  158.       $ps1Path = "$Path.ps1"
  159.       Rename-Item $Path (Split-Path $ps1Path -Leaf)
  160.       $sig = Microsoft.PowerShell.Security\Get-AuthenticodeSignature -FilePath $ps1Path | select *
  161.       Rename-Item $ps1Path (Split-Path $Path -Leaf)
  162.       $sig.PSObject.TypeNames.Insert( 0, "System.Management.Automation.Signature" )
  163.       $sig.Path = $Path
  164.       $sig
  165.    } else {
  166.       Microsoft.PowerShell.Security\Get-AuthenticodeSignature -FilePath $Path
  167.    }
  168.  
  169. }
  170. }
  171.  
  172. CMDLET Select-Signed -snapin Huddled.Authenticode {
  173. PARAM (
  174.    [Parameter(Position=1, Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
  175.    [Alias("FullName")]
  176.    [ValidateScript({
  177.       if((resolve-path $_).Provider.Name -ne "FileSystem") {
  178.          throw "Specified Path is not in the FileSystem: '$_'"
  179.       }
  180.       return $true
  181.    })]
  182.    [string]
  183.    $Path
  184. ,
  185.    [Parameter()]
  186.    [switch]$MineOnly
  187. ,
  188.    [Parameter()]
  189.    [switch]$NotMineOnly
  190. ,
  191.    [Parameter()]
  192.    [switch]$BrokenOnly
  193. ,
  194.    [Parameter()]
  195.    [switch]$TrustedOnly
  196. ,
  197.    [Parameter()]
  198.    [switch]$ValidOnly
  199. ,
  200.    [Parameter()]
  201.    [switch]$InvalidOnly
  202. ,
  203.    [Parameter()]
  204.    [switch]$UnsignedOnly
  205.  
  206. )
  207.  
  208.    if(!(Test-Path -PathType Leaf $Path)) {
  209.       # if($ErrorAction -ne "SilentlyContinue") {
  210.       #    Write-Error "Specified Path is not a File: '$Path'"
  211.       # }
  212.    } else {
  213.  
  214.       $sig = Get-AuthenticodeSignature $Path
  215.      
  216.       # Broken only returns ONLY things which are HashMismatch
  217.       if($BrokenOnly   -and $sig.Status -ne "HashMismatch")
  218.       {
  219.          Write-Debug "$($sig.Status) - Not Broken: $Path"
  220.          return
  221.       }
  222.      
  223.       # Trusted only returns ONLY things which are Valid
  224.       if($TrustedOnly  -and $sig.Status -ne "Valid")
  225.       {
  226.          Write-Debug "$($sig.Status) - Not Trusted: $Path"
  227.          return
  228.       }
  229.      
  230.       # AllValid returns only things that are SIGNED and not HashMismatch
  231.       if($ValidOnly    -and (($sig.Status -ne "HashMismatch") -or !$sig.SignerCertificate) )
  232.       {
  233.          Write-Debug "$($sig.Status) - Not Valid: $Path"
  234.          return
  235.       }
  236.      
  237.       # NOTValid returns only things that are SIGNED and not HashMismatch
  238.       if($InvalidOnly  -and ($sig.Status -eq "Valid"))
  239.       {
  240.          Write-Debug "$($sig.Status) - Valid: $Path"
  241.          return
  242.       }
  243.      
  244.       # Unsigned returns only things that aren't signed
  245.       # NOTE: we don't test using NotSigned, because that's only set for .ps1 or .exe files??
  246.       if($UnsignedOnly -and $sig.SignerCertificate )
  247.       {
  248.          Write-Debug "$($sig.Status) - Signed: $Path"
  249.          return
  250.       }
  251.      
  252.       # Mine returns only things that were signed by MY CertificateThumbprint
  253.       if($MineOnly     -and (!($sig.SignerCertificate) -or ($sig.SignerCertificate.Thumbprint -ne $CertificateThumbprint)))
  254.       {
  255.          Write-Debug "Originally signed by someone else, thumbprint: $($sig.SignerCertificate.Thumbprint)"
  256.          Write-Debug "Does not match your default certificate print: $CertificateThumbprint"
  257.          Write-Debug "     $Path"
  258.          return
  259.       }
  260.  
  261.       # NotMine returns only things that were signed by MY CertificateThumbprint
  262.       if($NotMineOnly  -and (!($sig.SignerCertificate) -or ($sig.SignerCertificate.Thumbprint -eq $CertificateThumbprint)))
  263.       {
  264.          if($sig.SignerCertificate) {
  265.             Write-Debug "Originally signed by you, thumbprint: $($sig.SignerCertificate.Thumbprint)"
  266.             Write-Debug "Matches your default certificate print: $CertificateThumbprint"
  267.             Write-Debug "     $Path"
  268.          }
  269.          return
  270.       }
  271.      
  272.       if(!$BrokenOnly  -and !$TrustedOnly -and !$ValidOnly -and !$InvalidOnly -and !$UnsignedOnly -and !($sig.SignerCertificate) )
  273.       {
  274.          # Write-Debug ("You asked for Broken ({0}) or Trusted ({1}) or Valid ({2}) or Invalid ({3}) or Unsigned ({4}) and the cert is: ({5})" -f  [int]$BrokenOnly, [int]$TrustedOnly, [int]$ValidOnly, [int]$InvalidOnly, [int]$UnsignedOnly, $sig.SignerCertificate)
  275.          Write-Debug "$($sig.Status) - Not Signed: $Path"
  276.          return
  277.       }
  278.      
  279.       get-childItem $sig.Path
  280.    }
  281. }
  282.  
  283. Export-ModuleMember Set-AuthenticodeSignature,Get-AuthenticodeSignature,Test-Signature,Select-Signed,Get-UserCertificate
  284.  
  285. # SIG # Begin signature block
  286. # MIIK0AYJKoZIhvcNAQcCoIIKwTCCCr0CAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB
  287. # gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR
  288. # AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUHoNib4Y35ha7fCzaYIyjS3sJ
  289. # HWGgggbEMIIGwDCCBKigAwIBAgIJAKpDRVMtv0LqMA0GCSqGSIb3DQEBBQUAMIHG
  290. # MQswCQYDVQQGEwJVUzERMA8GA1UECBMITmV3IFlvcmsxEjAQBgNVBAcTCVJvY2hl
  291. # c3RlcjEaMBgGA1UEChMRSHVkZGxlZE1hc3Nlcy5vcmcxHjAcBgNVBAsTFUNlcnRp
  292. # ZmljYXRlIEF1dGhvcml0eTErMCkGA1UEAxMiSm9lbCBCZW5uZXR0IENlcnRpZmlj
  293. # YXRlIEF1dGhvcml0eTEnMCUGCSqGSIb3DQEJARYYSmF5a3VsQEh1ZGRsZWRNYXNz
  294. # ZXMub3JnMB4XDTA4MDcwMjAzNTA1OVoXDTA5MDcwMjAzNTA1OVowgcAxCzAJBgNV
  295. # BAYTAlVTMREwDwYDVQQIEwhOZXcgWW9yazESMBAGA1UEBxMJUm9jaGVzdGVyMRow
  296. # GAYDVQQKExFIdWRkbGVkTWFzc2VzLm9yZzEuMCwGA1UECxMlaHR0cDovL0h1ZGRs
  297. # ZWRNYXNzZXMub3JnL0NvZGVDZXJ0LmNydDEVMBMGA1UEAxMMSm9lbCBCZW5uZXR0
  298. # MScwJQYJKoZIhvcNAQkBFhhKYXlrdWxASHVkZGxlZE1hc3Nlcy5vcmcwggIiMA0G
  299. # CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDXuceXJZYARJbSTU4hoh91goVp2POx
  300. # 6Mz/QZ6D5jcT/JNhdW2GwYQ9YUxNj8jkhXg2Ixbgb1djRGMFC/ekgRkgLxyiuhRh
  301. # NrVE1IdV4hT4as3idqnvWOi0S3z2R2EGdebqwm2mrRmq9+DbY+FGxuNwLboWZx8Z
  302. # roGlLLHRPzt9pabQq/Nu/FIFO+4JzZ8S5ZnEaKTm4dpD0g6j653OWYVvNXJbS/W4
  303. # Dis5aRkHT1q1Gp02dYHh3NTKrpv1nus9BTDlJRwmU/FgGLNQIvnRwqVoBh+I7tVq
  304. # NIRnI1RpDTGyFEohbH8mRlwq3z4ijtb6j9boUJEqd8hQshzUMcALoTIR1tN/5APX
  305. # u2j4OqGFESM/OG0i2hLKbnP81u581aZT1BfVfQxvDuWrFiurMxllVGY1NvKkXwc8
  306. # aOZktqMQWbWAs2bxZqERbOILXOmkL/mvPdy+e5yQveriHAhrDONu7a79ylreMHBR
  307. # XrmYJTK2G/aHvB5vrXjMPw0TBeph0sM2BN2eVzenAAMsIiGlXPXvtKrpKRiBdx5f
  308. # 9SV5dyUG2tR8ANDuc2AMB8FKICuMUd8Sx96p4FOBQhXhvF/RZcWZIW5o+A4sHvYE
  309. # /s4oiX7LxGrQK2abNiCVs9BDLI/EcSs/TP+ZskBqu7Qb+AVeevoY3T7skihuyC/l
  310. # h7EwqjfNpVQ9UwIDAQABo4G0MIGxMB0GA1UdDgQWBBTgB9XYJV/kJAvnkWmKDHsh
  311. # 7Cn3PzAfBgNVHSMEGDAWgBQ+5x4ah0JG0o4iUj0TebNd4MCVxTAJBgNVHRMEAjAA
  312. # MBEGCWCGSAGG+EIBAQQEAwIEEDAWBgNVHSUBAf8EDDAKBggrBgEFBQcDAzALBgNV
  313. # HQ8EBAMCBsAwLAYJYIZIAYb4QgENBB8WHU9wZW5TU0wgR2VuZXJhdGVkIENlcnRp
  314. # ZmljYXRlMA0GCSqGSIb3DQEBBQUAA4ICAQAw8B6+s48CZZo5h5tUeKV7OWNXKRG7
  315. # xkyavMkXpEi58BSLutmE3O7smc3uvu3TdCXENNUlGrcq/KQ8y2eEI8QkHgT99VgX
  316. # r+v5xu2KnJXiOOIxi65EZybRFFFaGJNodTcrK8L/tY6QLF02ilOlEgfcc1sV/Sj/
  317. # r60JS1iXIMth7nYZVjtWeYXOrsd+I+XwJuoVNJlELNdApOU4ZVNrPEuV+QRNMimj
  318. # lqIOv2tn9TDdNGUqaOCI0w+a1XQvapEPWETfQK+o9pvYINTswGDjNeb7Xz8ar2JB
  319. # 9IVs2xtxDohHB75kyRrlY1hkoY5j12ZhWOlm0L9Ks6XvmMtXJIjj0/m9Z+3s+9p6
  320. # U7IYjz5NnzmDvtNUn2y9zxB/rUx/JqoUO3BWRKiLX0lvGRWJlzFr9978kH2SXxAD
  321. # rsKfzB7YZzMh9hZkGNlJf4T+HTB/OXG1jyfkyqQvhNB/tDAaq+ejDtKNBF4hMS7K
  322. # Z0B4vagIxFwMuTiei4UaOjrGzeCfT9w1Bmj6uLJme5ydQVM0V7z3Z6jR3LVq4c4s
  323. # Y1dfPmYlw62cbyV9Kb/H2hYw5K0OMX60LfLQZOzIPzAeRJ87NufwZnC1afxsSCmU
  324. # bvSx4kCMgRZMXw+d1SHRhh7z+06YTQjnUMmtTGt7DtUkU6I8LKEWF/mAzF7sq/7P
  325. # AyhPsbu91X5FuzGCA3YwggNyAgEBMIHUMIHGMQswCQYDVQQGEwJVUzERMA8GA1UE
  326. # CBMITmV3IFlvcmsxEjAQBgNVBAcTCVJvY2hlc3RlcjEaMBgGA1UEChMRSHVkZGxl
  327. # ZE1hc3Nlcy5vcmcxHjAcBgNVBAsTFUNlcnRpZmljYXRlIEF1dGhvcml0eTErMCkG
  328. # A1UEAxMiSm9lbCBCZW5uZXR0IENlcnRpZmljYXRlIEF1dGhvcml0eTEnMCUGCSqG
  329. # SIb3DQEJARYYSmF5a3VsQEh1ZGRsZWRNYXNzZXMub3JnAgkAqkNFUy2/QuowCQYF
  330. # Kw4DAhoFAKB4MBgGCisGAQQBgjcCAQwxCjAIoAKAAKECgAAwGQYJKoZIhvcNAQkD
  331. # MQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwIwYJ
  332. # KoZIhvcNAQkEMRYEFIav5gxrUCeM18gHLn9rvM7Y9bSfMA0GCSqGSIb3DQEBAQUA
  333. # BIICAClTS+mIW7vAIsYB+MvbqD6oR5BW1TRXq5CAfPLK+XveUmYczvs4Ujx7TBAp
  334. # twJm4rR/nz9cEi+yI12R5YbYmVcfa2gxreXkp3BKWOvhYvfPggh07VssBc7Ik4q+
  335. # ppqdD8tbh1lln8atkGaTfNm3h50es5KADSkMSErXlM+eMNAttnDElKzYszDlLkn4
  336. # VTUrvqorx5kLpk7soOb7NdkqG4FnIBjGgpsCGcfkBZNzdIjJnnaXje5DF6MwLLKv
  337. # kBBXkmLXa4c+NpzDW9BhVa4VvfVPNpEMuOoJ9LxIXp4hWOcD4oAYGYbuqNpTzhsc
  338. # nrgebPHh1RmMbyShYCHIilMeLKJUgJqS7yCp+qAlxfKmtVKUJOMxJXZahDwLVImq
  339. # a89OiBDjsDtE2sKdlfVu+rFOa/itWa0NvMX6Qn5+jo/D//3EddYTPIUQnPjYcdo2
  340. # RveOsun6apOo749M7WSTSCzvkwR86TpQU9sYyrhCMdOPdhDrqRgGwXpjUhzjG4Wl
  341. # Xk18XlpuqAMA7nQlcZ2SSIu7hgEXRR3zP/rXWvjcxan42qai3pFt9JBshieWveE2
  342. # VveewNAv4ltAjE+80DXGf81q92uBB9ym3g9vbRRJY42J/4MG5Z9lL0SbDY54LiJh
  343. # BA/Z0IU5PGgpt5PEpejJqx4o/URH7bZTI5x16NWXhvQDJJyb
  344. # 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