The default instructions on the PivKey documentation site: https://pivkey.zendesk.com/hc/en-us do not provide any examples for configuring a self-signed certificate in any of the 25 slots. These instructions were tested with the PivKey C910
version, but likely most Taglio variants will work the same way.
There is support in powershell 5.1+ on currently supported Windows OS (Server 2012+/Windows 10+) configurations for generating self-signed certificates with a wide variety of configuration parameters, including support for the Microsoft Smart Card Key Storage Provider
to generate keys on a smartcard.
- Reference: https://learn.microsoft.com/en-us/powershell/module/pki/new-selfsignedcertificate
- Setup mapping of generate certificate to certificate slots (see Powershell script snippet below) to automatically assign a slot by using the correct
Application Policies
OID configuration in in the initial request.
Requires installation of the PivKey Windows minidriver to allow writing to the certificate slots and generation of private keys. Once certificates/keys have been generated and written, you can use the same smartcard on other devices (Windows) and platforms (Linux/MacOS) with either in-box drivers, or open-sources OpenSC
solutions.
- Download the latest Pivkey Admin software version from here: https://pivkey.com/pkadmin.zip
- Download OpenSC pkcs11 drivers from here: https://github.com/OpenSC/OpenSC/releases (or
winget install opensc.opensc
as desired)
- Install the PivKey Admin software. (re-insert card after installation to allow minidriver to load)
- Begin by setting a secure user pin (6-8 digits) and management key (48 digits) using
pivkeytool.exe
using example .bat file (or manually)- See:
"C:\Program Files (x86)\PIVKey Installer\PIVKey Admin Tools\pivkeytool-example-change-pin-and-admin-key.bat"
after install and change thechangepin
andchangeadminkey
values, then run the batch file.
- See:
- Generate a new self-signed certificate (example shown here in powershell enables
CodeSigning
andClient Authentication
, but feel free to modify as desired.- Example below uses RSA 2048-bit Key. Have not tested other configurations yet.
- Configure your desired application for OpenSC's pkcs11 module.
- e.g.: add
PKCS11Provider "C:\Program Files\OpenSC Project\OpenSC\pkcs11\opensc-pkcs11.dll"
to your~/.ssh/config
file as desired - Export public keys for use with
~/.ssh/authorized_keys
file on remote hosts for usersssh-add -L
orssh-keygen -D 'C:\Program Files\OpenSC Project\OpenSC\pkcs11\opensc-pkcs11.dll'
- e.g.: add
$debugpreference = "Continue"
new-alias pivkeytool 'C:\Program Files (x86)\PIVKey Installer\PIVKey Admin Tools\PivKeyTool.exe'
function user-setup-pivkeycert ($slot){
<#
.SYNOPSIS
Query or Generate new certificate and key on SmartCard using CSP: "Microsoft Base Smart Card Crypto Provider", and associate with a user-provided slot id.
.DESCRIPTION
- If there is a code signing certificate and key in the Windows certificate store under Personal\Certificates (aka CurrentUser\My), use it for the next step.
- If there is no applicable certificate, create a new self-signed certificate using the $slot parameter
- Copy Certificate to the Trusted Root store for the Current User (does not require admin)
- Copy Certificate to the Trusted Publisher store for the Current User (does not require admin)
#>
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 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
if ( -not $cert ) { Write-Error "Fatal error generating certificate, please debug..."; return $false}
# You can change these to LocalMachine instead of CurrentUser, but requires admin. For portability, leaving as user.
$RootcertStore = New-Object System.Security.Cryptography.X509Certificates.X509Store("Root", "CurrentUser")
$RootcertStore.Open("ReadWrite")
# Will cause a dialog to appear for confirmation.
$RootcertStore.Add($cert)
$RootcertStore.Close()
$PubcertStore = New-Object System.Security.Cryptography.X509Certificates.X509Store("TrustedPublisher", "CurrentUser")
$PubcertStore.Open("ReadWrite")
$PubcertStore.Add($cert)
$PubcertStore.Close()
}
}
- Ensure your smartcard is inserted and PivKey minidriver installed.
- Identify appropriate slots/keys to delete (powershell example):
new-alias pivkeytool 'C:\Program Files (x86)\PIVKey Installer\PIVKey Admin Tools\PivKeyTool.exe'
new-alias pkcs11-tool 'C:\Program Files\OpenSC Project\OpenSC\tools\pkcs11-tool.exe'
pivkeytool --listmd --listpiv
pkcs11-tool --list-objects --type cert
certutil -key -csp SC -v | findstr /c:Serial /c:Subject: /c:KeyId
- Verify the key id (prefix-guid) is the one you want to remove (FIFO rules apply) e.g.:
te-b8010332-1cb4-442b-b83e-dd7a82617c1a
- Start an admin powershell (or command prompt), and run:
certutil -delkey -csp SC <key id>
(where<key id>
is value from Step 2.)