2020年12月15日火曜日

Oracle Database 21cのRSA暗号を試してみる

Oracle Database 21cより、RSA暗号が提供されています。以前にOracle DatabaseのJavaの実装を使ってJSON WebアルゴリズムのRSASSA-PKCS1-v1_5(RS256)に対応する方法を記事にしましたが、Autonomous DatabaseのようなJavaが使えない構成では不可ですし、パフォーマンス面での懸念もありました。

21cから、DBMS_CRYPTOのPKENCRYPT/PKDECRYPTによるRSA公開鍵暗号による暗号化/復号化、および、SIGN/VERIFYによる署名/検証を行うことができます。マニュアルの記載はこちらです。

動作確認と勉強を兼ねて、マニュアルの例題を動かしてみました。

まず、公開鍵/秘密鍵のペアを生成します。opensslを使った鍵の生成方法は、例題のコメントに記載されています。

最初に鍵長が2048ビットの秘密鍵を生成します。

openssl genrsa -out private.pem 2048

続いて、生成した秘密鍵より公開鍵を、PEM形式で取り出します。

openssl rsa -in private.pem -outform PEM -pubout -out public.pem

以前の記事でも紹介しましたが、生成されている秘密鍵と公開鍵をシェル・スクリプトを使って1行にしました。

#!/bin/sh

while read l
do
   test ${l:0:1} != "-" && /bin/echo -n $l
done < private.pem
echo

公開鍵を1行にするときは、private.pemの代わりに、public.pemを指定します。

マニュアルにあるExample 2: PKENCRYPTION and PKDECRYPTION Functionsを実行します。ファンクション名は正しくは、PKENCRYPTPKDECRYPTです。例題のコードに含まれるファンクション名は正しいのですが、以下の2行でのシングル・クオートが正しくありません

シングル・クオートも含めて、正しくXXXXを公開鍵YYYYを秘密鍵に置き換えます。

  pubkey    VARCHAR (2000) := XXXX;
  prvkey    VARCHAR (2000) := YYYY; 
DECLARE
  ip_str     VARCHAR (200) := 'Secret Message';
  op_str     VARCHAR (200);
  -- Use OpenSSL to generate the private and public key (2048 bit RSA key)
  -- openssl genrsa -out private.pem 2048
  -- openssl rsa -in private.pem -outform PEM -pubout -out public.pem
  pubkey    VARCHAR (2000) := 'ここに1行にしたpublic.pemの内容を含める';
  prvkey    VARCHAR (2000) := 'ここに1行にしたprivate.pemの内容を含める';
  enc_raw    RAW (2000);
  dec_raw    RAW (2000);
  eType      PLS_INTEGER := DBMS_CRYPTO.PKENCRYPT_RSA_PKCS1_OAEP;
  kType      PLS_INTEGER := DBMS_CRYPTO.KEY_TYPE_RSA;

BEGIN

  DBMS_OUTPUT.PUT_LINE('-------------------------------------------------');
  DBMS_OUTPUT.PUT_LINE('Original String := ' || ip_str);
  DBMS_OUTPUT.PUT_LINE('-------------------------------------------------');
  enc_raw:= DBMS_CRYPTO.PKENCRYPT
  (
    src        => UTL_I18N.STRING_TO_RAW(ip_str,'AL32UTF8'),
    pub_key    => UTL_I18N.STRING_TO_RAW( pubkey, 'AL32UTF8'),
    pubkey_alg => kType,
    enc_alg    => eType
  );

  dec_raw := DBMS_CRYPTO.PKDECRYPT
  (
    src        => enc_raw,
    prv_key    => UTL_I18N.STRING_TO_RAW( prvkey, 'AL32UTF8'),
    pubkey_alg => kType,
    enc_alg    => eType
  );
  op_str := UTL_I18N.RAW_TO_CHAR(dec_raw,'AL32UTF8');
  dbms_output.put_line('-------------------------------------------------');
  dbms_output.put_line('Decrypted String := ' || op_str);
  dbms_output.put_line('-------------------------------------------------');
end;
/

実行した結果は以下になります。

------------------------------------------------- Original String := Secret Message ------------------------------------------------- ------------------------------------------------- Decrypted String := Secret Message ------------------------------------------------- PL/SQL procedure successfully completed. Elapsed: 00:00:00.006

次にExample 3: SIGN and VERIFY Functionsを試してみます。鍵の置き換えの注意点はPKENCRYPT/PKDECRYPTと同様です。
DECLARE
  ip_str     VARCHAR2 (200) := 'Secret Message';
  -- Use OpenSSL to generate the private and public key (2048 bit RSA key)
  -- openssl genrsa -out private.pem 2048
  -- openssl rsa -in private.pem -outform PEM -pubout -out public.pem
  pubkey    VARCHAR (2000) := 'ここに1行にしたpublic.pemの内容を含める';
  prvkey    VARCHAR (2000) := 'ここに1行にしたprivate.pemの内容を含める';
  sign_raw   RAW (2000);
  returnval  BOOLEAN := false;
  sType      PLS_INTEGER := DBMS_CRYPTO.SIGN_SHA224_RSA;
  kType      PLS_INTEGER := DBMS_CRYPTO.KEY_TYPE_RSA;
BEGIN
  sign_raw := DBMS_CRYPTO.SIGN
  (
   src        => UTL_I18N.STRING_TO_RAW(ip_str,'AL32UTF8'),
   prv_key    => UTL_I18N.STRING_TO_RAW( prvkey, 'AL32UTF8'),
   pubkey_alg => kType,
   sign_alg   => sType
  );
  returnval := DBMS_CRYPTO.VERIFY
  (
   src        => UTL_I18N.STRING_TO_RAW( ip_str,'AL32UTF8'),
   sign       => sign_raw,
   pub_key    => UTL_I18N.STRING_TO_RAW( pubkey, 'AL32UTF8'),
   pubkey_alg => kType,
   sign_alg   => sType
  );
  DBMS_OUTPUT.PUT_LINE('-------------------------------------------------');
  IF returnval THEN
    DBMS_OUTPUT.PUT_LINE('True');
  ELSE
    DBMS_OUTPUT.PUT_LINE('False');
  END IF;
  DBMS_OUTPUT.PUT_LINE('-------------------------------------------------');
END;
/

実行した結果は以下になります。

------------------------------------------------- True ------------------------------------------------- PL/SQL procedure successfully completed. Elapsed: 00:00:00.008

以上で、マニュアルの例題を用いた確認ができました。APEXでSAML認証をサポートしていないのはRSA公開鍵暗号方式が無いことが理由で、これが実装されたらサポートする方針と聞いているので、早く提供されることを期待しています。

# Oracle Database 19cにバックポートしてほしい。(OCT2020 19.9 DB RUでバックポートされてました)