PowerShell / PowerShell

PowerShell for every system!
https://microsoft.com/PowerShell
MIT License
44.7k stars 7.24k forks source link

Set-AuthenticodeSignature says Code Signing certificate is not valid for code signing usage #19436

Open zhihaoy opened 1 year ago

zhihaoy commented 1 year ago

Prerequisites

Steps to reproduce

I created two ECDSA certificates using New-SelfSignedCertificate, one as the root CA and one as the code signing certificate. The code signing certificate has EnhancedKeyUsageList = Code Signing. I signed the code signing certificate with the root CA. I exported the root CA. I added the root CA to the machine Trusted CA store. I find the code signing certificate, and save it in the variable $codeCertificate. I run command:

Set-AuthenticodeSignature -FilePath .\vimdiff.ps1 -Certificate $codeCertificate -TimeStampServer "http://timestamp.digicert.com"

Expected behavior

The .ps1 script is signed, just like what signtool does.

Actual behavior

PS> $v = Set-AuthenticodeSignature -FilePath .\vimdiff.ps1 -Certificate $codeCertificate -TimeStampServer "http://timestamp.digicert.com"
PS> $v.Status
UnknownError
PS> $v.StatusMessage
The certificate is not valid for the requested usage.
PS> $codeCertificate

   PSParentPath: Microsoft.PowerShell.Security\Certificate::LocalMachine\My

Thumbprint                                Subject              EnhancedKeyUsageList
----------                                -------              --------------------
694839C14DC54DF4EB54D8643F93BD6782C0B70A  CN=DePaul University Code Signing

Environment data

Name                           Value
----                           -----
PSVersion                      7.3.3
PSEdition                      Core
GitCommitId                    7.3.3
OS                             Microsoft Windows 10.0.19045
Platform                       Win32NT
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0
jborean93 commented 1 year ago

Does the cert have the private key accessible to your user? This has to be available to actually sign the file.

zhihaoy commented 1 year ago

The document says:

Using the CodeSigningCert parameter with Get-ChildItem only returns certificates that have code-signing authority and contain a private key. If there is no private key, the certificates cannot be used for signing.

So I did

PS> Get-ChildItem Cert:\LocalMachine\My -CodeSigningCert

   PSParentPath: Microsoft.PowerShell.Security\Certificate::LocalMachine\My

Thumbprint                                Subject              EnhancedKeyUsageList
----------                                -------              --------------------
694839C14DC54DF4EB54D8643F93BD6782C0B70A  CN=DePaul University Code Signing

This reminds me. My certificate is password protected, but Set-AuthenticodeSignature does not promote me for password.

jborean93 commented 1 year ago

Sorry I forgot that -CodeSigningCert is meant to filter by this already. Unfortunately I've seen reports that while Windows things that a private key is available there are still extra mechanisms that might block it from getting the key. Things like an external HSM or yubikey needing to be present when it's requested. It could even be that the underlying mechanism just doesn't support ECDSA keys. Technically they should because the standard Authenticode is based on (PKCS 7/CMS) does but it was originally written in a time when RSA was pretty much the main key type.

You could try the following and see if it errors or not

[System.Security.Cryptography.X509Certificates.ECDsaCertificateExtensions]::GetECDsaPrivateKey($codeCertificate)

This reminds me. My certificate is password protected, but Set-AuthenticodeSignature does not promote me for password.

Do you mean the pfx that was originally created had a password or are you using strong protection on the cert in the cert store itself?

zhihaoy commented 1 year ago

You could try the following and see if it errors or not

[System.Security.Cryptography.X509Certificates.ECDsaCertificateExtensions]::GetECDsaPrivateKey($codeCertificate)

It works fine.

PS> [System.Security.Cryptography.X509Certificates.ECDsaCertificateExtensions]::GetECDsaPrivateKey($codeCertificate)

KeySize              : 256
LegalKeySizes        : {System.Security.Cryptography.KeySizes, System.Security.Cryptography.KeySizes}
HashAlgorithm        : SHA256
Key                  : System.Security.Cryptography.CngKey
KeyExchangeAlgorithm :
SignatureAlgorithm   : ECDsa

This reminds me. My certificate is password protected, but Set-AuthenticodeSignature does not promote me for password.

Do you mean the pfx that was originally created had a password or are you using strong protection on the cert in the cert store itself?

Nevermind. To be used with signtool for comparison, I created a .pfx out of this certificate. The certificate itself has no concept of being password-protected.

jborean93 commented 1 year ago

I'm not sure what the problem is sorry, I would have to create my own ECDSA key and try it out. You can also try out my own module OpenAuthenticode (source code is here)and see if it works or not. It implements the signing functionality all in dotnet and isn't reliant on some C library where errors are somewhat hidden. I can't guarantee it will work as I haven't tested out ECDSA keys with it yet.

zhihaoy commented 1 year ago

OpenAuthenticode can sign it, but signtool verify /pa the-signed.ps1 says

Index  Algorithm  Timestamp
========================================
SignTool Error: The signing certificate is not valid for the requested usage.

The same command gives a different error to the file I signed using signtool itself:

Index  Algorithm  Timestamp
========================================
SignTool Error: WinVerifyTrust returned error: 0x80096010
        The digital signature of the object did not verify.

This is strange now, because I used openssl to generate the same kinds of certificates, and they work perfectly fine with signtool. Those certificates have nothing special. Their algorithms are -name prime256v1, and the code signing certificate uses -addext "extendedKeyUsage = codeSigning".

This is how did I create the two certificates (that gave me those errors in this thread) using PowerShell:

New-SelfSignedCertificate -certstorelocation cert:\localmachine\my -DnsName "add this to CA" -KeyAlgorithm "ECDSA_secP256r1"
new-selfSignedCertificate -certstorelocation cert:\localmachine\my -dnsname "fill what you want" -KeyAlgorithm "ECDSA_secP256r1" -Type CodeSigningCert -Signer $rootcert
zhihaoy commented 1 year ago

I checked those OpenSSL certificates into my cert store, it shows:

image

But the -Type CodeSigningCert I created using new-selfSignedCertificate shows

image

jborean93 commented 1 year ago

Thanks for the code to generate these certs. I can confirm that Set-AuthenticodeSignature actually signs the code it's just not able to verify the signature, just like Windows and signtool. You can see the same thing with Get-OpenAuthenticodeSignature and through the GUI

$ Get-OpenAuthenticodeSignature C:\temp\test.ps1
Get-OpenAuthenticodeSignature: SignerInfo digest algorithm '2.16.840.1.101.3.4.2.1' is not valid for signature algorithm ''.

OpenAuthenticode can sign it, but signtool verify /pa the-signed.ps1 says

Yea Set-AuthenticodeSignature will be using the registered powershell SIP provider to sign the file so makes sense they are producing the same data. The OpenAuthenticode library uses the dotnet class SignedCms to do all the signature work with some custom code around storing it in the ps1 itself. I do need to add more validation to OpenAuthenticode to pre-check the certificate but I mostly focused on just getting the signature work itself up and running before looking into that side more.

One thing I did notice is that the signature signed by Set-AuthenticodeSignature has a digest encryption algorithm of ECC whereas Set-OpenAuthenticodeSignature has a value of sha256ECDSA. The latter seems to be enough to provide a valid signature from a PKCS7/CMS perspective, it's just that the certificate used isn't seen as valid for its purpose as you've pointed out.

Unfortunately I'm not sure why it is being seen as invalid. I'll try and investigate some more and see what I can find.

vedosis commented 1 year ago

Digging into this a little more because it's really buggin' me.

I have a similar problem. I'm using cfssl to generate certificates against my Internal CA. I have a proper certificate chain setup with a root -> intermediate -> personal and the root certificate is trusted by all systems.

I've setup some garbage certificates and an environment:


PS C:\Users\user01> $file = gci \\local.network.path\subdir\profile.ps1
PS C:\Users\user01> $file.Exists
True

PS C:\Users\user01> $ecdsaCert,$rsaCert,$selfSignedCert = Get-ChildItem -Path Cert:/CurrentUser/My -CodeSigningCert
PS C:\Users\user01> $ecdsaCert,$rsaCert,$selfSignedCert | Format-List Subject,Thumbprint,HasPrivateKey,EnhancedKeyUsageList

Subject              : CN=[ECDSA] Vedosis
Thumbprint           : FD16C389450276F86455209F9107FA88FC8AC446
HasPrivateKey        : True
EnhancedKeyUsageList : {Code Signing (1.3.6.1.5.5.7.3.3), Secure Email (1.3.6.1.5.5.7.3.4), Secure Email (1.3.6.1.5.5.7.3.4)}

Subject              : CN=[RSA] Vedosis
Thumbprint           : A0929794A54A2EAC220D66F500E324DBD5C61BAC
HasPrivateKey        : True
EnhancedKeyUsageList : {Code Signing (1.3.6.1.5.5.7.3.3), Secure Email (1.3.6.1.5.5.7.3.4), Secure Email (1.3.6.1.5.5.7.3.4)}

Subject              : CN=[SelfSigned] Vedosis
Thumbprint           : 6D0044B84D1D81B829714FCCB58AAF9AD5CCD973
HasPrivateKey        : True
EnhancedKeyUsageList : {Code Signing (1.3.6.1.5.5.7.3.3)}

PS C:\Users\user01> Set-AuthenticodeSignature -File $file -Certificate $ecdsaCert

    Directory: \\local.network.path\subdir

SignerCertificate                         Status        StatusMessage                                          Path
-----------------                         ------        -------------                                          ----
FD16C389450276F86455209F9107FA88FC8AC446  UnknownError  The certificate is not valid for the requested usage.  profile.ps1

PS C:\Users\user01> Set-AuthenticodeSignature -File $file -Certificate $rsaCert

    Directory: \\local.network.path\subdir

SignerCertificate                         Status        StatusMessage                                          Path
-----------------                         ------        -------------                                          ----
A0929794A54A2EAC220D66F500E324DBD5C61BAC  UnknownError  The certificate is not valid for the requested usage.  profile.ps1

PS C:\Users\user01> Set-AuthenticodeSignature -File $file -Certificate $selfSignedCert

    Directory: \\local.network.path\subdir

SignerCertificate                         Status  StatusMessage        Path
-----------------                         ------  -------------        ----
6D0044B84D1D81B829714FCCB58AAF9AD5CCD973  Valid   Signature verified.  profile.ps1

What we see is by default, the Intermediate CA signed certs don't work, but the self signed does. I did some research into what I think the code is trying to do. It looks like Set-AuthenticodeSignature is doing some things:

https://github.com/PowerShell/PowerShell/blob/master/src/System.Management.Automation/security/Authenticode.cs#L153 - Checking for CertIsGoodForSigning https://github.com/PowerShell/PowerShell/blob/master/src/System.Management.Automation/security/SecuritySupport.cs#L607 - Implementation of CertIsGoodForSigning with current certificate (a X509Certificate2 type certificate)

Needs to have (2) things:

  1. c.HasPrivateKey (which is inherited from https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.x509certificates.x509certificate2.hasprivatekey?view=net-7.0#system-security-cryptography-x509certificates-x509certificate2-hasprivatekey)
  2. CertHasOID (where OID is defined as "1.3.6.1.5.5.7.3.3")

Looking at the available certificates, I can see HasPrivateKey is True AND it contains OID Code Signing (1.3.6.1.5.5.7.3.3).

Just to verify there wasn't something exotic with the keys:

PS C:\Users\user01> [System.Security.Cryptography.X509Certificates.ECDSACertificateExtensions]::GetECDSAPrivateKey($ecdsaCert)

KeySize              : 256
LegalKeySizes        : {System.Security.Cryptography.KeySizes, System.Security.Cryptography.KeySizes}
HashAlgorithm        : SHA256
Key                  : System.Security.Cryptography.CngKey
KeyExchangeAlgorithm :
SignatureAlgorithm   : ECDsa

PS C:\Users\user01> [System.Security.Cryptography.X509Certificates.RSACertificateExtensions]::GetRSAPrivateKey($rsaCert)

LegalKeySizes        : {System.Security.Cryptography.KeySizes}
Key                  : System.Security.Cryptography.CngKey
KeyExchangeAlgorithm : RSA
SignatureAlgorithm   : RSA
KeySize              : 2048

PS C:\Users\user01> [System.Security.Cryptography.X509Certificates.RSACertificateExtensions]::GetRSAPrivateKey($selfSignedCert)

LegalKeySizes        : {System.Security.Cryptography.KeySizes}
Key                  : System.Security.Cryptography.CngKey
KeyExchangeAlgorithm : RSA
SignatureAlgorithm   : RSA
KeySize              : 2048

Doesn't look like it's the ECDSA key or the RSA key. I suspect it's something missing from the CSR and something within how the C# lib is evaluating either the OID or the presence of the key. I tried looking deeper into the differences between the self-signed and the Intermediate signed certs but didn't find anything that stood out to me:

PS C:\Users\brian.wilcox\Downloads> $ecdsaCert,$rsaCert,$selfSignedCert | Format-List -Property Subject,EnhancedKeyUsageList,*

Subject                  : CN=[ECDSA] Vedosis
PSPath                   : Microsoft.PowerShell.Security\Certificate::CurrentUser\My\FD16C389450276F86455209F9107FA88FC8AC446
PSParentPath             : Microsoft.PowerShell.Security\Certificate::CurrentUser\My
PSChildName              : FD16C389450276F86455209F9107FA88FC8AC446
PSDrive                  : Cert
PSProvider               : Microsoft.PowerShell.Security\Certificate
PSIsContainer            : False
EnhancedKeyUsageList     : {Code Signing (1.3.6.1.5.5.7.3.3), Secure Email (1.3.6.1.5.5.7.3.4), Secure Email (1.3.6.1.5.5.7.3.4)}
DnsNameList              : {}
SendAsTrustedIssuer      : False
EnrollmentPolicyEndPoint : Microsoft.CertificateServices.Commands.EnrollmentEndPointProperty
EnrollmentServerEndPoint : Microsoft.CertificateServices.Commands.EnrollmentEndPointProperty
PolicyId                 :
Archived                 : False
Extensions               : {System.Security.Cryptography.Oid, System.Security.Cryptography.Oid, System.Security.Cryptography.Oid, System.Security.Cryptography.Oid…}
FriendlyName             :
HasPrivateKey            : True
PrivateKey               :
IssuerName               : System.Security.Cryptography.X509Certificates.X500DistinguishedName
NotAfter                 : 9/12/2024 1:04:00 PM
NotBefore                : 7/17/2023 1:04:00 PM
PublicKey                : System.Security.Cryptography.X509Certificates.PublicKey
RawData                  : {48, 130, 2, 138…}
RawDataMemory            : System.ReadOnlyMemory<Byte>[654]
SerialNumber             : 76A84488B27370330558191A2D764D4190CFC92D
SignatureAlgorithm       : System.Security.Cryptography.Oid
SubjectName              : System.Security.Cryptography.X509Certificates.X500DistinguishedName
Thumbprint               : FD16C389450276F86455209F9107FA88FC8AC446
Version                  : 3
Handle                   : 1531194107056
Issuer                   : CN=IntermediateCA
SerialNumberBytes        : System.ReadOnlyMemory<Byte>[20]

Subject                  : CN=[RSA] Vedosis
PSPath                   : Microsoft.PowerShell.Security\Certificate::CurrentUser\My\A0929794A54A2EAC220D66F500E324DBD5C61BAC
PSParentPath             : Microsoft.PowerShell.Security\Certificate::CurrentUser\My
PSChildName              : A0929794A54A2EAC220D66F500E324DBD5C61BAC
PSDrive                  : Cert
PSProvider               : Microsoft.PowerShell.Security\Certificate
PSIsContainer            : False
EnhancedKeyUsageList     : {Code Signing (1.3.6.1.5.5.7.3.3), Secure Email (1.3.6.1.5.5.7.3.4), Secure Email (1.3.6.1.5.5.7.3.4)}
DnsNameList              : {}
SendAsTrustedIssuer      : False
EnrollmentPolicyEndPoint : Microsoft.CertificateServices.Commands.EnrollmentEndPointProperty
EnrollmentServerEndPoint : Microsoft.CertificateServices.Commands.EnrollmentEndPointProperty
PolicyId                 :
Archived                 : False
Extensions               : {System.Security.Cryptography.Oid, System.Security.Cryptography.Oid, System.Security.Cryptography.Oid, System.Security.Cryptography.Oid…}
FriendlyName             :
HasPrivateKey            : True
PrivateKey               : System.Security.Cryptography.RSACng
IssuerName               : System.Security.Cryptography.X509Certificates.X500DistinguishedName
NotAfter                 : 9/12/2024 1:04:00 PM
NotBefore                : 7/17/2023 1:04:00 PM
PublicKey                : System.Security.Cryptography.X509Certificates.PublicKey
RawData                  : {48, 130, 3, 84…}
RawDataMemory            : System.ReadOnlyMemory<Byte>[856]
SerialNumber             : 4F9B03AFCEF99E29D1A8B7546BC9FEE23C354BA3
SignatureAlgorithm       : System.Security.Cryptography.Oid
SubjectName              : System.Security.Cryptography.X509Certificates.X500DistinguishedName
Thumbprint               : A0929794A54A2EAC220D66F500E324DBD5C61BAC
Version                  : 3
Handle                   : 1531194107184
Issuer                   : CN=IntermediateCA
SerialNumberBytes        : System.ReadOnlyMemory<Byte>[20]

Subject                  : CN=[SelfSigned] Vedosis
PSPath                   : Microsoft.PowerShell.Security\Certificate::CurrentUser\My\6D0044B84D1D81B829714FCCB58AAF9AD5CCD973
PSParentPath             : Microsoft.PowerShell.Security\Certificate::CurrentUser\My
PSChildName              : 6D0044B84D1D81B829714FCCB58AAF9AD5CCD973
PSDrive                  : Cert
PSProvider               : Microsoft.PowerShell.Security\Certificate
PSIsContainer            : False
EnhancedKeyUsageList     : {Code Signing (1.3.6.1.5.5.7.3.3)}
DnsNameList              : {[SelfSigned] Vedosis}
SendAsTrustedIssuer      : False
EnrollmentPolicyEndPoint : Microsoft.CertificateServices.Commands.EnrollmentEndPointProperty
EnrollmentServerEndPoint : Microsoft.CertificateServices.Commands.EnrollmentEndPointProperty
PolicyId                 :
Archived                 : False
Extensions               : {System.Security.Cryptography.Oid, System.Security.Cryptography.Oid, System.Security.Cryptography.Oid}
FriendlyName             :
HasPrivateKey            : True
PrivateKey               : System.Security.Cryptography.RSACng
IssuerName               : System.Security.Cryptography.X509Certificates.X500DistinguishedName
NotAfter                 : 7/17/2024 1:21:05 PM
NotBefore                : 7/17/2023 1:01:05 PM
PublicKey                : System.Security.Cryptography.X509Certificates.PublicKey
RawData                  : {48, 130, 3, 24…}
RawDataMemory            : System.ReadOnlyMemory<Byte>[796]
SerialNumber             : 23EAA6A52AC0F5844B553982432B231B
SignatureAlgorithm       : System.Security.Cryptography.Oid
SubjectName              : System.Security.Cryptography.X509Certificates.X500DistinguishedName
Thumbprint               : 6D0044B84D1D81B829714FCCB58AAF9AD5CCD973
Version                  : 3
Handle                   : 1531194106160
Issuer                   : CN=[SelfSigned] Vedosis
Subject                  : CN=[SelfSigned] Vedosis
SerialNumberBytes        : System.ReadOnlyMemory<Byte>[16]

Yeah, not really sure where to go here. I might try a certificate with ONLY code signing to see if that works, but ... What is goin' on here?

Oh, and as far as signtool goes:

PS C:\Users\user01> signtool sign /sha1 FD16C389450276F86455209F9107FA88FC8AC446 /fd sha256 \\local.network.path\subdir\l_profile.ps1
Done Adding Additional Store
Successfully signed: \\local.network.path\subdir\l_profile.ps1

So, there's that.

tbiles commented 1 year ago

Hi, I stumbled across this thread while researching the same issue. I have a code signing certificate from Sectigo which will sign my powershell scripts using Set-AuthenticodeSignature or using the SDK signtool.exe (also tried Set-OpenAuthnticodeSignature with different results and more feedback from the command) but in all cases, I get a script with a signature block, but they show as not signed through verification procedures.

Based on feedback above, I'm sharing some of my cert information below in hopes that it might help track down this issue. I'm stumped and unable to sign code until I can find a resolution. I appreciate any feedback you can provide.

[System.Security.Cryptography.X509Certificates.ECDsaCertificateExtensions]::GetECDsaPrivateKey($cert)

KeySize : 384 LegalKeySizes : {System.Security.Cryptography.KeySizes, System.Security.Cryptography.KeySizes} HashAlgorithm : SHA256 Key : System.Security.Cryptography.CngKey KeyExchangeAlgorithm : SignatureAlgorithm : ECDsa

$cert | Format-List Subject,Thumbprint,HasPrivateKey,EnhancedKeyUsageList

Subject : CN=University of Minnesota, O=University of Minnesota, S=Minnesota, C=US Thumbprint : F0669EDD9C08C933956AF5EEDD7FDA7CFF4E6643 HasPrivateKey : True EnhancedKeyUsageList : {Code Signing (1.3.6.1.5.5.7.3.3)}

bluecraank commented 5 months ago

My issue:

KeyUsage "Digital Signature" was not checked while creating code signing certificate

And for parent CA, Code-Signing and Digital Signature must be checked as well

daemonhorn commented 1 month ago

There seem to be several sharp edges in the PowerShell implementation of self-signed Authenticode code signing certificates.

Just as an added datapoint to confirm if you utilize any non-default OIDs in your certificate generation, you may need to tweak the config to make it compatible with Authenticode. This example is for a smartcard that requires a custom oid under "Application Policies" to have the driver map to a normal PIV slot , and requires re-iterating the EKU values as well:

Switch ($slot) {
    # These oid values are specific to Pivkey / Taglio driver https://pivkey.zendesk.com/hc/en-us/articles/115000506843-Mapping-a-PIV-Certificate-using-an-OID
    '9a' { $slot_oid = '1.3.6.1.4.1.44986.2.1.1' }
    '9c' { $slot_oid = '1.3.6.1.4.1.44986.2.1.0' }
    '9d' { $slot_oid = '1.3.6.1.4.1.44986.2.1.2' }
    '9e' { $slot_oid = '1.3.6.1.4.1.44986.2.5.0' }
    default { Write-Warning "Unable to determine slot id (9a,9c,9d,9e)"; exit $false }
    }
Write-Debug "Slot: $slot, Slot_oid: $slot_oid"

[DateTime] $ValidThrough = (Get-Date)
# Check to see if there is one in the cert store and use it using -CodeSigningCert and -eku "1.3.6.1.5.5.7.3.2" for boolean &&
$cert = Get-ChildItem Cert:\CurrentUser\My -CodeSigningCert -eku "1.3.6.1.5.5.7.3.2" | Where-Object { $_.NotAfter -gt $ValidThrough -and $_HasPrivateKey} | Select-Object -First 1
Write-Debug "Certificate Store: ($cert)"
if ( -not $cert ) {
    #Create self-signed cert
    $params = @{
        Subject = "CN=$env:USERNAME@$env:USERDOMAIN Smartcard PIV $slot"
        KeyDescription = "CN=$env:USERNAME@$env:USERDOMAIN Smartcard PIV Usage"
        FriendlyName = "$env:USERNAME PIV:$slot"  #Friendlyname is smart on windows and will remove older duplicate mappings in case of collision
        CertStoreLocation = 'Cert:\CurrentUser\My'
        KeyUsage = 'DigitalSignature'
        KeyUsageProperty = 'All'
        Type = 'Custom'
        TextExtension = @(
            # .NET function for OID Mapping (name <-> oid:  [Security.Cryptography.Oid]::new($OIDOrFriendlyName)
            # 2.5.29.37 = Extended Key Usage (EKU) https://docs.redhat.com/en/documentation/red_hat_certificate_system/9/html/administration_guide/standard_x.509_v3_certificate_extensions#Standard_X.509_v3_Certificate_Extensions-extKeyUsage
                # 1.3.6.1.5.5.7.3.1 = Server authentication
                # 1.3.6.1.5.5.7.3.2 = Client authentication
                # 1.3.6.1.5.5.7.3.3 = Code Signing
                # 1.3.6.1.5.5.7.3.4 = Email
                # 1.3.6.1.5.5.7.3.8 = Timestamping
                # 1.3.6.1.5.5.7.3.9 = OCSP Signing
            # 2.5.29.17 = Subject Alt Name (SAN) https://docs.redhat.com/en/documentation/red_hat_certificate_system/9/html/administration_guide/standard_x.509_v3_certificate_extensions#Standard_X.509_v3_Certificate_Extensions-subjectAltName
            # 1.3.6.1.4.1.311.21.10 = Application Policies (Microsoft) https://www.alvestrand.no/objectid/1.3.6.1.4.1.311.html and https://learn.microsoft.com/en-us/powershell/module/pki/new-selfsignedcertificate?view=windowsserver2025-ps&wt.mc_id=ps-gethelp#examples
            # The Application Policies OID value 1.3.6.1.4.1.44986.2.X.X = https://pivkey.zendesk.com/hc/en-us/articles/115000506843-Mapping-a-PIV-Certificate-using-an-OID

            # This EKU example uses Client authentication and Code Signing (see EKU definitions above)
            "2.5.29.37={text}1.3.6.1.5.5.7.3.2,1.3.6.1.5.5.7.3.3",
            # This SAN (Subject Alt Name) value is dynamically generated from the environment variables, may want to use FQDN here for some applications instead of $env:USERDOMAIN.  FQDN may also be needed for Subject=CN value depending on verify routines/libraries
            "2.5.29.17={text}upn=$env:USERNAME@$env:USERDOMAIN"
            # Important:  If you are using Application Policies, you also need to map in the OID values for your EKU configuration, or some validation will not work (e.g. Authenticode CodeSigning)
            "1.3.6.1.4.1.311.21.10={text}oid=$slot_oid&oid=1.3.6.1.5.5.7.3.3&oid=1.3.6.1.5.5.7.3.2"
        )
        KeyAlgorithm = 'RSA'
        KeyLength = 2048
                HashAlgorithm = 'sha256'
        Provider = 'Microsoft Smart Card Key Storage Provider'
        KeyExportPolicy = 'NonExportable'
        }
    Write-Debug "Certificate Params: $params"
    $cert = New-SelfSignedCertificate @params

Note: The "1.3.6.1.4.1.311.21.10={text}oid=$slot_oid&oid=1.3.6.1.5.5.7.3.3&oid=1.3.6.1.5.5.7.3.2" includes both the custom oid for mapping ($slot_oid) and the normal eku oids for code signing and client authentication, or it will invalidate it's use for authenticode.