時間がかなり経過してしまいましたが、NSEG#41の白馬合宿 にてキーサインパーティがありました。
自分はWindows7で参加しましたが、問題なくキーサインパーティの一連の作業を行うことができたため、その辺をまとめてみます。
また、キーサインパーティ後の作業については、Debian関係では caff (パッケージ名:signing-party)という便利なツールがありますが、残念ながらWindowsでは動作しません。
仕方ないので手動でやることも考えましたが、漏れがないようにいろいろやるのは手間そうでした。
そこで、PowerShellを兼ねて、Gpg4winをラップしたcaffっぽいツールを作成し、署名付公開鍵をメール送信をしてみました*1 *2 ので、その辺もまとめてみます。
キーサインパーティ前の準備
gpg --gen-keyでキーを生成し、キーサーバへアップしたような記憶があります*3。
注意点としては、以下のところだったと思います。
- 鍵の種類は、「(1) RSA と RSA (デフォルト)」を選ぶ: 署名鍵と暗号鍵を同時に生成*4
- 鍵長は「4096」を選ぶ: 現在の主流は4096のようです。
- 期限なし: 延長とかいろいろと面倒そうなので...。
なお、GPG鍵を更新する方法は以下に記載がありました。
キーサインパーティ後の作業
以下(と文中でリンクされているpdf)を参考に、作業を行いました。
行う作業は、大きく分けて2つありました。
- 公開鍵に署名して送信: キーサインパーティに参加した人の公開鍵をキーサーバより取得し、自分の秘密鍵で署名、それぞれの人へ送信
- 署名付公開鍵を受信後: キーサインパーティに参加した人から署名付公開鍵(鍵IDは自分の公開鍵ID)を受け取り、自分の公開鍵の鍵束に取り込み、キーサーバへアップ
公開鍵に署名して送信する作業
- 「gpg --keyserver <キーサーバ> --recv-key <相手の公開鍵ID>」でキーサーバより相手の公開鍵を取得し、自分の公開鍵の鍵束に入れる
- 「gpg --fingerprint <相手の公開鍵ID>」で表示されるフィンガープリントと、手元の書類のフィンガープリントが一致していることを確認
- 「gpg --sign-key <相手の公開鍵ID>」で相手の公開鍵に署名*5
- 「gpg --export --armor --output <ローカルの出力先> <相手の公開鍵ID>」で署名した公開鍵をエクスポート
- エクスポートした公開鍵をメールに添付し、メールを暗号化して相手へ送信
一つの鍵IDごとに上記の作業が必要になることから、やはり caff が欲しくなります。
署名付公開鍵を受信した後の作業
- (必要に応じて)「gpg <暗号化された添付ファイル>」で、添付ファイルを復号化する
- 「gpg --import <公開鍵ファイル>」で、自分の公開鍵の鍵束へ取り込む
- 「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のサブディレクトリを作成・使用
これ以降は、作るときに調査したことなどを自分のために残しておきます。
PowerShell・Thunderbirdまわりの記載とし、caffやGPGまわりは次回にします。
PowerShellまわり
自分の調べ方が悪かったようで、MSDNにてPowerShell関連の情報をうまく引き出せなかったため、他のWebサイトへのリンクが多めです。
なお、以下のサイトに文法などがまとまっています。
- 関数、フィルタ、スクリプト - Windows PowerShell | ++C++; // 未確認飛行 C
- PowerShell基礎文法最速マスター - PowerShell Scripting Weblog
$argsの中身を逐次処理する
いろいろと方法があるかと思いますが、今回はForEach-Objectコマンドレットを使いました。
入力されたオブジェクトは「$_」で参照できるので、変数名を考えなくても済みました。
コマンドライン引数をタブ補完できるようにする
Paramステートメントを使います。なお、スクリプトの先頭に記載する必要があるようです。
- [PowerShell] PS1 ファイル実行時にとる引数をコマンドレットみたいにタブ補完したい! - 管理者は見た!~AD と ILM 一家の秘密~ - Site Home - TechNet Blogs
- How to handle command-line arguments in PowerShell - Stack Overflow
自作のヘルプを作る
PowerShellでは「Get-Help」でコマンドレットのヘルプを呼び出せます。
自作でも呼び出したかったため、以下を参考にファイルの先頭あたりにヘルプ用のテキストを書きました。
PowerShellで外部のps1ファイルにある関数をインクルードする
ドットソース形式(ソース演算子)を使います。
# mail_content.ps1 に記載されている GetMailSubject という関数を使う . (Join-Path $runningDir "mail_content.ps1") $mail.Subject = GetMailSubject
PowerShellで参照設定をする
C#ではdllなどをVisualStudioから参照設定すればプログラムの中で使えるようになりますが、PowerShellではその方法が取れません。
PowerShellでは Add-Type コマンドレットを使えば良いようです(昔は「LoadWithPartialName」を使うようでした)。
関数名の付け方のガイドラインとコーディングスタイル
PowerShellの変数のスコープ
通常はlocalスコープになるため、function内で呼び出し元の変数を参照できたりします。
ただ、それだと他のプログラム言語と異なっていることから、どこで使っているのかが分かりづらくなりました。
そのため、エントリポイント配下の変数は「$private:hoge」として意図的にスコープを狭め、functionには引数の形で渡しました。
PowerShellからXML形式の設定ファイルを読み込む
設定ファイルにはいろいろな形式がありそうですが、PowerShellではXMLが標準で楽に扱えるようになっているため、XMLを使うことにしました*6。
標準出力への出力をやめる
functionで値を戻す場合、標準出力への出力があると、return時に指定した変数に加え標準出力のデータが戻されてしまいます。
そこで、何か出力されるコマンドレットの場合には、パイプラインの最後に「Out-Null」を指定して標準出力へ出力しないようにしました。
配列などの絞込
フィンガープリントからsplit演算子で配列を作り、その配列の中でメールアドレスに一致するものだけに絞り込みたいと考えたとき、LINQのWhere句みたいなものがないかを探してみたところ、「Where-Object(エイリアス:where・?)」がありました。
文字列での変数展開とヒアドキュメント
ダブルクォートにより、文字列での変数展開ができます。
また変数展開を含んだヒアドキュメントは「@"」〜「"@」でできるようです。
なお、PowerShellのヒアドキュメントではインデントは利用できないようです。
ファイル/ディレクトリの作成/削除
New-ItemとRemove-Item。
ファイルパスの結合
Join-Path。
パスワードのセキュアな入力
$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」まわりを使いました。
- Blogs - フィールドSEあがりの安納です - Site Home - TechNet Blogs
- PowerShell でメール送信 | NYORO PRESS
- まこちの技術情報覚え書き PowerShellでメール送信
- c# - Sending email in .NET through Gmail - Stack Overflow
なお、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にも両方での使い方についてまとまっていました。
- SMTP認証でメールを送信する: .NET Tips: C#, VB.NET
- SSL/TLSを使用してSMTPでメールを送信する: .NET Tips: C#, VB.NET
- Reply-To、Sender、X-Mailerなどのヘッダを追加してメールを送信する: .NET Tips: C#, VB.NET
- Reply-To、Sender、X-Mailerなどのヘッダを追加してメールを送信する: .NET Tips: C#, VB.NET
.NET4.0以下でtransfer-encodingを7bitにする
.NET4.5では、MailMessage.BodyTransferEncodingプロパティを使えば容易に設定できるようです。
.NET4.0以下では、そのプロパティが無いため、Alternateviewsを使ってtransfer-encodingを設定します。
Thunderbirdまわり
Thunderbirdをコマンドラインから起動する
composeオプションを使ってexeを叩くと、Toや本文・添付ファイルが設定済の状態で起動できました。