ブロックチェーン表への挿入操作をブラウザから実施し、その操作によって挿入した行の署名をブラウザの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





















