Signtool allows me to sign code but Set-AuthenticodeSignature says the “certificate is not suitable for code signing”
-
13-06-2021 - |
質問
I have a self-signed code signing certificate, made with the directions from this answer, that works fine when used with signtool.exe
, however if I try to sign using Set-AuthenticodeSignature
, it fails.
Why can I sign using signtool
, but not using Set-AuthenticodeSignature
?
signtool
:Signtool sign /v /n "VetWeb" SetupRDPPermissions.ps1 The following certificate was selected: Issued to: VetWeb Issued by: VetWeb CA Expires: Sat Dec 31 18:59:59 2039 SHA1 hash: 84136EBF8D2603C2CD6668C955F920C6C6482EE4 Done Adding Additional Store Successfully signed: SetupRDPPermissions.ps1 Number of files successfully Signed: 1 Number of warnings: 0
Set-AuthenticodeSignature
:$cert = @(Get-Childitem cert:\CurrentUser\My | Where-Object -FilterScript {$_.Subject -eq 'CN=VetWeb'})[0] Set-AuthenticodeSignature SetupRDPPermissions.ps1 $cert Set-AuthenticodeSignature : Cannot sign code. The specified certificate is not suitable for code signing. At line:1 char:26 + Set-AuthenticodeSignature <<<< SetupRDPPermissions.ps1 $cert + CategoryInfo : InvalidArgument: (:) [Set-AuthenticodeSignature], PSArgumentException + FullyQualifiedErrorId : Argument,Microsoft.PowerShell.Commands.SetAuthenticodeSignatureCommand
Get-Childitem cert:\CurrentUser\My -CodeSigningCert
returns no results
-
$cert | Format-List * PSPath : Microsoft.PowerShell.Security\Certificate::CurrentUser\My\84136EBF8D2603C2CD6668C955F920C6C6482EE4 PSParentPath : Microsoft.PowerShell.Security\Certificate::CurrentUser\My PSChildName : 84136EBF8D2603C2CD6668C955F920C6C6482EE4 PSDrive : cert PSProvider : Microsoft.PowerShell.Security\Certificate PSIsContainer : False Archived : False Extensions : {System.Security.Cryptography.Oid} FriendlyName : IssuerName : System.Security.Cryptography.X509Certificates.X500DistinguishedName NotAfter : 12/31/2039 5:59:59 PM NotBefore : 6/1/2012 1:49:31 PM HasPrivateKey : True PrivateKey : System.Security.Cryptography.RSACryptoServiceProvider PublicKey : System.Security.Cryptography.X509Certificates.PublicKey RawData : {48, 130, 1, 235...} SerialNumber : CF330347F35AC0B4427AFFA82DB51238 SubjectName : System.Security.Cryptography.X509Certificates.X500DistinguishedName SignatureAlgorithm : System.Security.Cryptography.Oid Thumbprint : 84136EBF8D2603C2CD6668C955F920C6C6482EE4 Version : 3 Handle : 479608336 Issuer : CN=VetWeb CA Subject : CN=VetWeb
解決
I had the same problem and the answer I figured out was that I had to create two certificates. First, a trusted root certificate authority using
makecert -n "CN=PowerShell Local Certificate Root" -a sha1 -eku 1.3.6.1.5.5.7.3.3 -r -sv root.pvk root.cer -ss Root -sr localMachine
And then a personal certificate from the above certificate authority using
makecert -pe -n "CN=PowerShell User" -ss MY -a sha1 -eku 1.3.6.1.5.5.7.3.3 -iv root.pvk -ic root.cer
Once these are created, use
$cert = @(Get-ChildItem cert:\CurrentUser\My -CodeSigning)[0]
for signing (assuming you have only one codesigning certificate). For example, if the script's name is xyz.ps1, use this command in PowerShell
Set-AuthenticodeSignature path/to/xyz.ps1 $cert
他のヒント
According to get-help certificate -CodeSigningCert
dynamic parameter from the certificate provider gets only those certificates with code-signing authority.
Now why signtool
can sign and not Set-AuthenticodeSignature
, the explanation is maybe in Introduction to Code Signing Microsoft document.
Here is my version of generation of Certification authority :
# Gen-CACert.ps1
clear-host
$scriptBlock = {.\Makecert -n `"CN=PowerShell Authorite de certification`" <# Sujet du certificat (conforme à la norme X50 #>`
-a sha1 <# Algorithme utilisé #>`
-eku 1.3.6.1.5.5.7.3.3 <# Option du certificat (signature de code) #>`
-r <# Certificat auto signé #>`
<# -ss `"$($args[0])`" Dossier de stockage du certificat #>`
-ss `"root`" <# Dossier de stockage du certificat #>`
-sr localMachine <# Magasin de stockage localmachine ou currentuser (defaut) #>`
-sv `"$($args[0]).pvk`" <# Nom du fichier contenant la clef privée #>`
`"$($args[0]).cer`"} <# Nom du fichier certificat #>
$PoshCARoot = "PoshCARoot"
Invoke-Command -ScriptBlock $scriptBlock -ArgumentList $PoshCARoot
Here is my version of generation of dev certificate :
# Gen-DevCert.ps1
clear-host
$scriptBlock = {.\Makecert -pe <# La clef privée est exportable #>`
-n `"CN=PowerShell Dev Team`" <# Sujet du certificat (conforme à la norme X509 #>`
-a sha1 <# Algorithme utilisé #>`
-eku 1.3.6.1.5.5.7.3.3 <# Option du certificat (signature de code) #>`
-ss `"My`" <# Dossier de stockage du certificat #>`
-sr currentuser <# Magasin de stockage localmachine ou currentuser (defaut) #>`
-iv `"$($args[0]).pvk`" <# Clef privée de l'autorité #>`
-ic `"$($args[0]).cer`" <# Certificat de l'autorité #>`
`"$($args[1]).cer`"} <# Nom du fichier certificat #>
$PoshCARoot = "PoshCARoot"
$PoshDevTeam = "PoshDevTeam"
Invoke-Command -ScriptBlock $scriptBlock -ArgumentList $PoshCARoot,$PoshDevTeam
The issue is the signing certificate is malformed and missing the correct KUs & EKUs.
To resolve, create a self-signed CA using openssl
and the openssl.cnf
linked to below, a code signing ICA signed by the self-signed CA, and finally a code signing cert signed by the ICA
Pre-built openssl.cnf
contains all info & commands required beginning on Line 430:
- Prerequisite:
- Create CA:
# CA key should have a secure passphrase of at least 20 characters, containing # at least 2 uppercase, 2 lowercase, 2 numbers, and 2 symbols # PreReqs: Create files crlnumber, index, rand, & serial mkdir cert crl echo 01 > crl\crlnumber ; echo > index ; echo > rand ; echo 00 > serial # Create CA: openssl req -x509 -new -sha512 -days 3650 -newkey rsa:4096 -keyout "CA.key.pem" -out "CA.crt.pem" -config "openssl.cnf" -extensions v3_ca
- Create ICA:
# ICA key should have a secure passphrase of at least 20 characters, containing # at least 2 uppercase, 2 lowercase, 2 numbers, and 2 symbols # Request: openssl req -out "code-signing-ICA.csr" -new -days 3650 -sha512 -newkey rsa:4096 -keyout "code-signing-ICA.key" -config "openssl.cnf" -extensions v3_signing_ica # Sign: openssl x509 -req -sha512 -days 3650 -in "code-signing-ICA.csr" -CA "CA.crt.pem" -CAkey "CA.key.pem" -CAserial "serial" -out "code-signing-ICA.crt.pem" -extfile "openssl.cnf" -extensions v3_signing_ica # Create Concatenated CA - ICA Cert Chain: # Windows: cmd /c type "code-signing-ICA.crt.pem" "CA.crt.pem" > "code-signing-ICA-Chain.crt.pem" # BSD/Linux: cat "code-signing-ICA.crt.pem" "CA.crt.pem" > "code-signing-ICA-Chain.crt.pem"
- Create Signing Cert:
# Request: openssl req -out "code-signing.csr" -new -days 3650 -sha512 -newkey rsa:2048 -keyout "code-signing.key.pem" -config "openssl.cnf" -extensions v3_codesign # Sign: openssl x509 -req -sha512 -days 3650 -in "code-signing.csr" -CA "code-signing-ICA-chain.crt.pem" -CAkey "code-signing-ICA.key.pem" -CAserial "serial" -out "code-signing.crt.pem" -extfile "openssl.cnf" -extensions v3_codesign # Export: openssl pkcs12 -export -out "code-signing.p12" -inkey "code-signing.key.pem" -in "code-signing.crt.pem" -certfile "code-signing-ICA-chain.crt.pem"
OpenSSL KUs & EKUs
Code signing certificates should have the following set:
-
keyUsage = critical, nonRepudiation, digitalSignature
nonRepudiation
:
Certificate may be used to sign data as above but the certificate public key may be used to provide non-repudiation services
(Prevents the signing entity from falsely denying some action)digitalSignature
:
Certificate may be used to apply a digital signature
(Used for entity authentication & data origin authentication with integrity)
-
extendedKeyUsage = critical, codeSigning, msCodeInd, msCodeCom, mcCTLSign, timeStamping
codeSigning
:
Code SigningmsCodeInd
:
Microsoft Individual Code Signing (authenticode)msCodeCom
:
Microsoft Commerical Code Signing (authenticode)mcCTLSign
:
Microsoft Trust List SigningtimeStamping
:
Trusted Timestamping
SignTool
Prerequisites:
- Install Windows SDK
- +R →
sysdm.cpl
→ OK
Advanced → Environment Variables... → System variables → Path → Edit... - Add to PATH:
%ProgramFiles(x86)%\Windows Kits\10\bin\10.0.15063.0\x64
- Ensure
\10\bin\10.0.15063.0\x64
reflects the proper path for your Windows version
- Ensure
# Establish $TS variable:
Set-Variable -Name TS -Value "http://sha256timestamp.ws.symantec.com/sha256/timestamp" -Scope "Global"
# Sign:
SignTool sign /s "MY" /fd "SHA256" /ph /td "SHA256" /tr $TS "Path\to\File"
sign
:
Sign files using an embedded signature/s
<name>
:
Specify the Store to open when searching for the cert (Default:MY
Store)/fd
:
Specifies the file digest algorithm to use for creating file signatures (Default: SHA1)/ph
:
Generate page hashes for executable files if supported/td
<alg>
:
Used with/tr
or/tseal
to request a digest algorithm used by the RFC3161 timestamp server/tr
<URL>
:
Specifies the RFC3161 timestamp server's URL (warning is generated if timestamping fails)- If
/tr
or/t
is not specified, the signed file will not be timestamped
- If
PowerShell
# Establish $cert variable:
$cert = Get-PfxCertificate -FilePath "Path\to\Signing\Cert"
# Establish $TS variable (if not already set above):
Set-Variable -Name TS -Value "http://sha256timestamp.ws.symantec.com/sha256/timestamp" -Scope "Global"
# Sign:
Set-AuthenticodeSignature -HashAlgorithm "sha256" -IncludeChain "all" -FilePath "File" -Certificate $cert -TimestampServer $TS
Set-AuthenticodeSignature
:
Adds an Authenticode signature to a PowerShell script or other files-HashAlgorithm
:
Specifies hashing algorithm used to compute the digital signature
(PowerShell 2: sha1 || PowerShell 3+: sha256)-IncludeChain
<String>
:
Determines which certs in the chain of trust are included in the digital signature (default:NotRoot
); Acceptable values:Signer
: Includes only the signer's certificateNotRoot
: Includes all certs in the certificate chain, except for the root authorityAll
: Includes all certs in the certificate chain
-Certificate
<X509Certificate>
:
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- To find a cert, use
Get-PfxCertificate
orGet-ChildItem
in the Certificate [Cert:
] drive; command fails if certificate isn't valid or does not havecodeSigning
authority
- To find a cert, use