pgcryptoモジュールはPostgreSQL用の暗号関連関数を提供します。
digest()
digest(data text, type text) returns bytea digest(data bytea, type text) returns bytea
与えられたdataのバイナリハッシュを計算します。 typeは使用するアルゴリズムです。 標準アルゴリズムはmd5、sha1、sha224、sha256、sha384、sha512です。 pgcryptoがOpenSSL付きで構築された場合、表F-21で詳解する、より多くのアルゴリズムを利用することができます。
ダイジェストを16進数表記の文字列としたい場合は、結果に対してencode()
を使用してください。
以下に例を示します。
CREATE OR REPLACE FUNCTION sha1(bytea) returns text AS $$ SELECT encode(digest($1, 'sha1'), 'hex') $$ LANGUAGE SQL STRICT IMMUTABLE;
hmac()
hmac(data text, key text, type text) returns bytea hmac(data bytea, key text, type text) returns bytea
keyをキーとしたdataのハッシュ化MACを計算します。
typeはdigest()
の場合と同じです。
digest()
と似ていますが、ハッシュを再計算してもキーがわかるだけです。
これは、誰かがデータを変更した、誰かがハッシュを変更したというシナリオを防ぎます。
キーがハッシュブロックサイズより大きい場合、まずハッシュ化され、その結果をキーとして使用します。
crypt()
およびgen_salt()
関数は特にパスワードのハッシュ化のために設計されたものです。
crypt()
がハッシュ処理を行い、gen_salt()
はハッシュ処理用のアルゴリズム上のパラメータを準備します。
crypt()
アルゴリズムは、以下の点でMD5やSHA1のような通常のハッシュ処理アルゴリズムと異なります。
低速です。 データ量が少ないためパスワード総当たり攻撃に対して頑健にする唯一の方法です。
結果にはソルトというランダムな値が含まれます。 このため同じパスワードのユーザでも異なった暗号化パスワードを持ちます。 これはアルゴリズムの反転処理に対する追加の防御です。
結果内にアルゴリズムの種類が含まれます。 このため異なるアルゴリズムでハッシュ化したパスワードが混在可能です。
一部は適合型です。 つまり、コンピュータが高速になったとしても、既存のパスワードとの互換性を損なうことなくアルゴリズムを低速に調整することができます。
表 F-18. crypt()
がサポートするアルゴリズム
アルゴリズム | パスワード最大長 | 適合型かどうか | ソルトビット長 | 説明 |
---|---|---|---|---|
bf | 72 | はい | 128 | Blowfishベース、2a版 |
md5 | 無制限 | いいえ | 48 | MD5ベースの暗号 |
xdes | 8 | はい | 24 | 拡張DES |
des | 8 | いいえ | 12 | 元来のUNIX crypt |
crypt()
crypt(password text, salt text) returns text
passwordのcrypt(3)形式のハッシュを計算します。
新しいパスワードを保管する時には、gen_salt()
を使用して新しいsaltを生成する必要があります。
パスワードを検査する時、既存のハッシュ値をsaltとして渡し、結果が格納された値と一致するかどうかを確認します。
新しいパスワードの設定例を以下に示します。
UPDATE ... SET pswhash = crypt('new password', gen_salt('md5'));
認証の例です。
SELECT pswhash = crypt('entered password', pswhash) FROM ... ;
入力パスワードが正しければtrueを返します。
gen_salt()
gen_salt(type text [, iter_count integer ]) returns text
crypt()
で使用するランダムなソルトを新規に生成します。
また、このソルト文字列はcrypt()
にどのアルゴリズムを使用するかを通知します。
typeパラメータはハッシュ化アルゴリズムを指定します。 受付可能な種類は、des、xdes、md5、bfです。
繰り返し回数を持つアルゴリズムでは、ユーザはiter_countパラメータを使用して繰り返し回数を指定できます。 指定する回数を高くすれば、パスワードのハッシュ化にかかる時間が長くなり、それを破るための時間も長くなります。 しかし、あまりに多くの回数を指定すると、ハッシュ計算にかかる時間は数年に渡ってしまう可能性があります。 これは実用的ではありません。 iter_countパラメータを省略した場合、デフォルトの繰り返し回数が使用されます。 iter_countで受け付けられる値はアルゴリズムにより異なります。
xdesの場合、回数が奇数でなければならないという追加の制限があります。
適切な繰り返し回数を選択するために、元々のDES暗号は当時のハードウェアで1秒あたり4個のハッシュを持つことができるように設計されたことを考えてください。 1秒4ハッシュより遅いと、おそらく使い勝手が悪いでしょう。 1秒100ハッシュより速いというのは、おそらく速すぎるでしょう。
ハッシュ化アルゴリズム別に相対的な速度に関する概要を以下の表にまとめました。
この表は、8文字のパスワード内のすべての文字の組合せを取るためにかかる時間を示します。
また、すべて小文字の英字のみのパスワードである場合と大文字小文字が混在した英字と数字のパスワードの場合を仮定します。
crypt-bfの項では、スラッシュの後の数値はgen_salt
のiter_countです。
表 F-20. ハッシュアルゴリズムの速度
アルゴリズム | 1秒当たりのハッシュ数 | [a-z]の場合 | [A-Za-z0-9]の場合 |
---|---|---|---|
crypt-bf/8 | 28 | 246年 | 251322年 |
crypt-bf/7 | 57 | 121年 | 123457年 |
crypt-bf/6 | 112 | 62年 | 62831年 |
crypt-bf/5 | 211 | 33年 | 33351年 |
crypt-md5 | 2681 | 2.6年 | 2625年 |
crypt-des | 362837 | 7日 | 19年 |
sha1 | 590223 | 4日 | 12年 |
md5 | 2345086 | 1日 | 3年 |
注意:
Pentium 4 1.5GHzのマシンを使用しました。
crypt-desおよびcrypt-md5アルゴリズムの数値はJohn the Ripper v1.6.38の-test出力から得たものです。
md5の数値はmdcrack 1.2のものです。
sha1の数値はlcrack-20031130-betaのものです。
crypt-bfの数は、1000個の8文字パスワードをループする単純なプログラムを使用して得たものです。 こうして、異なる回数の速度を示すことができました。 参考までに、john -testはcrypt-bf/5で213 loops/secでした。 (結果の差異が非常に小さいことは、pgcryptoにおけるcrypt-bf実装がJohn the Ripperで使用されるものと同じであるという事実と一致します。)
"すべての組み合わせを試行する"ことは現実的な演習ではありません。 通常パスワード推定は、普通の単語とその変形の両方を含む辞書を使用して行われます。 ですので、多少単語に似たパスワードは上で示した数値よりも速く推定されます。 また6文字の単語に似ていないパスワードは推定を免れるかもしれませんし、免れないかもしれません
ここで示す関数はOpenPGP(RFC 4880)標準の暗号処理部分を実装します。 対称キーおよび公開キー暗号化がサポートされます。
暗号化されたPGPメッセージは次の2つの部品(またはパケット)から構成されます。
セッションキーを含むパケット。 対称キーまたは公開キー暗号化の両方。
セッションキーにより暗号化されたデータを含むパケット。
対称キー(つまりパスワード)で暗号化する場合
与えられたパスワードはString2Key(S2K)アルゴリズムでハッシュ化されます。
これはどちらかというとcrypt()
アルゴリズムと似て、意図的に低速で、かつランダムなソルトを使用します。
しかし、全長のバイナリキーを生成します。
分離したセッションキーが要求された場合、新しいランダムなキーが生成されます。 さもなくば、S2Kキーがそのままセッションキーとして使用されます。
S2Kキーがそのまま使用される場合、S2K設定のみがセッションキーパケットに格納されます。 さもなくば、セッションキーはS2Kキーで暗号化され、セッションキーパケットに格納されます。
公開キーで暗号化する場合
新しいランダムなセッションキーが生成されます。
これは公開キーを使用して暗号化され、セッションキーパケットに格納されます。
どちらの場合でもデータ暗号化は以下のように処理されます。
省略可能なデータ操作として、圧縮、UTF-8への変換、改行の変換があります。
データの前にはランダムなバイト数のブロックが付きます。 これはrandom IVを使用する場合と同じです。
ランダムな前置ブロックとデータのSHA1ハッシュが後に付けられます。
これをすべてセッションキーで暗号化し、データパケットに格納します。
pgp_sym_encrypt()
pgp_sym_encrypt(data text, psw text [, options text ]) returns bytea pgp_sym_encrypt_bytea(data bytea, psw text [, options text ]) returns bytea
対称PGPキーpswでdataを暗号化します。 optionsパラメータには後述のオプション設定を含めることができます。
pgp_sym_decrypt()
pgp_sym_decrypt(msg bytea, psw text [, options text ]) returns text pgp_sym_decrypt_bytea(msg bytea, psw text [, options text ]) returns bytea
対称キーで暗号化されたPGPメッセージを復号化します。
pgp_sym_decrypt
でbytea型のデータを復号化することはできません。
これは無効な文字データの出力を防止するためです。
元のテキストのデータをpgp_sym_decrypt_bytea
で復号化することが正しい方法です。
optionsパラメータには後述のオプション設定を含めることができます。
pgp_pub_encrypt()
pgp_pub_encrypt(data text, key bytea [, options text ]) returns bytea pgp_pub_encrypt_bytea(data bytea, key bytea [, options text ]) returns bytea
公開PGPキーkeyでdataを暗号化します。 この関数に秘密キーを与えるとエラーになります。
optionsパラメータには後述のオプション設定を含めることができます。
pgp_pub_decrypt()
pgp_pub_decrypt(msg bytea, key bytea [, psw text [, options text ]]) returns text pgp_pub_decrypt_bytea(msg bytea, key bytea [, psw text [, options text ]]) returns bytea
公開キーで暗号化されたメッセージを復号化します。 keyは、暗号化に使用した公開キーに対応する秘密キーでなければなりません。 秘密キーがパスワードで保護されている場合は、そのパスワードをpswで指定しなければなりません。 パスワードはないが、オプションを指定したい場合は空のパスワードを指定する必要があります。
pgp_pub_decrypt
でbytea型のデータを復号化することはできません。
これは無効な文字データの出力を防止するためです。
元のテキストのデータをpgp_pub_decrypt_bytea
で復号化することが正しい方法です。
optionsパラメータには後述のオプション設定を含めることができます。
pgp_key_id()
pgp_key_id(bytea) returns text
pgp_key_id
はPGP公開キーまたは秘密キーのキーIDを取り出します。
暗号化されたメッセージが指定された場合は、データの暗号化に使用されたキーIDを与えます。
2つの特殊なキーIDを返すことがあります。
SYMKEY
メッセージは対称キーで暗号化されました。
ANYKEY
メッセージは公開キーで暗号化されましたが、キーIDが消去されていました。 つまり、どれで復号化できるかを判定するためにはすべての秘密キーを試行しなければならないことを意味します。 pgcrypto自身はこうしたメッセージを生成しません。
異なるキーが同一IDを持つ場合があることに注意してください。 これは稀ですが、正常なイベントです。 この場合クライアントアプリケーションはどちらが当てはまるかを調べるために、ANYKEYの場合と同様に、それぞれのキーで復号化を試行しなければなりません。
armor()
, dearmor()
armor(data bytea) returns text dearmor(data text) returns bytea
PGPのASCIIアーマー形式にデータを隠す、または、データを取り出します。 ASCIIアーマーは基本的にCRC付きのBASE64という形式であり、他にもフォーマットがあります。
オプションはGnuPGに似せて命名しています。 オプションの値は等号記号の後に指定しなければなりません。 複数のオプションはカンマで区切ってください。 以下に例を示します。
pgp_sym_encrypt(data, psw, 'compress-algo=1, cipher-algo=aes256')
convert-crlfを除くすべてのオプションは暗号化関数にのみ適用可能です。 復号化関数はPGPデータからこうしたパラメータを入手します。
もっとも興味深いオプションはおそらくcompress-algoとunicode-modeでしょう。 残りはデフォルトで問題ないはずです。
使用する暗号アルゴリズム。
値: bf, aes128, aes192, aes256 (OpenSSL-only: 3des, cast5) デフォルト: aes128 適用範囲: pgp_sym_encrypt, pgp_pub_encrypt
使用する圧縮アルゴリズム。 PostgreSQLがzlib付きで構築されている場合のみ利用可能です。
値: 0 - 非圧縮 1 - ZIP圧縮 2 - ZLIB圧縮 (ZIPにメタデータとブロックCRCを加えたもの) デフォルト: 0 適用範囲: pgp_sym_encrypt, pgp_pub_encrypt
どの程度圧縮するか。 レベルが大きい程小さくなりますが、低速になります。 0は圧縮を無効にします。
値: 0, 1-9 デフォルト: 6 適用範囲: pgp_sym_encrypt, pgp_pub_encrypt
暗号化の際に\nを\r\nに、復号化の際に\r\n を\n に変換するかどうか。 RFC 4880では、テキストデータは改行コードとして\r\n を使用して格納すべきであると規定されています。 完全にRFC準拠の動作を行いたければ、これを使用してください。
値: 0, 1 デフォルト: 0 適用範囲: pgp_sym_encrypt, pgp_pub_encrypt, pgp_sym_decrypt, pgp_pub_decrypt
データをSHA-1で保護しません。 このオプションを使用することが良い唯一の理由は、SHA-1で保護されたパケットがRFC 4880に追加される前の、古いPGP製品との互換性を達成することです。 最近のgnupg.orgおよびpgp.comのソフトウェアではこれを正しくサポートしています。
値: 0, 1 デフォルト: 0 適用範囲: pgp_sym_encrypt, pgp_pub_encrypt
分離したセッションキーを使用します。 公開キー暗号では常に分離したセッションキーを使用します。 これは対称キー暗号向けのもので、デフォルトではS2Kをそのまま使用します。
値: 0, 1 デフォルト: 0 適用範囲: pgp_sym_encrypt
使用するS2Kアルゴリズム。
値: 0 - ソルト無。危険です 1 - ソルト有。固定繰り返し回数。 3 - 可変繰り返し回数 デフォルト: 3 適用範囲: pgp_sym_encrypt
分離したセッションキーの暗号化に使用する暗号。
値: bf, aes, aes128, aes192, aes256 デフォルト: use cipher-algo. 適用範囲: pgp_sym_encrypt
テキストデータをデータベース内部符号化方式からUTF-8に変換して戻すかどうか。 データベースがすでにUTF-8であれば、変換は起こらず、データにUTF-8としてタグが付くのみです。 このオプションがないと、何も行われません。
値: 0, 1 デフォルト: 0 適用範囲: pgp_sym_encrypt, pgp_pub_encrypt
新しいキーを生成します。
gpg --gen-key
推奨するキー種類は"DSA and Elgamal"です。
RSA暗号化のためには、マスタとしてDSAまたはRSAで署名のみのキーを作成し、そしてgpg --edit-keyを使用してRSAで暗号化された副キーを追加しなければなりません
キーを列挙します。
gpg --list-secret-keys
ASCIIアーマー形式で公開キーをエキスポートします。
gpg -a --export KEYID > public.key
ASCIIアーマー形式の秘密キーをエキスポートします。
gpg -a --export-secret-keys KEYID > secret.key
PGP関数にこれらのキーを渡す前にdearmor()
`を使用する必要があります。
バイナリデータを扱うことができる場合、gpgから-aフラグを取り除くことができます。
詳細はman gpg、http://www.gnupg.org/gph/en/manual.htmlThe GNU Privacy Handbook、http://www.gnupg.orgサイトの各種文書を参照してください。
署名に関するサポートはありません。 これはまた、暗号化副キーがマスタキーに属しているかどうか検査しないことを意味します。
マスタキーとして暗号化キーをサポートしません。 一般的にこうした状況は現実的ではありませんので、問題にならないはずです。
複数の副キーに関するサポートはありません。 よくありますので、これは問題になりそうに見えます。 一方、通常のGPG/PGPキーをpgcryptoで使用すべきではありません。 使用する状況が多少異なりますので新しく作成してください。
これらの関数はデータ全体を暗号化するためだけに実行します。 PGP暗号化の持つ先端的な機能はありません。 したがって、大きな問題がいくつか存在します。
暗号キーとしてユーザキーをそのまま使用します。
暗号化されたデータが変更されたかどうかを確認するための整合性検査をまったく提供しません。
ユーザが、IVをも含め暗号化パラメータ自体をすべて管理していることを想定しています。
テキストは扱いません。
このため、PGP暗号化の導入もあり、暗号化のみの関数はあまり使用されません。
encrypt(data bytea, key bytea, type text) returns bytea decrypt(data bytea, key bytea, type text) returns bytea encrypt_iv(data bytea, key bytea, iv bytea, type text) returns bytea decrypt_iv(data bytea, key bytea, iv bytea, type text) returns bytea
typeで指定した暗号方法を使用してデータの暗号化・復号化を行います。 type文字列の構文は以下の通りです。
algorithm [ - mode ] [ /pad: padding ]
ここでalgorithmは以下の1つです。
bf — Blowfish
aes — AES (Rijndael-128)
またmodeは以下の1つです。
cbc — 次のブロックは前ブロックに依存します(デフォルト)
ecb — 各ブロックは独自に暗号化されます(試験用途のみ)
paddingは以下の1つです。
pkcs — データ長に制限はありません(デフォルト)
none — データは暗号ブロックサイズの倍数でなければなりません
このため、例えば以下は同じです。
encrypt(data, 'fooz', 'bf') encrypt(data, 'fooz', 'bf-cbc/pad:pkcs')
encrypt_iv
およびdecrypt_iv
では、ivパラメータはCBCモード用の初期値となります。
ECBでは無視されます。
正確にブロック長でない場合、切り抜かれるか無視されます。
このパラメータがない場合、関数のデフォルト値はすべてゼロです。
gen_random_bytes(count integer) returns bytea
暗号論的に強いランダムなcountバイトを返します。 一度に最大で1024バイトを抽出することができます。 ランダム性ジェネレータプールを空にすることを防止するためのものです。
pgcryptoは自身で主PostgreSQLのconfigureスクリプトの検出結果に従って構築します。 構築に影響するオプションは--with-zlibと--with-opensslです。
ZLIB付きでコンパイルされた場合、PGP暗号化関数は暗号化前にデータを圧縮することができます。
OpenSSL付きでコンパイルされた場合、より多くのアルゴリズムが利用できるようになります。 また、OpenSSLがより最適化されたBIGNUM関数を持つため、公開キー暗号化関数は高速になります。
表 F-21. OpenSSLの有無による機能のまとめ
機能 | 組込み | OpenSSL付き |
---|---|---|
MD5 | ○ | ○ |
SHA1 | ○ | ○ |
SHA224/256/384/512 | ○ | ○(注1) |
この他のダイジェストアルゴリズム | × | ○(注2) |
Blowfish | ○ | ○ |
AES | ○ | ○(注3) |
DES/3DES/CAST5 | × | ○ |
暗号化そのもの | ○ | ○ |
PGP対称暗号化 | ○ | ○ |
PGP公開キー暗号化 | ○ | ○ |
注
SHA2はOpenSSLバージョン0.9.8から含まれています。 これより古いバージョンでは、pgcryptoは組込みのコードを使用します。
OpenSSLがサポートする任意のダイジェストアルゴリズムが自動的に選択されます。 これは、明示的にサポートされなければならない暗号では使用できません。
AESはOpenSSLバージョン0.9.7から含まれています。 これより古いバージョンでは、pgcryptoは組込みのコードを使用します。
標準SQLの通り、引数のいずれかがNULLの場合、すべての関数はNULLを返します。 注意せずに使用すると、これがセキュリティ上の問題になるかもしれません。
pgcryptoの関数はすべてデータベースサーバ内部で実行されます。 これは、pgcryptoとクライアントアプリケーションとの間でやり取りされるデータはすべて平文であることを意味します。 したがって、以下を行う必要があります。
ローカルまたはSSL接続で接続
システム管理者およびデータベース管理者を信頼
これらが不可能であれば、クライアントアプリケーション内で暗号化する方が望まれます。
http://www.gnupg.org/gph/en/manual.html
GNUプライバシーハンドブック
http://www.openwall.com/crypt/
blowfish暗号アルゴリズムの説明
http://www.stack.nl/~galactus/remailers/passphrase-faq.html
優れたパスワードの選び方
http://world.std.com/~reinhold/diceware.html
パスワード決定に関する面白い考え
http://www.interhack.net/people/cmcurtin/snake-oil-faq.html
良い暗号、悪い暗号に関する説明
http://www.ietf.org/rfc/rfc4880.txt
OpenPGPメッセージフォーマット
http://www.ietf.org/rfc/rfc1321.txt
MD5 メッセージダイジェストアルゴリズム
http://www.ietf.org/rfc/rfc2104.txt
HMAC: Keyed-Hashing for Message Authentication.
http://www.usenix.org/events/usenix99/provos.html
DES暗号、MD5暗号、bcryptアルゴリズムの比較
http://csrc.nist.gov/cryptval/des.htm
DES、3DES、AES標準
http://en.wikipedia.org/wiki/Fortuna_(PRNG)
Fortuna CSPRNGの説明
Linux用Jean-Luc Cooke Fortunaに基く/dev/randomドライバ
http://www.cs.ut.ee/~helger/crypto/
暗号学に関するリンク集
Marko Kreen <markokr@gmail.com>
pgcryptoは以下のソースを使用しています。
表 F-22. クレジット
アルゴリズム | 作者 | 元ソース |
---|---|---|
DES crypt | David Burren and others | FreeBSD libcrypt |
MD5 crypt | Poul-Henning Kamp | FreeBSD libcrypt |
Blowfish crypt | Solar Designer | www.openwall.com |
Blowfish cipher | Simon Tatham | PuTTY |
Rijndael cipher | Brian Gladman | OpenBSD sys/crypto |
MD5 and SHA1 | WIDE Project | KAME kame/sys/crypto |
SHA256/384/512 | Aaron D. Gifford | OpenBSD sys/crypto |
BIGNUM math | Michael J. Fromberger | dartmouth.edu/~sting/sw/imath |