読者です 読者をやめる 読者になる 読者になる

Windowsでキーサインパーティに参加した時のまとめ & caffっぽいツールを作ってみた

GPG PowerShell

時間がかなり経過してしまいましたが、NSEG#41の白馬合宿 にてキーサインパーティがありました。
自分はWindows7で参加しましたが、問題なくキーサインパーティの一連の作業を行うことができたため、その辺をまとめてみます。


また、キーサインパーティ後の作業については、Debian関係では caff (パッケージ名:signing-party)という便利なツールがありますが、残念ながらWindowsでは動作しません。
仕方ないので手動でやることも考えましたが、漏れがないようにいろいろやるのは手間そうでした。
そこで、PowerShellを兼ねて、Gpg4winをラップしたcaffっぽいツールを作成し、署名付公開鍵をメール送信をしてみました*1 *2 ので、その辺もまとめてみます。


環境

 

キーサインパーティ前の準備

gpg --gen-keyでキーを生成し、キーサーバへアップしたような記憶があります*3
注意点としては、以下のところだったと思います。

  • 鍵の種類は、「(1) RSARSA (デフォルト)」を選ぶ: 署名鍵と暗号鍵を同時に生成*4
  • 鍵長は「4096」を選ぶ: 現在の主流は4096のようです。
  • 期限なし: 延長とかいろいろと面倒そうなので...。


なお、GPG鍵を更新する方法は以下に記載がありました。

 

キーサインパーティ後の作業

以下(と文中でリンクされているpdf)を参考に、作業を行いました。


行う作業は、大きく分けて2つありました。

  1. 公開鍵に署名して送信: キーサインパーティに参加した人の公開鍵をキーサーバより取得し、自分の秘密鍵で署名、それぞれの人へ送信
  2. 署名付公開鍵を受信後: キーサインパーティに参加した人から署名付公開鍵(鍵IDは自分の公開鍵ID)を受け取り、自分の公開鍵の鍵束に取り込み、キーサーバへアップ

 

公開鍵に署名して送信する作業

  1. 「gpg --keyserver <キーサーバ> --recv-key <相手の公開鍵ID>」でキーサーバより相手の公開鍵を取得し、自分の公開鍵の鍵束に入れる
  2. 「gpg --fingerprint <相手の公開鍵ID>」で表示されるフィンガープリントと、手元の書類のフィンガープリントが一致していることを確認
  3. 「gpg --sign-key <相手の公開鍵ID>」で相手の公開鍵に署名*5
  4. 「gpg --export --armor --output <ローカルの出力先> <相手の公開鍵ID>」で署名した公開鍵をエクスポート
  5. エクスポートした公開鍵をメールに添付し、メールを暗号化して相手へ送信


一つの鍵IDごとに上記の作業が必要になることから、やはり caff が欲しくなります。

 

署名付公開鍵を受信した後の作業

  1. (必要に応じて)「gpg <暗号化された添付ファイル>」で、添付ファイルを復号化する
  2. 「gpg --import <公開鍵ファイル>」で、自分の公開鍵の鍵束へ取り込む
  3. 「gpg --send-keys --keyserver <キーサーバ> <自分の公開鍵ID>」で、キーサーバへアップする

 

作ったもの

「caffish-ps」として以下で公開しています。


PowerShell2.0(と任意でThunderbird + Enigmail)のみ必要なので、WindowsXP以降であれば動くかと思います。
なお、不要なコードやコメント・2種類の設定ファイル(xmlとps1)がありますが、自分の学習兼務ということで...。

 

機能

上記の「公開鍵に署名して送信する作業」を自動化しました。


なお、caffっぽくすることを考えたため、以下の機能はあえて実装しませんでした。

  • サーバから取得した公開鍵に対し、信頼度の変更をすること (gpg --edit-key のあとの、trust)
  • PowerShellでメールを送信する際、メールに自分の秘密鍵による署名を付けること


また、実装的にはPowerShellによるGpg4winの単なるラッパーであることから、以下の機能が実現できませんでした。

  • キーサーバから公開鍵を取得する際、自分の公開鍵の鍵束に入れずに処理をすること (上記pdf資料より、caffでは「~/.caff/gnupghome/pubring.gpg」に入れているっぽい)
  • PowerShellでメールを送信する際、メール全体を暗号化すること


他にも、現時点で不要だった以下の仕様は実装していません。

  • 自分が複数のIDをもっている場合、使用するIDを指定すること


独自の仕様は以下の通りです。

  • PowerShellで送信するメールは、本文と添付ファイルを別々に暗号化(本文だけ暗号化しない機能もあり)
  • 日本語のみの表示
  • 暗号化したメールを送信する際はUTF8でのエンコード
  • 作業ディレクトリとして、caffish-ps.ps1のサブディレクトリを作成・使用


これ以降は、作るときに調査したことなどを自分のために残しておきます。
PowerShellThunderbirdまわりの記載とし、caffやGPGまわりは次回にします。

 

PowerShellまわり

自分の調べ方が悪かったようで、MSDNにてPowerShell関連の情報をうまく引き出せなかったため、他のWebサイトへのリンクが多めです。
なお、以下のサイトに文法などがまとまっています。

 

コマンドラインからパラメータ指定の無い引数を受け取る

「$args」で受け取ります。
今回は送信相手のIDを複数受け取る時に使いました。

 

$argsの中身を逐次処理する

いろいろと方法があるかと思いますが、今回はForEach-Objectコマンドレットを使いました。
入力されたオブジェクトは「$_」で参照できるので、変数名を考えなくても済みました。

 

コマンドラインからOn/Offを示すようなパラメータを指定する

Paramにて[switch]でパラメータを指定します。

 

自作のヘルプを作る

PowerShellでは「Get-Help」でコマンドレットのヘルプを呼び出せます。
自作でも呼び出したかったため、以下を参考にファイルの先頭あたりにヘルプ用のテキストを書きました。

 

PowerShellで外部プログラムを実行する

GPGやThunderbirdを実行するには、「&」を使います。

 

PowerShellで外部のps1ファイルにある関数をインクルードする

ドットソース形式(ソース演算子)を使います。

# mail_content.ps1 に記載されている GetMailSubject という関数を使う
. (Join-Path $runningDir "mail_content.ps1")
$mail.Subject = GetMailSubject

 

PowerShellで外部プログラムの実行結果を拾う

$?で直前の実行結果を拾えます($true/$false)

 

PowerShellで参照設定をする

C#ではdllなどをVisualStudioから参照設定すればプログラムの中で使えるようになりますが、PowerShellではその方法が取れません。
PowerShellでは Add-Type コマンドレットを使えば良いようです(昔は「LoadWithPartialName」を使うようでした)。

 

PowerShellで関数を記載する位置

関数を呼び出す前に、その関数を宣言しておく必要があります。
そのため、関数宣言 -> エントリポイント の順番で記述します。

 

PowerShellの変数のスコープ

通常はlocalスコープになるため、function内で呼び出し元の変数を参照できたりします。
ただ、それだと他のプログラム言語と異なっていることから、どこで使っているのかが分かりづらくなりました。
そのため、エントリポイント配下の変数は「$private:hoge」として意図的にスコープを狭め、functionには引数の形で渡しました。

 

.Netの静的メンバへのアクセス

「::演算子」を使い、 [hoge]::fuga のようにアクセスします。

 

現在実行中のスクリプトファイル名やディレクトリを取得します。

 

空ファイルの強制的な上書き

 

PowerShellからXML形式の設定ファイルを読み込む

設定ファイルにはいろいろな形式がありそうですが、PowerShellではXMLが標準で楽に扱えるようになっているため、XMLを使うことにしました*6

 

標準出力への出力をやめる

functionで値を戻す場合、標準出力への出力があると、return時に指定した変数に加え標準出力のデータが戻されてしまいます。
そこで、何か出力されるコマンドレットの場合には、パイプラインの最後に「Out-Null」を指定して標準出力へ出力しないようにしました。

 

配列などの絞込

フィンガープリントからsplit演算子で配列を作り、その配列の中でメールアドレスに一致するものだけに絞り込みたいと考えたとき、LINQのWhere句みたいなものがないかを探してみたところ、「Where-Object(エイリアス:where・?)」がありました。

 

文字列での変数展開とヒアドキュメント

ダブルクォートにより、文字列での変数展開ができます。
また変数展開を含んだヒアドキュメントは「@"」〜「"@」でできるようです。
なお、PowerShellのヒアドキュメントではインデントは利用できないようです。

 

画面でyes/noの選択・入力をする

 

正規表現

match演算子を使います。結果はmatches[0]のようにして取得します。

 

ファイル/ディレクトリの作成/削除

New-ItemとRemove-Item。

 

ファイルパスの結合

Join-Path。

 

ハッシュテーブル関係
# ハッシュテーブルの作成
$hoge = @{fuga = "aaa"}

# 値の取得
$hoge.fuga

# 値の設定
$hoge.piyo = "bbb"

 

パスワードのセキュアな入力
$secure = Read-Host EnterPassword -AsSecureString
$bstr = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($secure)
$password = [System.Runtime.InteropServices.Marshal]::PtrToStringBSTR($bstr)
# BSTR型をクリア
[System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($bstr)

 

PowerShellからGmailを送信する

メールを送信するには、System.Net.MailかSystem.Web.Mailを使えば良いようです(ただし、後者は後方互換のためだけのもの)。
PowerShell3.0ではSend-MailMessageで簡単に送れるようです。


ただし、今回使用するのは2.0なので、「System.Net.Mail.SmtpClient」「System.Net.Mail.MailMessage」「Net.NetworkCredential」まわりを使いました。


なお、System.Net.MailとSystem.Web.Mailでは使うポートが異なりますが、以下によると「EXPLICIT SSL」「IMPLICIT SSL」の違いだそうです。


Web.MailやNet.Mail関連の情報は、こちら。


Web.MailのFieldsで使う名前空間は、こちら。


Web.Mailで使うメールヘッダ関連の情報は、以下が参考になりました。
sendusing=2(SMTPポートをに接続して送信する)や、smtpauthenticate=1(Basic認証)などの意味がわかりました。


他に、DOBON.NETにも両方での使い方についてまとまっていました。

 

.NET4.0以下でtransfer-encodingを7bitにする

.NET4.5では、MailMessage.BodyTransferEncodingプロパティを使えば容易に設定できるようです。
.NET4.0以下では、そのプロパティが無いため、Alternateviewsを使ってtransfer-encodingを設定します。

 

Thunderbirdまわり

Thunderbirdコマンドラインから起動する

composeオプションを使ってexeを叩くと、Toや本文・添付ファイルが設定済の状態で起動できました。

*1:手元のWindows/Ubuntu環境では、文字化けとかは一応大丈夫そうでした

*2:初回はbatchモードで失敗し、署名なしで公開鍵を送信してしまいました...すみません。今回のコードでは直っています

*3:アルコールや眠気があったため、あまり自信がありません...

*4:RSA(署名のみ)だと、caff実行時に暗号化できないなどの不都合があります。

*5:「gpg --edit-key <相手の公開鍵ID>」後の「sign」のショートカット

*6:なお、YAMLであればDotNetYAMLというライブラリがありました