OpenSSLで生成したRSA/DSA秘密鍵は
今までずっと暗号化しないPKCS#8だと勘違いしていたんですが
PKCS#1の秘密鍵だったんですね。
#常識でしたか、、、、、(T_T)
Javaの
PKCS8EncodedKeySpecで読んで使おうと思ったら
できないんで、おかしいなぁと思って調べちゃいました。
ちなみにOpenSSLで生成したDSA秘密鍵
% openssl dsaparam -out aaa.prm 1024
% openssl gendsa -out aaa.prvkey aaa.prm
当たり前ですが、こんなASN.1構造です。
SEQUENCE {
INTEGER 0 -- version
INTEGER -- P プライム
INTEGER -- Q サブプライム
INTEGER -- G ベース
INTEGER -- Y 公開鍵
INTEGER -- X 秘密鍵 }
さらにはOpenSSLで生成したPKCS#1 RSA秘密鍵
% openssl genrsa -out bbb.prvkey 1024
ASN.1構造はこんな感じ。
SEQUENCE {
INTEGER 0 -- version
INTEGER -- n モジュラス(modulus)
INTEGER -- e 公開指数(public exponent)
INTEGER -- d 秘密指数 (private exponent)
INTEGER -- p プライム1 nの素因数 (prime1)
INTEGER -- q プライム2 nの素因数 (prime2)
INTEGER -- プライム1指数 (exponent1 = d mod (p-1))
INTEGER -- プライム2指数 (exponent2 = d mod (q-1))
INTEGER -- 中国剰余定理係数 (coefficient = (inverse of q) mod p) }
以上のPKCS#1フォーマットのRSA/DSA鍵はそのままでは
Javaでは読み込むことができない(と思う)ので
PKCS#8にくるんでやる必要があります。
OpenSSLのPKCS#1 DSA秘密鍵をPKCS#8に変換
秘密鍵を共通鍵暗号で保護しない平文形式の鍵に
変換するには以下のようにします。
% openssl pkcs8 -in dsa.prvkey -out dsa.prvkey.p8 -topk8 -nocrypt
すると生成されたPKCS#8のDSA秘密鍵は
SEQUENCE {
INTEGER 0 -- version
SEQUENCE {
OBJECT IDENTIFIER dsa (1.2.840.10040.4.1)
SEQUENCE {
INTEGER -- P プライム
INTEGER -- Q サブプライム
INTEGER -- G ベース
}
}
OCTET STRING, encapsulates {
INTEGER -- X 秘密鍵
}
}
}
ありゃりゃ、Yが無くなり、秘密鍵XがOCTET STRINGで
カプセル化されちゃったぞ。
OpenSSLのPKCS#1 RSA秘密鍵をPKCS#8に変換
同様にPKCS#1 RSA秘密鍵をPKCS#8に変換してみます。
% openssl pkcs8 -in rsa.prvkey -out rsa.prvkey.p8 -topk8 -nocrypt
すると生成されたPKCS#8 RSA秘密鍵のASN.1構造は
こんな感じ。
SEQUENCE {
INTEGER 0 -- version
SEQUENCE {
OBJECT IDENTIFIER rsaEncryption (1.2.840.113549.1.1.1)
NULL }
OCTET STRING, encapsulates {
SEQUENCE {
INTEGER 0
INTEGER -- n モジュラス
INTEGER -- e 公開指数
INTEGER -- d 秘密指数
INTEGER -- p プライム1
INTEGER -- q プライム2
INTEGER -- プライム1指数
INTEGER -- プライム2指数
INTEGER -- 中国剰余定理係数
}
}
}
AiCryptoのPKCS#8 DSA秘密鍵は?
Challenge PKI Test Suiteでは秘密鍵はどうなってるのか
ちゃんと見たことも無かったので見てみると保護されないPKCS#8になっていました。
この部分はOpenSSLと同じような暗号ライブラリAiCryptoの
PEM_write_p8()関数が使わています。
で、出力はこんな感じ。
SEQUENCE {
INTEGER 0 -- version
SEQUENCE {
OBJECT IDENTIFIER dsa (1.2.840.10040.4.1)
SEQUENCE {
INTEGER -- P プライム
INTEGER -- Q サブプライム
INTEGER -- G ベース
INTEGER -- Y 公開鍵
INTEGER -- X 秘密鍵
}
}
}
公開鍵Yがあったり、秘密鍵XがOCTETSTRINGでカプセル化
されていないところがOpenSSLとはちょっと違いますね。
AiCryptoのPKCS#8 RSA秘密鍵は?
同様にAiCryptoの生成したPKCS#8 RSA秘密鍵も見てみます。
SEQUENCE {
INTEGER 0 -- version
SEQUENCE {
OBJECT IDENTIFIER rsaEncryption (1.2.840.113549.1.1.1)
NULL }
OCTET STRING, encapsulates {
SEQUENCE {
INTEGER 0
INTEGER -- n モジュラス
INTEGER -- e 公開指数
INTEGER -- d 秘密指数
INTEGER -- p プライム1
INTEGER -- q プライム2
INTEGER -- プライム1指数
INTEGER -- プライム2指数
INTEGER -- 中国剰余定理係数
}
}
}
これは、OpenSSLの変換結果と全く同じ構造ですね。
PKCS#8ファイルをJavaで読み込む
読み込むのは以下のような感じになります。
byte[] p8 = PEMの内容を何らかの方法でbyte配列に
KeySpec keySpec = new PKCS8EncodedKeySpec(p8)
KeyFactory kf = null;
if (鍵がRSAなら)
kf = KeyFactory.getInstance("RSA");
else
kf = KeyFactory.getInstance("DSA");
PrivateKey pk = kf.generatePrivate(keySpec);
その鍵がRSAであるかDSAであるかはkeySpecからはわからないので
tryで適当にやってみるしか無いと思います。
OpenSSLやAiCryptoのPKCS#8がJavaで読めたか
実行結果をまとめたのが下表になります。
| 秘密鍵の生成方法 | Javaで読めるか? |
|---|---|
| DSA OpenSSL PKCS1→PKCS8 | 読める |
| DSA AiCrypto PKCS8 | 読める |
| RSA OpenSSL PKCS1→PKCS8 | 読める |
| RSA AiCrypto PKCS8 | 読める |
なるほどなるほど、カプセル化の部分はきちんと吸収してくれるらしい。
しかしながら、DSAだろうが、RSAだろうが
PKCS#1だろうが、PKCS#8だろうが、PKCS#12だろうが
ファイル渡すとポッと秘密鍵が使えるものがあると有難いですね。
ううん、作っておこう、、、、