qshinoの日記

Powershell関係と徒然なこと

powershell証明書暗号化

※ この投稿は PowerShell Advent Calendar 2011 に参加しています。

証明書というと、つい SSL で使用するサーバー証明書を思い浮かべてしまいますが、パスワードを暗号化してどこかに格納しておきたいとか、パスワードを誰かに伝えるために暗号化して渡したい..なんていうシチューエションにも証明書が使われることがあります。

パスワードを暗号化する場合、シードと呼ばれる文字列を決めておき、これを用いて暗号化するという方法が良く使われますが、この方法の場合、シード自身の漏えいを配慮する必要があります。そこで、よく使われるのが証明書を使用してパスワードの暗号化する方法です。証明書を使用する方法であれば、証明書がインストールされていなければ復号化することもできないため、文字列のシードで管理するよりは安全性が高まります。Windows Azure なんかでも、この方法を使用したパスワードの暗号化を要求されることが多く、IT Pro にとっても身に着けておきたい手法だと言えます。

証明書による暗号化は、PowerShell を使用すると簡単です。

1.証明書の準備

まずは暗号化に使用する証明書を作成しましょう。

証明書の作成は PowerShell ではなく、通常のコマンドを使用します。

ここでは自己署名証明書を使用することにします。自己署名証明書を作成する方法はいくつもありますが、代表的な方法は以下の2通りです。

IIS の管理コンソールで作成する
Makecert コマンドを使用する
前者の場合、作成できる主体名は自分自身のみなので、ちょっと自由度が落ちるんですよね。ま、解凍できればよいので何でも構わないといえば構わないのですが…。

それよりも、Makecert コマンドを覚えて置いたほうが今後役に立ちます。Makecert コマンドは、Windows SDK に含まれているので、これをインストールしておく必要があることに注意してください。

Windows SDK for Windows 7 | MSDN

makecert コマンドは、以下のような比較的深いパスにインストールされています。

C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin
C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin\x64

通常のコマンドプロンプトPowerShellコンソールではパスが通っていないので、一度上記のフォルダに移動する必要があるので注意しましょう。

Makecert コマンドで証明書を作成するための書式は以下の通りです。以下の書式では、CN=Encpassword という主体名の証明書を、ハッシュアルゴリズム sha1 を使用して 2048ビット長のキーで暗号化し、EncPassword.cer というファイル名で保存しています。

makecert -sky exchange -r -n "CN=Encpassword" -pe -a sha1 -len 2048 -b 12/15/2012 -e 12/30/2013 -sr currentuser -ss My ”EncPassword.cer" Succeeded
-sr currentuser –ss My は、作成したパスワードのインストール先です。currentuser は「現在のユーザー」、Myは「個人フォルダ」を示しています。指定せずに後から自分でインストールすることもできます。

-b 12/15/2012 -e 12/30/2013 はパスワードの有効期間です。-b が開始、-e が終了の年月です。

上記のコマンドを実行すると、以下のように証明書が作成されます。

image

image

長くなってしまいましたが、これで証明書の準備は完了です。

2.PowerShell を使用して暗号化

まずは以下のスクリプトをご覧ください。これがパスワードを暗号化するためのスクリプトです。

$PlainPassword = “<パスワード>"
$thumbprint = “<Thumbprint>"
$Cert = get-item cert:\CurrentUser\MY\$thumbprint
Add-type –AssemblyName System.Security
$pass = [Text.Encoding]::UTF8.GetBytes($PlainPassword)
$content = new-object Security.Cryptography.Pkcs.ContentInfo –argumentList (,$pass)
$env = new-object Security.Cryptography.Pkcs.EnvelopedCms $content
$env.Encrypt*1
事前に用意しておくのは、パスワードとなる文字列と、証明書の拇印(Thumbprint)です。

Thumbprint は、MMC の「証明書」で確認することもできますが、せっかくなので PowerShell で確認してみましょう。

PowerShell のコンソールから、以下のように入力してください。

PS C:\> cd cert:
PS cert:\> dir
Location : CurrentUser
StoreNames : {SmartCardRoot, UserDS, AuthRoot, CA…}
Location : LocalMachine
StoreNames : {CA, AuthRoot, NGO Certificate Store, TrustedPeople…}
cd コマンドはファイルシステムディレクトリ移動に使用するものと相場は決まっていますが、cert: と指定することで、証明書ストアに移動することができます。CurrentUser および LocalMachine は、MMCコンソールの「証明書 – 現在のユーザー」「証明書(ローカル コンピューター)」に相当します。

image

もうおわかりですね。あとは、以下のようにして EncPassword 証明書の Thumbprint を取得できます。

PS cert:> cd .\CurrentUser
PS cert:\CurretnUser> cd .\My
PS cert:\CurrentUser\My> dir
ディレクトリ: Microsoft.PowerShell.Security\Certificate::CurrentUser\My
Thumbprint Subject
———- ——-
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx CN=Junichi Anno
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx CN=Windows Azure Tools
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx CN=Windows Azure Tools
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx CN=junichia



PS cert:\CurrentUser\My> dir | Where-Object {$_.Subject -eq "CN=encpassword"}
ディレクトリ: Microsoft.PowerShell.Security\Certificate::CurrentUser\My
Thumbprint Subject
———- ——-
13B97E99532151A1FC2617636CB04FE07C43C6C2 CN=Encpassword

暗号化したいパスワドードを「password」とし、Thumbprint と共に先のスクリプトに埋め込むと以下のようになります。

$PlainPassword = "password"
$thumbprint = "13B97E99532151A1FC2617636CB04FE07C43C6C2"
$Cert = get-item cert:\CurrentUser\MY\$thumbprint
## 暗号化に必要な System.Security アセンブリを読み込む
Add-type –AssemblyName System.Security
## 生パスワードをバイト型に変換
$pass = [Text.Encoding]::UTF8.GetBytes($PlainPassword)
## バイト型に変換したパスワードを CMS/PKCS #7 ContentInfo データ構造体と呼ばれる
## データとして格納します。つまり、所定の封筒に入れるのに合った形に成形している。
$content = new-object Security.Cryptography.Pkcs.ContentInfo –argumentList (,$pass)
## ContentInfo データをエンベロープデータとして格納。
## 要は、相手に渡したいパスワードデータを封筒に入れている。
$env = new-object Security.Cryptography.Pkcs.EnvelopedCms $content
## エンベロープの受取人の証明書を用いて、エンベロープを暗号化
## つまり、データ入れた封筒が誰からも開封できないように証明書で封印している
$env.Encrypt*2
これをこのまま PowerShell コンソールにコピペすれば、あっというまに以下のような暗号化されたパスワードが出力されます。

MIIBlQYJKoZIhvcNAQcDoIIBhjCCAYICAQAxggFGMIIBQgIBADAqMBYxFDASBgNVBAMTC0VuY3Bhc3N
3b3JkAhBDKAdUePxKgU2KTFX0C+3AMA0GCSqGSIb3DQEBAQUABIIBADIaINybKk4XmzFEKGaYTuPZ
9QO8BceJi/E1zDWT+Um9aeGCcXJ+JZSftTBvVsxKuX310O0RZMBUu7RmuDFLG14pRQd4alESp6vda6j9
qIjNxE3ZK9qnQJhepQY8cujt84PpISeOOPuVQqTJ8k9cny0qmyDgMSWa0S4TDMWb/njbvTDT8ShBAcqn lHqEnKHgfeBXPARxKwVcaFSIqAy4oxxm+qOR9jQYeov2Tlhm9sdO6uULFXx19PUYDUGTYHYPIyA25oeV
8CRP7VmEOHTATeyWbECGGICFG1BPhu9XuB/Txc8uI6RKC4H3C5AuJu5Kdp8ZFSIc6jDZp+mWckpm3+
4wMwYJKoZIhvcNAQcBMBQGCCqGSIb3DQMHBAjZBRa1G6lvL4AQyJm6YKTXKG1ukiiaq95r6Q==
出力されたパスワードには改行が入っていますが、通常は以下のように改行を全て削除して使用します(右側がはみ出ていて見えないと思いますが)。

3b3JkAhBDKAdUePxKgU2KTFX0C+3AMA0GCSqGSIb3DQEBAQUABIIBADIaINybKk4XmzFEKGaYTuPZ9QO8BceJi/E1zDWT+Um9aeGCcXJ+JZSftTBvVsxKuX310O0RZMBUu7RmuDFLG14pRQd4alESp6vda6j9qIjNxE3ZK9qnQJhepQY8cujt84PpISeOOPuVQqTJ8k9cny0qmyDgMSWa0S4TDMWb/njbvTDT8ShBAcqnlHqEnKHgfeBXPARxKwVcaFSIqAy4oxxm+qOR9jQYeov2Tlhm9sdO6uULFXx19PUYDUGTYHYPIyA25oeV8CRP7VmEOHTATeyWbECGGICFG1BPhu9XuB/Txc8uI6RKC4H3C5AuJu5Kdp8ZFSIc6jDZp+mWckpm3+4wMwYJKoZIhvcNAQcBMBQGCCqGSIb3DQMHBAjZBRa1G6lvL4AQyJm6YKTXKG1ukiiaq95r6Q==

以上でパスワードの暗号化は完了です。

では、受け取った相手はこいつをどうやって復号化かすればよいのでしょう?

それは次回の投稿にて。

*1:new-object System.Security.Cryptography.Pkcs.CmsRecipient($Cert)))
[Convert]::ToBase64String($env.Encode(

*2:new-object System.Security.Cryptography.Pkcs.CmsRecipient($Cert)))
##暗号化した封筒をBase64エンコードして文字列に変換
[Convert]::ToBase64String($env.Encode(