Skip to content

Instantly share code, notes, and snippets.

@silverkorn
Last active March 10, 2023 21:41
Show Gist options
  • Save silverkorn/8da0b5127dde1f74f109414f2ebb38f6 to your computer and use it in GitHub Desktop.
Save silverkorn/8da0b5127dde1f74f109414f2ebb38f6 to your computer and use it in GitHub Desktop.
PowerShell GUI for OpenSSH ASKPASS to have a similar style as PuTTY's Pageant
### Global Varaibles ###
$global:windowsTopMost = $false
$global:returnCode = 0
$global:privateKeyPath = $args[0]
$global:passphraseEnvName = $args[1]
$global:sshJob = $null
$global:sshPassEnvVarName = $null
### Main Functions ###
function OpenSSHKeyFileDialog {
param(
$InitialDirectory
)
[System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") | Out-Null
$openFileDialog = New-Object System.Windows.Forms.OpenFileDialog
#$openFileDialog = (New-Object Microsoft.Win32.OpenFileDialog)
if (Test-Path $InitialDirectory -PathType container) {
$openFileDialog.InitialDirectory = $InitialDirectory
}
$openFileDialog.Title = "Select Private Key File"
$openFileDialog.Filter = "All Files (*.*)| *.*"
$openFileDialog.Multiselect = $false
$openFileDialog.AutoUpgradeEnabled = $true
$openFileDialogWindow = $null
if ($global:windowsTopMost) {
$openFileDialogWindow = (New-Object System.Windows.Forms.Form -Property @{TopMost = $true })
}
if ($openFileDialog.ShowDialog($openFileDialogWindow)) {
return $openFileDialog.FileName
}
return $null
}
function SSHAddKey {
param(
$PrivateKeyPath,
$Passphrase
)
$return=$false
#Invoke-Expression 'ssh-agent'
Start-Process -WindowStyle Hidden ssh-agent
$global:sshJob = Start-Job -ScriptBlock {
$encodedPassphrase = [Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes($Using:Passphrase))
$global:sshPassEnvVarName = "SSH_TEMP_PASS_$((( 1..16 | foreach-object {'{0:x}' -f (Get-Random -Maximum 16)}) -join '').toUpper())"
Set-Item -Path "env:$global:sshPassEnvVarName" -Value $encodedPassphrase
$Env:DISPLAY="0"
$Env:SSH_ASKPASS="$Env:COMSPEC /c powershell.exe -NoLogo -NonInteractive -F `"$Using:PSCommandPath`" `"$Using:PrivateKeyPath`" `"$global:sshPassEnvVarName`""
$global:LASTEXITCODE = 0
Invoke-Expression 'ssh-add "$Using:PrivateKeyPath" 2> $null > $null'
Write-Output $global:LASTEXITCODE
if (Test-Path -Path "env:$global:sshPassEnvVarName"){
Remove-Item -Path "env:$global:sshPassEnvVarName"
}
$global:sshPassEnvVarName = $null
}
$global:sshJob | Wait-Job -Timeout 3 # Unfortunately, 1 second is too low and might returns false-positive
if ($global:sshJob.JobStateInfo.State -Eq "Running"){
Remove-Job -Force -InstanceId $global:sshJob.InstanceId > $null
return $false
} else {
Receive-Job $global:sshJob -OutVariable sshJobOutput > $null
if ([String]$sshJobOutput -Eq "0"){
$return=$true
} else {
return $false
}
}
return $return
}
### Main Process ###
if (([string]::IsNullOrEmpty($global:privateKeyPath)) -Or -Not (Test-Path $global:privateKeyPath -PathType leaf)){
if (Test-Path "$env:USERPROFILE\.ssh" -PathType container){
$global:privateKeyPath = (OpenSSHKeyFileDialog -InitialDirectory "$env:USERPROFILE\.ssh")
} else {
$global:privateKeyPath = ({ OpenSSHKeyFileDialog })
}
if (([string]::IsNullOrEmpty($global:privateKeyPath)) -Or -Not (Test-Path $global:privateKeyPath -PathType leaf)){
Exit 1
}
}
if (-Not ([string]::IsNullOrEmpty($global:passphraseEnvName))){
$decodedPassphrase = [System.Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String((Get-Item -Path "env:$global:passphraseEnvName").Value))
Write-Output $decodedPassphrase
Exit $global:returnCode
}
Add-Type -AssemblyName System.Windows.Forms
[System.Windows.Forms.Application]::EnableVisualStyles()
$frmPassphrase = New-Object system.Windows.Forms.Form
$frmPassphrase.ClientSize = New-Object System.Drawing.Point(318,120)
$frmPassphrase.text = "OpenSSH Agent Passphrase"
$frmPassphrase.TopMost = $global:windowsTopMost
$frmPassphrase.MaximizeBox = $false
$frmPassphrase.MinimizeBox = $false
$frmPassphrase.FormBorderStyle = 'Fixed3D'
$frmPassphrase.StartPosition = "CenterScreen"
$frmPassphrase.KeyPreview = $true
$frmPassphrase.ShowIcon = $false
$frmPassphrase.Add_KeyDown({if($_.KeyCode -eq "Enter"){ok($txtPassphrase.Text)}})
$frmPassphrase.Add_KeyDown({if($_.KeyCode -eq "Escape"){cancel}})
$lblPassphrase = New-Object system.Windows.Forms.Label
$lblPassphrase.text = "Passphare for `"" + (Split-Path $global:privateKeyPath -leaf) + "`":"
$lblPassphrase.AutoSize = $true
$lblPassphrase.width = 25
$lblPassphrase.height = 10
$lblPassphrase.location = New-Object System.Drawing.Point(10,20)
$txtPassphrase = New-Object system.Windows.Forms.MaskedTextBox
$txtPassphrase.multiline = $false
$txtPassphrase.width = 298
$txtPassphrase.height = 20
$txtPassphrase.location = New-Object System.Drawing.Point(10,42)
$txtPassphrase.PasswordChar = ''
$btnOk = New-Object system.Windows.Forms.Button
$btnOk.text = "OK"
$btnOk.width = 74
$btnOk.height = 30
$btnOk.location = New-Object System.Drawing.Point(157,77)
$btnCancel = New-Object system.Windows.Forms.Button
$btnCancel.text = "Cancel"
$btnCancel.width = 73
$btnCancel.height = 30
$btnCancel.location = New-Object System.Drawing.Point(235,77)
$frmPassphrase.controls.AddRange(@($btnOk,$btnCancel,$lblPassphrase,$txtPassphrase))
$btnOk.Add_Click({ ok($txtPassphrase.Text) })
$btnCancel.Add_Click({ cancel })
function ok ($text) {
$btnOk.enabled = $false
$btnCancel.enabled = $false
$txtPassphrase.enabled = $false
$sshPassphraseSuccess = (SSHAddKey -PrivateKeyPath $global:privateKeyPath -Passphrase $text)
if ( $sshPassphraseSuccess -Eq $true){
# Good!
$frmPassphrase.Close()
}
$btnOk.enabled = $true
$btnCancel.enabled = $true
$txtPassphrase.enabled = $true
$txtPassphrase.text = ""
$frmPassphrase.Activate()
$txtPassphrase.Focus()
}
function cancel() {
$global:returnCode = 1
$frmPassphrase.Close()
}
$frmPassphrase.Add_Shown({$frmPassphrase.Activate(); $txtPassphrase.Focus()})
[void]$frmPassphrase.ShowDialog()
Exit $global:returnCode
Dim objShell, objFSO, objFile, strArguments
Set objShell = WScript.CreateObject("WScript.shell")
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.GetFile(WScript.ScriptFullName)
For Each strArgument in WScript.Arguments
strArguments = strArguments & " " & strArgument
Next
objShell.Run "powershell.exe -WindowStyle Hidden -File " & """" & objFSO.GetParentFolderName(objFile) & "\" & objFSO.GetBaseName(WScript.ScriptFullName) & ".ps1""" & strArguments, 0, False
@silverkorn
Copy link
Author

silverkorn commented Feb 9, 2021

Copy both files and launch by using the .vbs script to start with the hidden terminal.

Install :

cd /D "%USERPROFILE%"
powershell "$w=(new-object Net.WebClient);$w.Encoding=[System.Text.Encoding]::UTF8;$w.DownloadString('https://gist.githubusercontent.com/silverkorn/8da0b5127dde1f74f109414f2ebb38f6/raw/ssh-askpass-gui.ps1') | Set-Content -Encoding UTF8 '%USERPROFILE%\ssh-askpass-gui.ps1'"
powershell "$w=(new-object Net.WebClient);$w.Encoding=[System.Text.Encoding]::ASCII;$w.DownloadString('https://gist.githubusercontent.com/silverkorn/8da0b5127dde1f74f109414f2ebb38f6/raw/ssh-askpass-gui.vbs') | Set-Content -Encoding ascii '%USERPROFILE%\ssh-askpass-gui.vbs'"

Run :

"%USERPROFILE%\ssh-askpass-gui.vbs" "%USERPROFILE%\.ssh\id_rsa"

Launch on user startup:

powershell "$s=(New-Object -COM WScript.Shell).CreateShortcut('%USERPROFILE%\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup\OpenSSH AskPass GUI.lnk');$s.TargetPath='"""%USERPROFILE%\ssh-askpass-gui.vbs"""';$s.Arguments='"""%USERPROFILE%\.ssh\id_rsa"""';$s.IconLocation='C:\Windows\System32\wmploc.DLL, 64';$s.Save()"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment