PKCS #12 個人情報交換ファイルフォーマットについて


# PKCS #12 個人情報交換ファイルとは
 **個人情報交換ファイル** は PKCS #12 **Personal Information Exchange Syntax Standard** という規格で定義されたファイルです。
 
PKCS #12という規格は、[英語版Wikipedia](http://en.wikipedia.org/wiki/PKCS)によると以下のように説明されています。

> See RFC 7292. Defines a file format commonly used to store private keys with accompanying public key certificates, protected with a password-based symmetric key. PFX is a predecessor to PKCS #12.

訳してみます。
> RFC 7292を参照。パスワードベースの対象鍵による保護のもと、秘密鍵をそれと関連する公開鍵証明書とともに保存するために使われるファイルフォーマットを規定しています

RSAでは秘密鍵と公開鍵のペアを生成しますが、個々の鍵がどのようなファイルフォーマットで保存されるかは、[こちらの投稿](http://qiita.com/kunichiko/items/2e0a2bd35c8e9492ceb5)で解説しました。その記事内で説明したフォーマットは個々の鍵のフォーマットについてですが、複数の鍵をまとめて保管したり、秘密鍵と公開鍵をペアで保存したりしたいことがあります。その要求を満たすため、 **様々な鍵を１つのファイルにまとめて保存するためのフォーマットが PKCS #12です。**

なお、PKCS #12のPersonal Information Exchangeは日本語では **個人情報交換** と呼ばれています。Mac OS Xの キーチェーンアクセスで鍵や証明書を exportするとき、ファイルフォーマットとして「個人情報交換 (.p12)」という選択肢が現れますが、これがまさに PKCS #12ファイルフォーマットのことです。

![スクリーンショット 2015-01-14 11.13.41.png](https://qiita-image-store.s3.amazonaws.com/0/41616/89debd3b-57b3-a95d-c579-144894bece97.png "スクリーンショット 2015-01-14 11.13.41.png")

## opensslコマンドで、秘密鍵と公開鍵(証明書)を PKCS #12ファイルにまとめる

```openssl pkcs12```コマンドを使うと、PEMエンコードされた秘密鍵を PKCS #12形式に変換することができます。秘密鍵がDERエンコードされている場合は、事前にPEMエンコードに変換してください。

```
> openssl pkcs12 -export -in public-key.crt -inkey private-key.pem -out my-identity.p12
Enter Export Password:
Verifying - Enter Export Password:
```

コマンドを実行するとパスワードの入力が求められます。個人情報交換ファイルは秘密鍵を含むため、全体がこのパスワードで暗号化されて保存されます。このパスワードはプログラムから読み込む際に必要になりますので忘れないようにしてください。

また、上記コマンドでは public-key.crt という公開鍵(証明書)の指定が必要ですので、[こちらのエントリ](http://qiita.com/kunichiko/items/12cbccaadcbf41c72735)などを参照して、あらかじめ用意しておいてください。証明書は自己署名証明書で構いません。

### PKCS #12ファイルに秘密鍵だけを保存する

証明書が必要ない場合は -nocerts オプションを指定することで秘密鍵だけを保存することもできます。

```
> openssl pkcs12 -export -nocerts -inkey private-key.pem -out my-identity.p12
Enter Export Password:
Verifying - Enter Export Password:
```

## opensslコマンドで、PCKS #12ファイルの中身を調べる

PKCS #12ファイルにどんなものが格納されているかを確認したい場合も ```openssl pkcs12``` コマンドが使えます。 -inでPKCS #12ファイルを指定し、-infoを指定するとファイルの構造が表示されます。その際、-nooutを指定しないと鍵そのものも一緒に出力しようとするので注意してください。

```
> openssl pkcs12 -in my-identity.p12 -noout -info
Enter Import Password:
MAC Iteration 2048
MAC verified OK
PKCS7 Encrypted data: pbeWithSHA1And40BitRC2-CBC, Iteration 2048
Certificate bag
PKCS7 Data
Shrouded Keybag: pbeWithSHA1And3-KeyTripleDES-CBC, Iteration 2048
```

パスワードを入力すると上記のようなダンプが出力されます。出力が読みにくいですが、PKCS #7形式の以下の２つのデータが含まれていることがわかります。

- PKCS7 Encrypted data (pbe with SHA1 and 40Bit RC2 (CBC), Iteration 2048
	- Certificate bag
- PKCS7 Data
	- Shrouded Keybag (pbe with SHA1 and 3-key Triple DES (CBC), Iteration 2048

一つ目は「Certificate bag」とあるので、証明書のことのようです。pbeは「Password Base Encryption」という意味で、パスワードで暗号化されていることを示しています。RC2が暗号アルゴリズムで、ビット長が40ビット。CBCは「Cipher-Block Chaining」で、暗号利用モードを示しています。暗号利用モードとは、ブロックベースの暗号アルゴリズムにおいて、ブロックサイズ以上のデータを暗号化する際のルールです。

二つ目は「Shrouded Keybag」とあり、鍵が入っているようです。こちらもパスワード暗号化されており、 **3-key Triple DES**が使われています。

正直もう少しわかりやすい出力にして欲しいところですが、こんな感じで、証明書と鍵が入っていることがわかりました。

他のファイルではどうかということで、試しに鍵だけが含まれるファイルを表示してみると以下のようになりました。

```
> openssl pkcs12 -in my-identity-nocert.p12 -noout -info
Enter Import Password:
MAC Iteration 2048
MAC verified OK
PKCS7 Data
Shrouded Keybag: pbeWithSHA1And3-KeyTripleDES-CBC, Iteration 2048
```

## opensslコマンドで、PKCS #12ファイルから証明書を取り出す

以下のコマンドで証明書を取り出すことができます。

```
> openssl pkcs12 -in my-identity.p12 -clcerts -nokeys -out public-key.crt
```

- -clcerts 証明書のみを取り出す
- -nokeys 秘密鍵を出力しない

## opensslコマンドで、PKCS #12ファイルから秘密鍵を取り出す

以下のコマンドで秘密鍵を取り出すことができます。

```
> openssl pkcs12 -in my-identity.p12 -nocerts -nodes -out private-key.pem
```

- -nocerts 証明書を出力しない
- -nodes 秘密鍵を暗号化せずに出力する

## opensslコマンドで、PKCS #12ファイルのパスワードを変更する

上記 opensslコマンドや Keychainで作成した .p12ファイルのパスフレーズを別のものに変更したい場合がありますが、意外と面倒です。

openssl コマンドでも一発ではできず、以下のように一度 .pem ファイルに変換して再度 .p12ファイルを生成する方法しかないようです。

### 一度中間ファイル `temp.pem` に書き出す
```
> openssl pkcs12 -in my-identity-old.p12 -passin pass:oldpassword -out temp.pem -nodes
```

### 中間ファイルを再度 .p12 に書き出す

```
> openssl pkcs12 -export -in temp.pem -out my-identity-new.p12 -passout pass:newpassword
```

### パスフレーズの指定について
上記例の `-passin`や`-passout` はパスワードを引数で与えるものです。これを使う場合、パスワードはちょっと変わったフォーマットで指定する必要がありますので注意が必要です。

具体的な指定方法は、`man openssl`の PASS PHRASE ARGUMENTS のセクションに以下のように書かれています。

- `pass:password`
 - the actual password is password. Since the password is visible to utilities (like 'ps' under Unix) this form should only be used where security is not important.
- `env:var`
 - obtain the password from the environment variable var. Since the environment of other processes is visible on certain platforms (e.g. ps under certain Unix OSes) this option should be used with caution.
- `file:pathname`
 - the first line of pathname is the password. If the same pathname argument is supplied to -passin and -passout arguments then the first line will be used for the input password and the next line for the output password. pathname need not refer to a regular file: it could for example refer to a device or named pipe.
- `fd:number`
 - read the password from the file descriptor number. This can be used to send the data via a pipe for example.
- `stdin`
 - read the password from standard input.

上記例では一番上の `pass:password` 方式を使って、パスワードを直接指定しています。`pass:oldpassword` や `pass:newpassword` のそれぞれ `oldpassword`と `newpassword`の部分がパスワードです。
です。`pass:oldpassword` というパスワードではありませんのでご注意ください。