無料で行うOracle Database の暗号化を簡単に使う方法

# 概要
Oracle Databaseで暗号化を行う場合、様々な方法があります。
その中でも無料で利用可能なものとして、DBMS_CRYPTO というパッケージがあります。

これを利用してみたところ、なかなかに使い方が面倒くさいことが分かったので、この記事ではDBMS_CRYPTOを使いやすくするファンクションのコードを紹介します。

# 暗号化/復号化の利用イメージ

DBMS_CRYPTOは暗号化や復号化が出来るということなので、以下のようにできるのかなぁ？と思ったけど、どうやら違うようだ。

```sql
※ 以下は動作しません
insert into employee_card(id,cardnumber) values (100001, DBMS_CRYPTO.ENCRYPT('123456789012'));
select id from employee_card where cardnumber = DBMS_CRYPTO.ENCRYPT('123456789012');
select DBMS_CRYPTO.DECRYPT(cardnumber) from employee_card where id = 100001;
```

実際は結構面倒なことをしないといけないらしい。
ただ、暗号化するアプリごとにそれを実装するのは超めんどい。。

なので、上記のように動作するようなファンクションを作ってしまえば良いのでは？と思い、DBUP_CRYPTOを作成しました。

# 前提条件

Oracle Database 19.15.0で検証しています。
また、暗号化のアルゴリズムなどは以下を選択しています。

|設定項目|設定内容|コマンド|
|---|---|---|
|暗号化アルゴリズム|AES 128bit|ENCRYPT_AES128|
|ブロック暗号連鎖|暗号ブロック連鎖|CHAIN_CBC|
|ブロック暗号パディング|PKCS#5|PAD_PKCS5|
|初期化ベクター|null|null|

この辺りの詳細情報は以下公式マニュアルをご参照ください。
[PL/SQLパッケージおよびタイプ・リファレンス - 42 DBMS_CRYPTO](https://docs.oracle.com/cd/F19136_01/arpls/DBMS_CRYPTO.html)

なお、暗号化鍵情報はDBの中に格納しておいた方が安心な気がしたので、encユーザーを作り、そこにenctabというテーブルを作ってその中に格納することにしています。

# 事前準備

鍵とかを事前に作る必要があるので、以下を行う

- 管理者権限のユーザーでencユーザーを作成し、鍵の値を入れるテーブルを作成して鍵の値を入力する
```sql
SQL> create user enc identified by ***** quota unlimited on users;
SQL> create table enc.enctab (enckey char(16));
SQL> insert into enc.enctab values ('1234567890123456');
SQL> commit;
```

- encユーザーにDBMS_CRYPTOの実行権限を付与する
```sql
SQL> grant execute on dbms_crypto to enc;
```
- パッケージを管理者権限のユーザーでインストールする
（この手順でencユーザーにdbup_cryptoが作成される）
```sql
SQL> @dbup_crypto.sql
```

- 暗号化パッケージを利用するユーザーにインストールしたファンクションの権限を付与する
```sql
SQL> grant execute on enc.dbup_crypto to パッケージユーザー;
```

# dbup_crypto.sqlのコード

```sql
create or replace package ENC.DBUP_CRYPTO 
as
  function encrypt (char_for_encryption in varchar2) return raw;
  function decrypt (char_for_decryption in raw) return varchar2;
  function readenqval return varchar2;
end;
/

create or replace package body ENC.DBUP_CRYPTO
is
    function readenqval return varchar2
    is
        v_data  varchar2(100);
    begin
        select enckey into v_data from ENC.ENCTAB where rownum < 2;
        return v_data;
    end readenqval;

    function encrypt (char_for_encryption in varchar2) return raw
    is
        v_myKey     varchar2(16);
        v_enctype   number := DBMS_CRYPTO.ENCRYPT_AES128 + DBMS_CRYPTO.CHAIN_CBC + DBMS_CRYPTO.PAD_PKCS5;
        v_keyval    raw(16);
        v_encval    raw(2000);
    begin
        v_myKey := dbup_crypto.readenqval;
        v_keyval := UTL_I18N.STRING_TO_RAW(v_myKey, 'AL32UTF8');
        v_encval := UTL_I18N.STRING_TO_RAW(char_for_encryption, 'AL32UTF8');
        return dbms_crypto.encrypt(src => v_encval, typ => v_enctype, key => v_keyval, iv => null);
    end encrypt;

    function decrypt (char_for_decryption in raw) return varchar2
    is 
        v_myKey     varchar2(16);
        v_enctype   number := DBMS_CRYPTO.ENCRYPT_AES128 + DBMS_CRYPTO.CHAIN_CBC + DBMS_CRYPTO.PAD_PKCS5;
        v_keyval    raw(16);
    begin
        v_myKey := dbup_crypto.readenqval;
        v_keyval :=  UTL_I18N.STRING_TO_RAW(v_myKey, 'AL32UTF8');
        return UTL_I18N.RAW_TO_CHAR(dbms_crypto.decrypt(src => char_for_decryption, typ => v_enctype, key => v_keyval, iv => null), 'AL32UTF8');
    end decrypt;

    end;
/
```

# DBUP_CRYPTOの利用サンプル
まずは暗号化データを格納するテーブルを作る
```sql
SQL> create table employee_card (id number, cardnumber raw(100));

Table created.
```

続いて、123456789012というデータを暗号化してテーブルに入れてみる
```sql
SQL> insert into employee_card values (100001, enc.DBUP_CRYPTO.ENCRYPT('123456789012'));

1 row created.

SQL> select * from employee_card;

        ID CARDNUMBER
---------- -----------------------------------
    100001 7E9634826EE8B79227B43C8B6A29AE17 ★暗号化されている
```
暗号化された状態でテーブルに格納された！

暗号化したデータをキーにして検索が出来るかも確認する。
```sql
SQL> select id from employee_card where cardnumber = enc.DBUP_CRYPTO.ENCRYPT('123456789012');

        ID
----------
    100001
```
検索できた！

復号したデータがselect出来るか確認する
```sql
SQL> select enc.DBUP_CRYPTO.DECRYPT(cardnumber) from employee_card where id = 100001;

ENC.DBUP_CRYPTO.DECRYPT(CARDNUMBER)
-------------------------------------
123456789012 ★暗号化する前の情報が確認できる
```
正しく復号出来ることが確認できた！

#結果
dbms_cryptoは使い勝手が悪いと思っていたが、使い勝手を良くするファンクションを使って開発を捗らせることができた