ブロックチェーン表への挿入操作をブラウザから実施し、その操作によって挿入した行の署名をブラウザのWeb Cryptography APIを使って行う方法を試してみました。その作業記録です。
以下、作業手順です。
ブロックチェーン表BC_BROWSER_TRANSACTIONSを、以下のDDLで作成しました。
create blockchain table BC_BROWSER_TRANSACTIONS
(
browser_transaction_id number generated by default on null as identity,
transaction_name varchar2(80),
transaction_volume number,
register_date date
)
no drop until 31 days idle no delete locked HASHING USING "SHA2_512" version "v1";
鍵の生成には以下のコマンドを使っています。
% openssl genrsa -out private.pem 2048
Generating RSA private key, 2048 bit long modulus
..........................+++
..............................................+++
e is 65537 (0x10001)
%
証明書の元になる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) []:JP
State or Province Name (full name) []:Tokyo
Locality Name (eg, city) []:Minato-ku
Organization Name (eg, company) []:*******
Organizational Unit Name (eg, section) []:******
Common Name (eg, fully qualified host name) []:****.****.*****
Email Address []:****.*****@******.com
Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
%
データベースが署名の検証に使用する証明書を作ります。
% openssl x509 -in cert.csr -out cert.crt -req -signkey private.pem -days 365
Signature ok
subject=/C=JP/ST=Tokyo/L=Minato-ku/O=Oracle/OU=Cloud/CN=apex.oracle.com/emailAddress=yuji.nakakoshi@oracle.com
Getting Private key
%
Web Cryptography APIで扱えるように、秘密鍵をPKCS8形式に変換します。
% openssl pkcs8 -topk8 -nocrypt -in private.pem -outform DER -out private.der
%
バイナリを16進数表現に変換します。
% xxd -ps private.der > private.hex
%
変換された16進数表現を改行なしの1行にします。
% while read l
while> do
while> echo -n $l
while> done < private.hex > private.l
%
以下のPL/SQLスクリプトをOracle APEXのSQLワークショップに含まれるSQLコマンドより実行し、作成した証明書をデータベースに登録します。httpsにてアクセスできる場所に証明書を配置します。
ハッシュを取り出すファンクションBC_GET_HASHを作成します。
create or replace function bc_get_hash (p_browser_transaction_id in number) return varchar2 as l_inst_id binary_integer; l_chain_id binary_integer; l_sequence_no binary_integer; l_row_data blob; l_raw raw(2000); begin select orabctab_inst_id$, orabctab_chain_id$, orabctab_seq_num$ into l_inst_id, l_chain_id, l_sequence_no from bc_browser_transactions where browser_transaction_id = p_browser_transaction_id; -- dbms_blockchain_table.get_bytes_for_row_signature( 'APEXDEV', 'BC_BROWSER_TRANSACTIONS', 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)); return rawtohex(l_raw); end bc_get_hash;
次に、外部で生成された署名を引数として、それをブロックチェーン表に加えるプロシージャBC_SIGN_ROWを作成します。
create or replace procedure bc_sign_row (p_browser_transaction_id in number, p_signature in varchar2 ) as l_inst_id binary_integer; l_chain_id binary_integer; l_sequence_no binary_integer; sign_raw raw(2000); cert_guid raw(16) := hextoraw('証明書のGUID'); begin select orabctab_inst_id$, orabctab_chain_id$, orabctab_seq_num$ into l_inst_id, l_chain_id, l_sequence_no from bc_browser_transactions where browser_transaction_id = p_browser_transaction_id; sign_raw := hextoraw(p_signature); dbms_blockchain_table.sign_row( 'APEXDEV', 'BC_BROWSER_TRANSACTIONS', l_inst_id, l_chain_id, l_sequence_no, NULL, sign_raw, cert_guid, DBMS_BLOCKCHAIN_TABLE.SIGN_ALGO_RSA_SHA2_512 ); end bc_sign_row;
ブラウザで署名をつけるために使用するコードは以下です。
// 16進数表現からバイナリに変換する // 参照先: https://stackoverflow.com/questions/38987784/how-to-convert-a-hexadecimal-string-to-uint8array-and-back-in-javascript const fromHexString = hexString => new Uint8Array(hexString.match(/.{1,2}/g).map(byte => parseInt(byte, 16))); // バイナリから16進数表現に変換する // 参照先: https://stackoverflow.com/questions/40031688/javascript-arraybuffer-to-hex function buf2hex(buffer) { // buffer is an ArrayBuffer return Array.prototype.map.call(new Uint8Array(buffer), x => ('00' + x.toString(16)).slice(-2)).join(''); } // 16進数表現のhashHexよりバイナリのhashDataを生成する var hashHex = '95E144F09593033ABFC70AA78922ECDC6108A5E005BE30C10228E357414992216E8DCEF7A3A5163614FD553239DCA7EC57B8E28A8644EEC8AE19EFE40A1F5F6D'; var hashData = fromHexString(hashHex); // 16進数表現のprvHexよりバイナリのprvDataを生成する var prvHex = '16進数表記の秘密鍵のデータ'; var prvData = fromHexString(prvHex); // 署名をつける // 参照先: http://blog.livedoor.jp/k_urushima/archives/1759093.html window.crypto.subtle.importKey( "pkcs8", prvData, { name: "RSASSA-PKCS1-v1_5", hash: {name: "SHA-512"} }, true, ["sign"] ).then( function(prvKey) { return window.crypto.subtle.sign("RSASSA-PKCS1-v1_5", prvKey, hashData); } ).then( function(sigVal) { console.log(buf2hex(sigVal)); } );
Oracle APEXのアプリケーションに組み込む前に、動作の確認を行います。
最初にブロックチェーン表BC_BROWSER_TRANSACTIONS表に一行挿入し、主キーであるBROWSER_TRANSACTION_IDを取得します。
以下のように表示されます。
BROWSER_TRANSCTION_ID = 2
次に、ファンクションBC_GET_HASHを呼び出して、行のハッシュ値を取得します。
以下のようにハッシュ値が返されます。
82FE46794AE7052DAB42886E57C7E2C557CF98234989B6067330A1EC1E8D963CE0A4665534181756EB3D618BD7FE1253F64968D3DFF13F0E7AF9C463F5480D4E
このハッシュ値をhashHex、秘密鍵であるファイルprivate.lをprvHexに与えて、ブラウザの開発者ツールで、先ほどのJavaScriptを実行します。(httpsのページが開いている状態で開発ツールを動かす必要があります)。
コンソールに出力された署名をコピーします。
文が処理されました。
Number of rows verified = 2
declare l_id number; begin begin insert into bc_browser_transactions ( transaction_name, transaction_volume, register_date ) values ( :P3_TRANSACTION_NAME, :P3_TRANSACTION_VOLUME, :P3_REGISTER_DATE ) returning browser_transaction_id into l_id; commit; end; :P3_HASH := bc_get_hash(l_id); :P3_BROWSER_TRANSACTION_ID := l_id; end;
// 16進数表現からバイナリに変換する // 参照先: https://stackoverflow.com/questions/38987784/how-to-convert-a-hexadecimal-string-to-uint8array-and-back-in-javascript const fromHexString = hexString => new Uint8Array(hexString.match(/.{1,2}/g).map(byte => parseInt(byte, 16))); // バイナリから16進数表現に変換する // 参照先: https://stackoverflow.com/questions/40031688/javascript-arraybuffer-to-hex function buf2hex(buffer) { // buffer is an ArrayBuffer return Array.prototype.map.call(new Uint8Array(buffer), x => ('00' + x.toString(16)).slice(-2)).join(''); } // 16進数表現のprvHexよりバイナリのprvDataを生成する var prvHex = '16進数表記の秘密鍵のデータ'; var prvData = fromHexString(prvHex);
// 署名をつける // 参照先: http://blog.livedoor.jp/k_urushima/archives/1759093.html window.crypto.subtle.importKey( "pkcs8", prvData, { name: "RSASSA-PKCS1-v1_5", hash: {name: "SHA-512"} }, true, ["sign"] ).then( function(prvKey) { return window.crypto.subtle.sign("RSASSA-PKCS1-v1_5", prvKey, fromHexString($v('P3_HASH'))); } ).then( function(sigVal) { apex.page.submit( { request: "CREATE", set: { "P3_BROWSER_TRANSACTION_ID": $v("P3_BROWSER_TRANSACTION_ID"), "P3_SIGNATURE": buf2hex(sigVal) }, showWait: false }) } );
begin bc_sign_row(:P3_BROWSER_TRANSACTION_ID, :P3_SIGNATURE); end;
フォームについてはこれで完成です。最後にレポート上で署名の有無を確認するため、列ORABCTAB_SIGNATURE_CERT$を検索される列に追加します。レポートのページをページ・デザイナで開きます。
select BROWSER_TRANSACTION_ID, TRANSACTION_NAME, TRANSACTION_VOLUME, REGISTER_DATE, ORABCTAB_SIGNATURE_CERT$ from BC_BROWSER_TRANSACTIONS