Nur das Einfügen von Daten in Tabellen (also INSERT-Only) zu erlauben und keine weiteren Manipulationen zuzulassen sind wichtige Anforderungen – auch an die Oracle Datenbank. Mit Oracle Database 21c war es nun soweit: Mit der interessanten Erweiterung Datenbanktabellen vom Typ Blockchain kann diese Anforderung erfüllt werden. Zusätzliche Security-Eigenschaften dieses Tabellentyps ermöglichen darüberhinaus Blockchain-Anwendungen nun auch zentral in der Datenbank zu verwalten. Typische Anwendungsfälle sind beispielsweise zentrale unveränderbare Daten wie z.B. Messwerte von IoT-Geräten, Compliance-Daten aus Revisionsgründen usw. innerhalb der Datenbank zu speichern.
Was sind noch einmal Blockchains? In einem Satz ausgedrückt: Blockchains sind per Definition kontinuierlich erweiterbare Listen von Datensätzen, die mittels kryptographischer Verfahren miteinander verkettet sind. Jeder Datensatz (Block) enthält dabei typischerweise einen kryptographisch sicheren Hash des vorangegangenen Blocks, einen Zeitstempel und Transaktionsdaten (Quelle Wikipedia).
Oracle Blockchain Tabellen erfüllen diese Voraussetzungen: Sie sind INSERT-Only und speichern zusätzlich zu den aktuellen Spaltenwerten kryptographische Hashwerte von früher eingefügten Blöcken (auch Chaining) in sogenannten hidden Spalten ab um Manipulationssicherheit zu gewährleisten. Wenn überhaupt können gewisse Zeilen oder die gesamte Tabelle nur nach einer gewissen (user-definierten) Zeit gelöscht werden. Da es sich um Datenbanktabellen handelt, ist der Zugriff über SQL oder PL/SQL ohne Änderung am SQL Code transparent wie üblich möglich.
Wie erzeugt man eine Blockchain Tabelle? Eine Blockchain Tabelle wird mit CREATE TABLE mit einer erweiterten Syntax erzeugt. Folgende Klauseln sind dabei mandatory:
– NO DROP [UNTIL n DAYS IDLE]
– NO DELETE [LOCKED] [UNTIL n DAYS AFTER INSERT]
– HASHING USING “SHA2_512” VERSION v1.
Im ersten Release sind dabei Datentypen wie NUMBER, VARCHAR2, RAW, JSON, BLOB, CLOB, DATE und weitere skalare Datentypen möglich; andere Datentypen wie ADT, BFILE etc. sind noch nicht erlaubt.
Im folgenden Beispiel legen wir eine Tabelle mit Namen LEDGER_TAB1 an, die erst nach frühestens 25 Tagen Idle Time (der Default ist 16) gelöscht werden kann. Das Löschen einzelner Zeilen ist erst nach 31 Tagen möglich. Das genaue Kommando sieht dann folgendermassen aus.
SQL> CREATE BLOCKCHAIN TABLE ledger_tab1 (bank VARCHAR2(128), d_date DATE, d_amount NUMBER)
NO DROP UNTIL 25 DAYS IDLE
NO DELETE UNTIL 31 DAYS AFTER INSERT
HASHING USING "SHA2_512" VERSION v1;
Table created.
Die Data Dictionary Tabelle USER(ALL|DBA|CDB)_BLOCKCHAIN_TABLES gibt einen Überblick über die Tabellen vom Typ Blockchain.
SQL> SELECT *
FROM user_blockchain_tables;
TABLE_NAME ROW_RETENTION ROW TABLE_INACTIVITY_RETENTION HASH_ALG
--------------- ------------- --- -------------------------- --------
LEDGER_TAB1 25 NO 16 SHA2_512
LEDGER_TAB2 16 NO 16 SHA2_512
ORDERS_BL 31 NO 31 SHA2_512
Die Tabelle besteht dabei aus den 3 user-definierten Spalten BANK, D_DATE und D_AMOUNT. Zusätzlich werden 10 weitere hidden Spalten angelegt, um die zusätzlichen Informationen für die Blockchain abzuspeichern. Hidden Spalten lassen sich entweder aus der Tabellen USER_TAB_COLS oder mit der SQL*PLUS Variablen SET COLINVISIBLE ON sichtbar machen.
SQL> desc ledger_tab1
Name Null? Type
----------------------------------------- -------- ----------------------------
BANK VARCHAR2(128)
D_DATE DATE
D_AMOUNT NUMBER
SQL> SELECT column_name, hidden_column
FROM user_tab_cols WHERE table_name='LEDGER_TAB1';
COLUMN_NAME HID
--------------------------------------------- ---
BANK NO
D_DATE NO
D_AMOUNT NO
ORABCTAB_INST_ID$ YES
ORABCTAB_CHAIN_ID$ YES
ORABCTAB_SEQ_NUM$ YES
ORABCTAB_CREATION_TIME$ YES
ORABCTAB_USER_NUMBER$ YES
ORABCTAB_HASH$ YES
ORABCTAB_SIGNATURE$ YES
ORABCTAB_SIGNATURE_ALG$ YES
ORABCTAB_SIGNATURE_CERT$ YES
ORABCTAB_SPARE$ YES
set colinvisible on
SQL> desc ledger_tab1
Name Null? Type
------------------------------------ ----- ---------------------------
D_DATE DATE
D_AMOUNT NUMBER
ORABCTAB_SPARE$ (INVISIBLE) RAW(2000 BYTE)
ORABCTAB_USER_NUMBER$ (INVISIBLE) NUMBER
ORABCTAB_HASH$ (INVISIBLE) RAW(2000 BYTE)
ORABCTAB_SIGNATURE$ (INVISIBLE) RAW(2000 BYTE)
ORABCTAB_SIGNATURE_ALG$ (INVISIBLE) NUMBER
ORABCTAB_SIGNATURE_CERT$ (INVISIBLE) RAW(16 BYTE)
ORABCTAB_SEQ_NUM$ (INVISIBLE) NUMBER
ORABCTAB_CHAIN_ID$ (INVISIBLE) NUMBER
ORABCTAB_INST_ID$ (INVISIBLE) NUMBER
ORABCTAB_CREATION_TIME$ (INVISIBLE) TIMESTAMP(6) WITH TIME ZONE
Wenn Zeilen eingefügt werden, werden krypthographische Hashwerte aus den vorangegangenen Blöcken gemeinsam mit den aktuellen Daten gespeichert – ein sogenanntes Chaining wird durchgeführt. Dieser Hashwert ist eine (SHA2-512)-Berechnung aus dem aktuellem Zeileninhalt des Users inklusive hidden Spalten Informationen (ohne Signatur Informationen) und der hidden Hashwert aus der vorangegangenen Zeile. Jede Änderung (Manipulation) der vorangegangenen Daten würden dann dazu führen, dass sich der Hashwert ändert und nicht mehr mit dem gespeicherten Hashwert der aktuellen Daten übereinstimmt.
Wie kann man nun mit diesen Tabellen arbeiten? Die Tabellen können natürlich wie immer mit SELECT-Operationen abgefragt werden, keinerlei Änderungen am SQL Code ist erforderlich. Möchte man die Informationen aus den “hidden” Spalten anzeigen, muss man diese explizit in der SELECT-Klausel angeben. Zum Befüllen der Tabelle – hier mit Einzelsatz INSERT – ist wie üblich das Objektprivileg INSERT erforderlich. Operationen wie DROP TABLE, DELETE, MERGE und UPDATE, Änderung an den Spalten, Bulk INSERTs usw. sind nicht möglich. Die genauen Details dazu kann man im Administrator Guide unter Guidelines for Managing Blockchain Tables nachlesen: In folgendem Ausschnitt werden exemplarisch einige SELECT Operationen – auch gemischte – ausgeführt.
SQL> SELECT bank, d_date, d_amount
FROM ledger_tab1 WHERE rownum=1;
BANK D_DATE D_AMOUNT
---------- --------- ----------
DB 17-NOV-19 1000
SQL> SELECT o.order_id, c.name, o.status, o.order_date
FROM orders_bl o JOIN ot.customers c USING (customer_id)
ORDER_ID NAME STATUS ORDER_DAT
---------- ------------------------------ -------------------- ---------
26 Supervalu Shipped 16-AUG-16
71 Supervalu Shipped 21-FEB-17
96 Supervalu Shipped 14-SEP-16
81 NextEra Energy Shipped 13-DEC-16
74 NextEra Energy Shipped 10-FEB-17
...
SQL> SELECT bank, d_date, d_amount,
ORABCTAB_SPARE$,ORABCTAB_USER_NUMBER$, ORABCTAB_HASH$,
ORABCTAB_SIGNATURE$, ORABCTAB_SIGNATURE_ALG$, ORABCTAB_SIGNATURE_CERT$,
ORABCTAB_SEQ_NUM$, ORABCTAB_CHAIN_ID$, ORABCTAB_INST_ID$, ORABCTAB_CREATION_TIME$
FROM us1.ledger_tab1 WHERE rownum<2;
BANK D_DATE D_AMOUNT
---------- --------- ----------
ORABCTAB_SPARE$
----------------------------------------------------------------------------------------------------
ORABCTAB_USER_NUMBER$
---------------------
ORABCTAB_HASH$
----------------------------------------------------------------------------------------------------
ORABCTAB_SIGNATURE$
----------------------------------------------------------------------------------------------------
ORABCTAB_SIGNATURE_ALG$ ORABCTAB_SIGNATURE_CERT$ ORABCTAB_SEQ_NUM$ ORABCTAB_CHAIN_ID$
----------------------- -------------------------------- ----------------- ------------------
ORABCTAB_INST_ID$ ORABCTAB_CREATION_TIME$
----------------- ---------------------------------------------------------------------------
DB 17-NOV-19 1000
110
739EBB6DFE29DED923318614487C812DD922A13658E00A176ABF19DE8BFFF001DBB8E0270BF54E193B74E2915EBFD1260978
75184DC6CBBB729DEE7D94B1E0FA
1 23
1 25-FEB-20 11.05.47.115998 AM +00:00
Standard SQL-Operationen wie DELETE und UPDATE werden wie alle nicht erlaubten Operationen mit folgender Fehlermeldung abgewiesen.
SQL> delete from ledger_tab1 where rownum=1;
delete from ledger_tab1 where rownum=1
*
ERROR at line 1:
ORA-05715: operation not allowed on the blockchain table
SQL> update ledger_tab1 set bank='X';
update ledger_tab1 set bank='X'
*
ERROR at line 1:
ORA-05715: operation not allowed on the blockchain table
Tabellen bzw. Datenbankschemas, die Blockchain Tabellen besitzen, können erst nach Ablauf der IDLE Time mit DROP-Operationen gelöscht werden. Zeilen ausserhalb der Retention Zeit lassen sich unabhängig davon mit der neuen Funktionalität DBMS_BLOCKCHAIN_TABLE.DELETE_ROWS löschen.
Es gibt darüberhinaus auch einige neue Funktionen, die über die PL/SQL Schnittstelle mit neuen Packages zur Verfügung gestellt werden. So kann man beispielsweise das neue Package DBMS_BLOCKCHAIN_TABLE verwenden um
- Zeilen zu löschen
- Zeilen zu verifizieren
- oder eine Signatur hinzuzufügen.
Hinzufügen einer Zeilen-Signatur (optional)
Eine Signatur zu einer Tabellenzeile hinzuzufügen ist optional. Da dies aber zu mehr Sicherheit führt, soll in den folgenden Abschnitten, die Vorgehensweise exemplarisch demonstriert werden. Die Zeile mit dem Spalteneintrag DB1 (für die Spalte BANK) soll eine Zeilen Signatur erhalten, die dann in den dazu vorgesehenen hidden Spalten gespeichert wird. Diese Spalten sind im Moment noch leer.
Insgesamt sind drei Schritte dazu erforderlich. Zuerst wird ein Zertifikat zur Datenbank hinzugefügt, um damit die Signatur der Zeile zu berechnen und zu verifizieren. Dazu wird die neue Funktion DBMS_USER_CERTS.ADD_SIGNATURE verwendet, die eine Zertifikat GUID als Resultat liefert. Mit OPENSSL wird dazu ein digitales Zertifikat example.com.pem zum Testen wie folgt erzeugt.
openssl req -x509 -sha256 -nodes -newkey rsa:4096 -keyout example.com.key -days 730 -out example.com.pem
Jetzt kann das Zertifikat example.com.pem aus dem logischen Directory BLOCK hinzugefügt werden.
set serveroutput on
DECLARE
file BFILE;
buffer BLOB;
amount NUMBER := 32767;
cert_guid RAW(16);
BEGIN
file := BFILENAME('BLOCK', 'example.com.pem');
DBMS_LOB.FILEOPEN(file);
DBMS_LOB.READ(file, amount, 1, buffer);
DBMS_LOB.FILECLOSE(file);
DBMS_USER_CERTS.ADD_CERTIFICATE(buffer, cert_guid);
DBMS_OUTPUT.PUT_LINE('Certificate GUID = ' || cert_guid);
END;
/
Certificate GUID = 9F7B7815464B7E48E0530200000ACE49
Die Zertifikat GUID lässt sich danach aus der Data Dictionary View DBA_CERTIFICATES wieder auslesen.
SQL> SELECT user_name, distinguished_name, certificate_guid
FROM dba_certificates;
USER_NAME DISTINGUISHED_NAME CERTIFICATE_GUID
--------------- --------------------------------------------- --------------------------------
US1 CN=OR,OU=SL,O=OR,L=MUC,ST=BY,C=DE 9F7B7815464B7E48E0530200000ACE49
Jetzt wird der Hash-Wert der Zeile berechnet und die Bytes in die Datei signature1.dat geschrieben.
DECLARE
row_data BLOB;
buffer RAW(4000);
inst_id BINARY_INTEGER;
chain_id BINARY_INTEGER;
sequence_no BINARY_INTEGER;
row_len BINARY_INTEGER;
l_file UTL_FILE.FILE_TYPE;
BEGIN
SELECT ORABCTAB_INST_ID$, ORABCTAB_CHAIN_ID$, ORABCTAB_SEQ_NUM$ INTO inst_id, chain_id, sequence_no
FROM US1.ledger_tab1 where bank='DB1';
DBMS_BLOCKCHAIN_TABLE.GET_BYTES_FOR_ROW_SIGNATURE('US1','LEDGER_TAB1',inst_id, chain_id, sequence_no, 1, row_data);
row_len := DBMS_LOB.GETLENGTH(row_data);
DBMS_LOB.READ(row_data, row_len, 1, buffer);
l_file := UTL_FILE.fopen('BLOCK','signature1.dat','wb', 32767);
UTL_FILE.put_raw(l_file, buffer, TRUE);
UTL_FILE.fclose(l_file);
END;
/
Die Signatur wird dann außerhalb der Datenbank mit Hilfe von OPENSSL unter Verwendung des Zertifikats berechnet und in Binärformat in der Datei sign1.final gespeichert.
openssl dgst -sha512 -sign example.com.key -out sign1.final signature1.dat
Danach wird die Signatur sign1.final zu der Zeile (mit Wert DB1 für die Spalte Bank) mit DBMS_BLOCKCHAIN_TABLE.SIGN_ROW eingefügt.
DECLARE
inst_id binary_integer;
chain_id binary_integer;
sequence_no binary_integer;
signature RAW(2000);
cert_guid RAW (16) := HEXTORAW('9F7B7815464B7E48E0530200000ACE49');
file bfile;
amount number:=2000;
buffer RAW(4000);
BEGIN
SELECT ORABCTAB_INST_ID$, ORABCTAB_CHAIN_ID$, ORABCTAB_SEQ_NUM$ INTO inst_id, chain_id, sequence_no
FROM us1.ledger_tab1 WHERE bank='DB1';
file := bfilename('BLOCK', 'sign1.final');
DBMS_LOB.FILEOPEN(file);
dbms_lob.READ(file, amount, 1, signature);
dbms_lob.FILECLOSE(file);
DBMS_BLOCKCHAIN_TABLE.SIGN_ROW('US1','LEDGER_TAB1', inst_id, chain_id, sequence_no, NULL, signature, cert_guid, DBMS_BLOCKCHAIN_TABLE.SIGN_ALGO_RSA_SHA2_512);
END;
/
Selektiert man erneut die Tabellenzeile, findet man die entsprechenden Signatur Informationen in den Spalten ORABCTAB_SIGNATURE$, ORABCTAB_SIGNATURE_ALG$ und ORABCTAB_SIGNATURE_CERT$ vor.
SQL> select BANK,D_DATE, D_AMOUNT,
ORABCTAB_SPARE$,ORABCTAB_USER_NUMBER$, ORABCTAB_HASH$,
ORABCTAB_SIGNATURE$, ORABCTAB_SIGNATURE_ALG$, ORABCTAB_SIGNATURE_CERT$,
ORABCTAB_SEQ_NUM$, ORABCTAB_CHAIN_ID$, ORABCTAB_INST_ID$, ORABCTAB_CREATION_TIME$
from us1.ledger_tab1 where rownum<2;
BANK
--------------------------------------------------------------------------------
D_DATE D_AMOUNT
--------- ----------
ORABCTAB_SPARE$
--------------------------------------------------------------------------------
ORABCTAB_USER_NUMBER$
---------------------
ORABCTAB_HASH$
--------------------------------------------------------------------------------
ORABCTAB_SIGNATURE$
--------------------------------------------------------------------------------
ORABCTAB_SIGNATURE_ALG$ ORABCTAB_SIGNATURE_CERT$ ORABCTAB_SEQ_NUM$
----------------------- -------------------------------- -----------------
ORABCTAB_CHAIN_ID$ ORABCTAB_INST_ID$
------------------ -----------------
ORABCTAB_CREATION_TIME$
---------------------------------------------------------------------------
DB
17-NOV-19 1000
110
739EBB6DFE29DED923318614487C812DD922A13658E00A176ABF19DE8BFFF001DBB8E0270BF54E19
3B74E2915EBFD126097875184DC6CBBB729DEE7D94B1E0FA
5E17CB909442868A5B4A999E18A78FF695C366E6B1815DE26DDAB5151AE87E962475C3316D634668
A7ADD2F99B17686A86A7ECD64F9AED0FE03900AE295A0366C958212A5DBD0520FE186A646495CC90
B98C844BAB233667C4950502B12024D055158A209A4E6FBB4FDEEF5AE5E7E73587171BEB7B5F4079
5C7E92E0D6C0C57
3 9F7B7815464B7E48E0530200000ACE49 1
23 1
25-FEB-20 11.05.47.115998 AM +00:00
Fazit
Die Eigenschaften des neuen Tabellentyps Blockchain wie INSERT-Only und Manipulationssicherheit führen dazu, dass ganz neue Anwendungen in der Datenbank gespeichert werden können (siehe auch Converged Database). Zum Erzeugen der Tabelle ist nur eine Erweiterung des CREATE TABLE Kommandos notwendig. SQL und PL/SQL Kommandos können wie gewohnt gemeinsam mit Standard Oracle Technologien wie Partitionierung, RAC usw. verwendet werden. Falls Einschränkungen existieren, sind diese im Administration Guide zu finden. Zusätzliche Funktionen wie Löschen abgelaufener Zeilen, Hinzufügen von Zertifikaten, Validieren von Zeilen usw. werden über die neuen PL/SQL Packages DBMS_BLOCKCHAIN_TABLE und DBMS_USER_CERTS bereitgestellt.
Weitere Links und Informationen
- Handbuch Database Administrator’s Guide “Managing Blockchain Tables“
- Handbuch DBMS_BLOCKCHAIN_TABLE
