2020年12月25日金曜日

ブロックチェーン表に21cのDBMS_CRYPTO.SIGNを使って署名を付けてみる

 表題の件を試してみました。その作業ログです。

miyacleさんのこちらの記事を参考にさせてもらっています。ありがとうございます。

最初に鍵長が2048ビットの秘密鍵を作成します。作成されるファイル名はprivate.pemです。

% openssl genrsa -out private.pem 2048

Generating RSA private key, 2048 bit long modulus

.............+++

..............................................................................................................................................+++

e is 65537 (0x10001)

%


証明書署名要求(CSR)を作ります。private.pemを元にcert.csrが作成されます。

% openssl req -new -days 365 -key private.pem -out cert.csr

You are about to be asked to enter information that will be incorporated

into your certificate request.

What you are about to enter is what is called a Distinguished Name or a DN.

There are quite a few fields but you can leave some blank

For some fields there will be a default value,

If you enter '.', the field will be left blank.

-----

Country Name (2 letter code) []:**

State or Province Name (full name) []:******

Locality Name (eg, city) []:******

Organization Name (eg, company) []: *******

Organizational Unit Name (eg, section) []:*******

Common Name (eg, fully qualified host name) []:****.*****.***

Email Address []:*****.******@****.***


Please enter the following 'extra' attributes

to be sent with your certificate request

A challenge password []:****

%


Country Name以下はそれぞれ適当に設定します。サーバー証明書に使うわけではないので、それほど厳密でなくても問題ありません。

証明書を作成します。作成されるファイルはcert.crtになります。

% openssl x509 -in cert.csr -out cert.crt -req -signkey private.pem -days 365


Signature ok

subject=/C=**/ST=*****/L=********/O=******/OU=****/CN=****.******.****/emailAddress=****.********@*****.****

Getting Private key

%


これで署名に使う証明書や鍵のデータの準備はできました。

実装の検証にAutonomous Databaseを使います。また、ほとんどの作業はOracle APEXのSQLワークショップに含まれるSQLコマンドからPL/SQL、SQLを実行します。

Oracle APEXを使う際にAPEXDEVというユーザーを作成しています。ユーザーを作成すれば、SQL Developer Webからでも同様に作業可能なはずです。

最初にSQL Developer WebにADMINユーザーでログインし、作業ユーザーにDBMS_CRYPTOパッケージの実行権限を与える作業が必要です。

grant execute on dbms_crypto to <ユーザー名>;

これ以降は、作成した一般のユーザーにて作業を行います。

まず、作成した証明書を登録します。どこかHTTPS(HTTPではだめです)でアクセスできる場所にcert.crtを配置します。次に以下のPL/SQLスクリプトを実行します。

declare
l_cert blob;
l_cert_guid raw(16);
begin
l_cert := apex_web_service.make_rest_request_b(
p_url => 'https://どこかにファイルを配置します/cert.crt',
p_http_method => 'GET'
);
dbms_user_certs.add_certificate(l_cert, l_cert_guid);
dbms_output.put_line('Certificate GUID = ' || l_cert_guid);
end;

apex_web_service.make_rest_request_bファンクションを呼び出して、証明書ファイルを直接BLOBとして取り込みます。それをdbms_user_certs.add_certificateにて、スキーマに登録します。印刷されるGUIDは後で使用するので覚えておきます。(ALL_CERTIFICATESビューを検索して見つけることもできます。)

ブロックチェーン表は以前のこの記事で作成したBC_HOUSEHOLD_EXPENSESを使います。行に署名を追加するプロシージャとしてsign_bc_household_expensesを作成しました。主キーであるEXPENSE_IDを引数に取ります。

create or replace procedure sign_bc_household_expenses 
(p_expense_id in number) 
as  
  -- sign row 
  l_inst_id binary_integer; 
  l_chain_id binary_integer; 
  l_sequence_no binary_integer; 
  l_row_data blob; 
  -- RSA sign 
  l_raw raw(2000); 
  prvkey varchar2(2000) := '一行にしたprivate.pemの内容';
  sign_raw raw(2000); 
  sType      PLS_INTEGER := DBMS_CRYPTO.SIGN_SHA512_RSA; 
  kType      PLS_INTEGER := DBMS_CRYPTO.KEY_TYPE_RSA; 
  cert_guid raw(16) := hextoraw('証明書のGUID'); 
begin 
  -- sign row 
  select orabctab_inst_id$, orabctab_chain_id$, orabctab_seq_num$ 
  into l_inst_id, l_chain_id, l_sequence_no 
  from bc_household_expenses where expense_id = p_expense_id; 
  dbms_blockchain_table.get_bytes_for_row_signature( 
      'APEXDEV', 
      'BC_HOUSEHOLD_EXPENSES', 
      l_inst_id, 
      l_chain_id, 
      l_sequence_no, 
      1, 
      l_row_data 
  ); 
  l_raw := dbms_lob.substr(l_row_data, dbms_lob.getlength(l_row_data)); 
  sign_raw := dbms_crypto.sign 
  ( 
    src => l_raw, 
    prv_key => utl_i18n.string_to_raw(prvkey, 'AL32UTF8'), 
    pubkey_alg => kType, 
    sign_alg => sType 
  ); 
  dbms_blockchain_table.sign_row( 
      'APEXDEV', 
      'BC_HOUSEHOLD_EXPENSES', 
      l_inst_id, 
      l_chain_id, 
      l_sequence_no, 
      NULL, 
      sign_raw, 
      cert_guid, 
      DBMS_BLOCKCHAIN_TABLE.SIGN_ALGO_RSA_SHA2_512 
   ); 
end sign_bc_household_expenses;

作成したプロシージャを使って署名を付けます。

begin
sign_bc_household_expenses(1);
end;

一度署名を付けたあと、再度実行すると以下のエラーが出力されました。

ORA-05734: 署名はインスタンスID '1'、チェーンID '21'、順序番号'1'の行にすでに存在します ORA-06512: "SYS.DBMS_BLOCKCHAIN_TABLE", 行747 ORA-06512: "SYS.DBMS_BLOCKCHAIN_TABLE", 行814 ORA-06512: "APEXDEV.SIGN_BC_HOUSEHOLD_EXPENSES", 行38 ORA-06512: 行2 ORA-06512: "SYS.DBMS_SQL", 行1766

DBMS_BLOCKCHAIN_TABLE.VERIFY_ROWSで検証をしてみます。

declare
l_num number;
begin
dbms_blockchain_table.verify_rows(
schema_name => 'APEXDEV',
table_name => 'BC_HOUSEHOLD_EXPENSES',
number_of_rows_verified => l_num
);
dbms_output.put_line('Number of rows verified = ' || l_num);
end;

Number of rows verified = 4
といった形でレポートされます。

最初は以前の記事で作成したトリガーに埋め込もうとしたのですが、DBMS_BLOCKCHAIN_TABLE.SIGN_ROWはコミット済みのデータを対象とするようで、トリガー内ではGET_BYTES_FOR_ROW_SIGNATUREがデータを見つけることができません、とエラーになりました。そのため、別プロシージャとして作成しています。

以上で、ブロックチェーン 表の署名をDBMS_CRYPTO.SIGNを使って生成した作業ログは終了です。