X

Technologie - Trends - Tipps&Tricks
in deutscher Sprache

Blockchain inside Oracle Database 20c

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. In Oracle Database 20c ist 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. Einen guten Überblick über die Anwendungsmöglichkeiten und das darunterliegende Konzept kann man im Blogposting "Native Blockchain Tables Extend Oracle Database’s Multi-model Converged Architecture" von Mark Rakhmilevich nachlesen. Dort findet sich auch eine Gegenüberstellung, wann es sinnvoll ist, Blockchain Anwendungen in der Datenbank oder in einer Blockchain Plattform zu betreiben.

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.

 

Der gesamte Beitrag findet sich auch unter folgendem Link "Blockchain inside Oracle Database 20c"

 

 

Be the first to comment

Comments ( 0 )
Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.