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だろうが

ファイル渡すとポッと秘密鍵が使えるものがあると有難いですね。

ううん、作っておこう、、、、