Here is my script that not only asks the user where the .PFX file is located, but also configures the management key and PUK based on a random number, asks the user for their preferred pin, and includes a function that allows you to reset the device easily. This script still requires Yubikey's PIV tool, found at https://developers.yubico.com/yubico-piv-tool/ . This script has been tested/used on version 0.1.3.
I am not primarily a programmer, so I apologize for any functionality that could have been done better or faster.
####################################################################################################
#
# Script to help out some of the management of YubiKeys in windows using powershell
# Bryan Loveless (bryan.loveless@gmail.com)
# January 2015
# http://actualreverend.blogspot.com/
#
# requires Yubikey PIV tool to be downloaded somewhere (0.1.3 was the verison tested)
# requires PFX to be exported in a pfx file, and not already assigned to a smartcard in use
# PFX must be protected with a password
#
# User must not use the pin numbers 12345 or 4711, as these are default and Reset-yubikey will fail
#
###################################################################################################
# Show an Open File Dialog and return the file selected by the user.
#Borrowed from http://blog.danskingdom.com/powershell-multi-line-input-box-dialog-open-file-dialog-folder-browser-dialog-input-box-and-message-box/
function Read-OpenFileDialog([string]$WindowTitle, [string]$InitialDirectory, [string]$Filter = "All files (*.*)|*.*", [switch]$AllowMultiSelect)
{
Add-Type -AssemblyName System.Windows.Forms
$openFileDialog = New-Object System.Windows.Forms.OpenFileDialog
$openFileDialog.Title = $WindowTitle
if (![string]::IsNullOrWhiteSpace($InitialDirectory)) { $openFileDialog.InitialDirectory = $InitialDirectory }
$openFileDialog.Filter = $Filter
if ($AllowMultiSelect) { $openFileDialog.MultiSelect = $true }
$openFileDialog.ShowHelp = $true # Without this line the ShowDialog() function may hang depending on system configuration and running from console vs. ISE.
$openFileDialog.ShowDialog() > $null
if ($AllowMultiSelect) { return $openFileDialog.Filenames } else { return $openFileDialog.Filename }
}
# Show an Open Folder Dialog and return the directory selected by the user.
#Borrowed from http://blog.danskingdom.com/powershell-multi-line-input-box-dialog-open-file-dialog-folder-browser-dialog-input-box-and-message-box/
function Read-FolderBrowserDialog([string]$Message, [string]$InitialDirectory, [switch]$NoNewFolderButton)
{
$browseForFolderOptions = 0
if ($NoNewFolderButton) { $browseForFolderOptions += 512 }
$app = New-Object -ComObject Shell.Application
$folder = $app.BrowseForFolder(0, $Message, $browseForFolderOptions, $InitialDirectory)
if ($folder) { $selectedDirectory = $folder.Self.Path } else { $selectedDirectory = '' }
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($app) > $null
return $selectedDirectory
}
# reset the device easily
function Reset-Yubikey{
write-host "Are you sure you want to reset the yubikey? Ctrl-C to stop now!"
pause
#prompt user where the yubico-piv executable is:
$yubiPIVExecutable = Read-OpenFileDialog -InitialDirectory 'C:\Program Files (x86)' -WindowTitle "Select yubico-piv-tool.exe file" -filter "yubico-piv-exe files (yubico-piv-tool.exe)|yubico-piv-tool.exe"
# lock the pin an puk numbers on device
& "$yubiPIVExecutable" -a verify-pin -P 4711
& "$yubiPIVExecutable" -a verify-pin -P 4711
& "$yubiPIVExecutable" -a verify-pin -P 4711
& "$yubiPIVExecutable" -a verify-pin -P 4711
& "$yubiPIVExecutable" -a change-puk -P 4711 -N 67567
& "$yubiPIVExecutable" -a change-puk -P 4711 -N 67567
& "$yubiPIVExecutable" -a change-puk -P 4711 -N 67567
& "$yubiPIVExecutable" -a change-puk -P 4711 -N 67567
& "$yubiPIVExecutable" -a verify-pin -P 4711
& "$yubiPIVExecutable" -a verify-pin -P 4711
& "$yubiPIVExecutable" -a verify-pin -P 4711
& "$yubiPIVExecutable" -a verify-pin -P 4711
& "$yubiPIVExecutable" -a change-puk -P 4711 -N 67567
& "$yubiPIVExecutable" -a change-puk -P 4711 -N 67567
& "$yubiPIVExecutable" -a change-puk -P 4711 -N 67567
& "$yubiPIVExecutable" -a change-puk -P 4711 -N 67567
& "$yubiPIVExecutable" -a reset
}
# password generator, using it to get new managment key, borrowed from http://blog.morg.nl/2014/01/generate-a-random-strong-password-in-powershell/
function Generate-Password() {
Param (
[int]$length = 8,
[bool] $includeLowercaseLetters = $true,
[bool] $includeUppercaseLetters = $true,
[bool] $includeNumbers = $true,
[bool] $includeSpecialChars = $false,
[bool] $noSimilarCharacters = $true
)
<#
(c) Morgan de Jonge CC BY SA
Generates a random password. you're able to specify:
- The desired password length (minimum = 4)
- Whether or not to use lowercase characters
- Whether or not to use uppercase characters
- Whether or not to use numbers
- Whether or not to use special characters
- Whether or not to avoid using similar characters ( e.g. i, l, o, 1, 0, I)
#>
# Validate params
if($length -lt 4) {
$exception = New-Object Exception "The minimum password length is 4"
Throw $exception
}
if ($includeLowercaseLetters -eq $false -and
$includeUppercaseLetters -eq $false -and
$includeNumbers -eq $false -and
$includeSpecialChars -eq $false) {
$exception = New-Object Exception "At least one set of included characters must be specified"
Throw $exception
}
#Available characters
$CharsToSkip = [char]"i", [char]"l", [char]"o", [char]"1", [char]"0", [char]"I"
$AvailableCharsForPassword = $null;
$uppercaseChars = $null
for($a = 65; $a -le 90; $a++) { if($noSimilarCharacters -eq $false -or [char][byte]$a -notin $CharsToSkip) {$uppercaseChars += ,[char][byte]$a }}
$lowercaseChars = $null
for($a = 97; $a -le 122; $a++) { if($noSimilarCharacters -eq $false -or [char][byte]$a -notin $CharsToSkip) {$lowercaseChars += ,[char][byte]$a }}
$digitChars = $null
for($a = 48; $a -le 57; $a++) { if($noSimilarCharacters -eq $false -or [char][byte]$a -notin $CharsToSkip) {$digitChars += ,[char][byte]$a }}
$specialChars = $null
$specialChars += [char]"=", [char]"+", [char]"_", [char]"?", [char]"!", [char]"-", [char]"#", [char]"$", [char]"*", [char]"&", [char]"@"
$TemplateLetters = $null
if($includeLowercaseLetters) { $TemplateLetters += "L" }
if($includeUppercaseLetters) { $TemplateLetters += "U" }
if($includeNumbers) { $TemplateLetters += "N" }
if($includeSpecialChars) { $TemplateLetters += "S" }
$PasswordTemplate = @()
# Set password template, to ensure that required chars are included
do {
$PasswordTemplate.Clear()
for($loop = 1; $loop -le $length; $loop++) {
$PasswordTemplate += $TemplateLetters.Substring((Get-Random -Maximum $TemplateLetters.Length),1)
}
}
while ((
(($includeLowercaseLetters -eq $false) -or ($PasswordTemplate -contains "L")) -and
(($includeUppercaseLetters -eq $false) -or ($PasswordTemplate -contains "U")) -and
(($includeNumbers -eq $false) -or ($PasswordTemplate -contains "N")) -and
(($includeSpecialChars -eq $false) -or ($PasswordTemplate -contains "S"))) -eq $false
)
#$PasswordTemplate now contains an array with at least one of each included character type (uppercase, lowercase, number and/or special)
foreach($char in $PasswordTemplate) {
switch ($char) {
L { $Password += $lowercaseChars | Get-Random }
U { $Password += $uppercaseChars | Get-Random }
N { $Password += $digitChars | Get-Random }
S { $Password += $specialChars | Get-Random }
}
}
return $Password
}
#function configure-yubikey{
#change key, pin, and puk from default setting:
#generate a random number and store it in keepass as USER-YUBIKEY-ManagementKEY must be 16 characters (010203040506070801020304050607080102030405060708 is default)
#$randomkey = get-random -minimum 999999999999999 -maximum 9999999999999999
$randomkey = Generate-Password 48 $false $false $true $false $false
write-host "create a keepass entry named yourusername-YUBIKEY-ManagmentKEY and enter $randomkey in for the value" -ForeGroundColor Red
pause
#generate a random number and store it in keepass as USER-YUBIKEY-PUK, must be 8 characters
#$randompuk = get-random -minimum 9999999 -maximum 99999999
$randompuk = Generate-Password 8 $false $false $true $false $false
write-host "create a keepass entry named yourusername-YUBIKEY-PUK and enter $randompuk in for the value" -ForeGroundColor Red
pause
#prompt user with protected pin entering box:
$pinencrypted = read-host -prompt "What do you want your pin number to be?" -AsSecureString
#extract plain text pin number from encrypted varible above
$BSTR = `
[System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($pinencrypted)
$pin = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR)
#prompt user where the yubico-piv executable is:
$yubiPIVExecutable = Read-OpenFileDialog -InitialDirectory 'C:\Program Files (x86)' -WindowTitle "Select yubico-piv-tool.exe file" -filter "yubico-piv-exe files (yubico-piv-tool.exe)|yubico-piv-tool.exe"
#ask user where PFX file is
$pfxfile = Read-OpenFileDialog -InitialDirectory c:\ -WindowTitle "Select PFX file" -filter "PFX files (*.pfx)|*.pfx"
#prompt user for PFX password:
$PFXpasswordencrypted = read-host -prompt "What is your PFX password?" -AsSecureString
#extract plain text pin number from encrypted varible above
$BSTR = `
[System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($PFXpasswordencrypted)
$PFXpassword = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR)
#changing pin from default(none) to random number generated above:
write-host "setting the yubikey managment key now"
& "$yubiPIVExecutable" -a set-mgm-key -n $randomkey
#changing PIN from default to user's seleciton
write-host "Changing the PIN number now"
& "$yubiPIVExecutable" -k $randomkey -a change-pin -P 123456 -N $pin
#Changing pin from default to random number generated above:
write-host "Changing the PUK number now"
& "$yubiPIVExecutable" -k $randomkey -a change-puk -P 12345678 -N $randompuk
# imports the key
& "$yubiPIVExecutable" --slot 9a --input=$pfxfile --password=$PFXpassword --key-format=PKCS12 --action=set-chuid --action=import-key --action=import-certificate -v2 --key=$randomkey
#}