2022年1月17日月曜日

DBのRSA暗号の互換性の確認

 RSA暗号がOracle Database 21c(19cにはバックポート)のDBMS_CRYPTOパッケージに追加されています。

DBMS_CRYPTO.PKENCRYPT/PKDECRYPTで暗号化/復号する、Javaに関してはjavax.crypto.Cipherで暗号化/復号するのは問題なくできます。また、DBMS_CRYPTO.SIGN/VERIFYでの署名/検証、java.security.Signatureによる署名/検証も同様です。

DBMS_CRYPTO.PKENCRYPTによる暗号化、javax.crypto.Cipherによる復号、またはその逆、およびDBMS_CRYPTO.SIGNによる署名、java.security.Signatureによる検証、またはその逆の互換性について確認した作業について記載します。


署名と検証


DBMS_CRYPTO.SIGNおよびVERIFYがサポートしている署名のハッシュ・アルゴリズムはマニュアルの以下に記載されています。

https://docs.oracle.com/en/database/oracle/oracle-database/21/arpls/DBMS_CRYPTO.html#GUID-E33DC872-3C26-4E7F-85F8-4D865FE73805

Table 47-22 SHA Hash AlgorithmsまたはTable 47-24 SHA Hash Algorithmsにリストがあります。

JavaのSignatureクラスで利用できる標準のアルゴリズムは以下にリストされています。



検証に使用したスクリプトは以下になります。DBとJavaの実装で、同じ公開鍵、秘密鍵を使用するため、JavaはOracle Databaseに実装されているJavaを使っています。DBMS_CRYPTOパッケージは秘密鍵のフォーマットとして、PKCS#1とPKCS#8の形式の両方をサポートしていたので、Javaで扱いやすいPKCS#8のフォーマットを採用しています。

PL/SQLのテスト・スクリプト


Javaによる署名と検証の実装


暗号化と復号


DBMS_CRYPTO.PKENCRYPT/PKDECRYPTがサポートしているアルゴリズムはPKENCRYPT_RSA_PKCS1_OAEPのみで詳しい仕様について記載がありません。


テスト・スクリプトをJavaで記述し暗号化/復号して確認したところ、ハッシュ・アルゴリズムとしてSHA-256が使用されていることが確認できています。

以下のスクリプトでテストしています。

PL/SQLのテスト・スクリプト


Javaによる暗号化と復号の実装


検証コードを書いていて気がついた点を以下に記載します。Javaのコードですが、最初は以下のようにjavax.crypto.Cipherを初期化していました。

    KeyFactory keyFactory = KeyFactory.getInstance("RSA");

    PublicKey  publicKey  = keyFactory.generatePublic(new X509EncodedKeySpec(pkdata));

    Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");

    cipher.init(Cipher.ENCRYPT_MODE, publicKey);


この方法で初期化するとデータベースで暗号化したテキストをJavaで復号、またはその逆はできませんでした。javax.crypto.BadPaddingExceptionが発生します。

成功した初期化手順は以下になります。PSource.PSpecified.DEFAULTの指定が必要になります。

    MGF1ParameterSpec mgf  = new MGF1ParameterSpec(alg);

    OAEPParameterSpec spec = new OAEPParameterSpec(alg,"MGF1",mgf,PSource.PSpecified.DEFAULT);

    KeyFactory keyFactory = KeyFactory.getInstance("RSA");

    PublicKey  publicKey  = keyFactory.generatePublic(new X509EncodedKeySpec(pkdata));

    Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPPadding");

    cipher.init(Cipher.ENCRYPT_MODE, publicKey, spec);


以上になります。