id:eel3:20130526:1369569828 の続き的なネタ。
GnuPGで公開鍵暗号方式でファイルを暗号化する場合、使用する公開鍵を指定しなくてはならない。
復号化する相手が決まりきっている場合は、バッチファイルに使用する公開鍵まで含めてコマンドを記述しておき、そのショートカットを「送る」メニューに登録すればよい。
@echo off gpg.exe -r myself@example.com -r you@example.com --encrypt-files %*
この方法なら、暗号化したいファイルを「送る」のショートカット経由で一発で暗号化できる。
しかし使用する公開鍵がケースバイケースで変化するような場合、公開鍵を選択する仕組みが必要となる。
そこでPowerShell + WPF + XAMLでGUIラッパーアプリを書いてみた。GnuPTやGpg4winは、ちょっとばかりオーバースペックなので。
# You must execute this script in STA mode
# $ powershell -Sta -File <this script> [file ...]
Set-StrictMode -version Latest
Add-Type -assemblyName PresentationFramework
Add-Type -assemblyName System.Windows.Forms
Set-Variable -name APP_TITLE -value 'Encrypt files with GPG' -option Constant
function Notify-Message ([string] $message = '')
{
[Void][System.Windows.Forms.MessageBox]::Show($message, $APP_TITLE);
}
function Get-GpgKeyList
{
$list = @{}
$nkeys = 0
gpg.exe --batch --no-tty --fixed-list-mode --list-keys | %{
if ($_ -cmatch '^uid\s+(?<name>[\w ]+) <(?<mail>\S+)>$') {
$list[$Matches.mail] = @{
name = $Matches.name
mail = $Matches.mail
checked = $False
}
$nkeys++
}
} | Out-Null
$list, $nkeys
}
function Escape-Xml ([string] $s)
{
[System.Security.SecurityElement]::Escape($s)
}
function ConvertTo-CheckBoxTag
{
begin {$nr = 0}
process {
$nr++
-join ('<CheckBox Name="', "checkBox$nr", '" ',
'Content="', (Escape-Xml $_), '" ',
'HorizontalAlignment="Left" ',
'DockPanel.Dock="Top" />')
}
}
function Make-XamlTags ([hashtable] $keyList)
{
$tags = $keyList[$keyList.Keys] |
%{"$($_.name) <$($_.mail)>"} |
Sort-Object |
ConvertTo-CheckBoxTag
@"
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="$APP_TITLE"
Width="400" Height="300"
>
<DockPanel>
<Button Name="encryptButton" Content="Encrypt" DockPanel.Dock="Bottom" />
<ScrollViewer HorizontalScrollBarVisibility="Auto">
<DockPanel LastChildFill="False">
$tags
</DockPanel>
</ScrollViewer>
</DockPanel>
</Window>
"@
}
function Show-MainWindow
{
$key_list, $nkeys = Get-GpgKeyList
if ($nkeys -le 0) {
Notify-Message 'No GPG keys found.'
exit 1
}
$xaml = Make-XamlTags $key_list
$window = [System.Windows.Markup.XamlReader]::Parse($xaml)
$button = $window.FindName('encryptButton')
$button.Add_Click({
$keys = $key_list[$key_list.Keys] |
?{$_.checked} |
%{'-r', $_.mail}
if (($keys | Measure-Object).Count -eq 0) {
Notify-Message 'No GPG keys selected.'
return
}
$script:public_keys = $keys
$script:do_encrypt = $True
$window.Close()
})
1..$nkeys | %{
$check_box = $window.FindName("checkBox$_")
$check_box.Add_Checked({
param([System.Windows.Controls.CheckBox] $cb)
if ($cb.Content -cmatch '^[\w ]+ <(?<mail>\S+)>$') {
$key_list[$Matches.mail].checked = $True
}
})
$check_box.Add_Unchecked({
param([System.Windows.Controls.CheckBox] $cb)
if ($cb.Content -cmatch '^[\w ]+ <(?<mail>\S+)>$') {
$key_list[$Matches.mail].checked = $False
}
})
}
$window.ShowDialog() | Out-Null
}
if (($Args | Measure-Object).Count -eq 0) {
Notify-Message 'No files specified.'
exit 1
}
$Args | %{
if (-not (Test-Path -literalPath $_ -pathType Leaf)) {
Notify-Message "file not found: '$_'"
exit 1
}
}
$public_keys = $Null
$do_encrypt = $False
Show-MainWindow
if ($do_encrypt) {
gpg.exe $public_keys --encrypt-files $Args
exit $LastExitCode
}GnuPGに登録されている公開鍵の情報を取り出す部分の正規表現は適当なので、他の環境では問題があると思う。名前に記号文字が含まれていなくて且つコメントを登録していない環境向け、なはず。
引数に指定されたファイルの存在チェックをしているけど、「送る」メニューから使う想定なのでワイルドカードのことは考えていない。
さて、このPowerShellのスクリプトのショートカットを「送る」メニューに登録しても動かないので、こんな内容のバッチファイルを作成してPowerShellのスクリプトと同じフォルダに配置して、そのショートカットを登録しておく。
@echo off start powershell -ExecutionPolicy RemoteSigned -Sta -WindowStyle Hidden -File %~dp0encrypt.ps1 %*
WPFを使うために「-Sta」を、GUIだけを見せる為に「-WindowStyle Hidden」を指定している。あとこのバッチファイル自体も即座に終了させる為にstartコマンドを使用している。
実際に使用すると、コマンドプロンプトが一瞬表示される現象が2回発生する。ちょっと微妙かもしれない。