Lekce 18 - Derby DB - Bezpečnost - Zašifrování databáze
V předchozím kvízu, Kvíz - Ukládání a získávání objektů UDT Derby DB v Javě, jsme si ověřili nabyté zkušenosti z předchozích lekcí.
V tomto tutoriálu objasníme a předvedeme zabezpečení databáze přes
šifrování. Jak to funguje? Nastavíme jej při tvorbě databáze, prakticky
pouze přidáme tři parametry. Jedna se o dataEncryption
,
encryptionAlgorithm
a bootPassword
. Při tvorbě
databáze jsou tyto parametry vzaté DerbyDB v potaz a databáze se vytvoří
šifrovaná. Což znamená pouze to, že data která již tak nejsou snadno
přístupná se ještě zašifrují. Popis naleznete v DerbySecurityGuide.pdf.
Takže pokud se chcete poté k databázi připojit, je nutné přidat ještě
další parametr a další heslo. Jen připomenu pochopitelné vyšší nároky
šifrované databáze na systémové prostředky při I/O operacích.
/** Zde provedeme vytvoreni zasifrovane databaze **/ try { connect = DriverManager.getConnection("jdbc:derby:C:/Program Files/JavaJDK08/db/bin/databazeSifr;create=true;user=Uzivatel;password=Heslo;" + "dataEncryption=true;encryptionAlgorithm=Blowfish/CBC/NoPadding;bootPassword=mojeHeslo"); System.out.println("Databaze se podarila vyrobit"); } catch (IOException e) { System.out.println("Databaze se nepodarila vyrobit"); e.printStackTrace(); }
Objasnění parametrů připojení
Objasněme si nyní parametry, které jsme použili v připojovacím řetězci v kódu výše:
dataEncryption
- Umožňuje nastavit pouzetrue
nebofalse
.true
znamená šifrovat afalse
nešifrovat.encryptionAlgorithm
- Nastavení šifrovacího algoritmu v tomto tvaru:algorithmName/feedbackMode/padding
. Tento tvar si dále ještě vysvětlíme.bootPassword
- Heslo pro šifrovanou databází. Bez tohoto hesla se jednoduše nepřipojíte, pokud je databáze zašifrovaná. Heslo prakticky slouží ke generování šifrovacího klíče o určité délce.encryptionKey
- Pokud nechceme používat heslo, lze zadávat přímo šifrovací klíč. Nicméně v článku budeme pracovat s heslem.
Nastavení šifrovacího algoritmu (encryptionAlgorithm)
Derby podporuje pouze určité šifrovací algoritmy a není jich moc. Pokud chcete vědět více, je v DerbyDB implementovaná podpora pro šifrovací zprostředkovatele, které je nutné nastudovat (tzv. encrypted providers, např. bouncycastle, více viz. JCE - Java Cryptography Extension). Zde případně naleznete seznam různých algoritmů a módů podporovaných např. v Java10 v základním providerem. Jenom připomínám, že různé algoritmy mají různé délky klíčů, pokud chcete využívat klíče.
Při nastavení algoritmu uvádíme:
algorithmName
- jméno algoritmu (např. DES, DESede, AES, atd...)feedbackMode
- mód algoritmu, podporován pouze CBC, CFB, ECB, OFBpadding
- podporován pouze NoPadding mód
Nastavenení šifrování databáze a připojení přes IJ
Nejdříve si šifrování předvedeme přes konfigurační příkaz IJ.
Přihlásíme se a vytvoříme databázi. Pokud spouštíte databázi přes
startNetworkServer
, pak slovem vypnout je myšleno použít
příkaz stopNetworkServer
a nikoliv Ctrl +
C. Pak se již k dané databázi přihlásíme jako nově vytvoření
uživatelé. V mém příkladě se připojuji přímo a tudíž restart
databáze se provádí disconnectem:
ij> connect 'jdbc:derby:databaze11;create=true;user=Uzivatel1;password=Heslo1;dataEncryption=true;encryptionAlgorithm=Blowfish/CBC/NoPadding; bootPassword=hesloDerby'; ij> CREATE TABLE DerbyCRYPT1(sloupec1 CHAR(10), sloupec2 CHAR(20), sloupec3 INTEGER); ij> INSERT INTO DerbyCRYPT1 VALUES ('text11','text12',7),('text21','text22',6),('text31','text32',14); ij> INSERT INTO DerbyCRYPT1 VALUES ('text41','text42',12),('text51','text52',354),('text61','text62',778); ij> SELECT * FROM DerbyCRYPT1; ij> disconnect; ij> exit;
Nyní je nutné DerbyDB vypnout a znovu zapnout. Tzv. resetovat, protože nastavení šifrování se projeví až po novém spuštění DerbyDB. Pokud se chceme znovu připojit k databázi, příkaz k připojení vypadá takto:
ij> connect 'jdbc:derby:databaze11;user=Uzivatel1;password=Heslo1;bootPassword=hesloDerby'; ij> SELECT * FROM DerbyCRYPT1; ij> disconnect; ij> exit;
Pokud chceme změnit heslo u již stávající zašifrované databáze, příkaz k připojení vypadá takto. (Opět je nutno databázi vypnout a znovu zapnout):
ij> connect 'jdbc:derby:databaze11;user=Uzivatel1;password=Heslo1;bootPassword=hesloDerby;newBootPassword=novehesloDerby'; ij> show tables; ij> disconnect; ij> exit;
Jak vidíte na přiloženém obrázku, jako první jsem se o připojení pokusil bez zadaného bootPasswordu, spojení zamrzlo. Pomohlo až Ctrl + C. Poté jsem daný parametr vložil a přihlášení proběhlo v pořádku:

Nastavenení šifrování databáze a připojení programově
Vytvoříme si i testovací příklad jako Java SE projekt v IDE. V menu vybereme File -> New -> Java Project. Pojmenujeme projekt a nastavíme JRE Java8:

Přidáme opět externí knihovny do našeho projektu do CLASSPATH. Jedná se o tyto externí knihovny:
derbyclient.jar
derby.jar
derbytools.jar
derbyoptionaltools.jar

Níže je vidět řešený zdrojový kód. Přiložený program se dá použít v první části pro připojení a tvorbu šifrované databáze, pak vytvoří tabulku, vloží do ní data a pro kontrolu si je i vypíšeme. V druhé fázi se již připojujeme k funkční databázi a daná data si rovněž vypíšeme:
package test; import java.sql.*; public class SifrovanaDatabaze { private static Connection connect = null; private static Statement statement = null; static { try { // nutne pridat do CLASSPATH - derbyclient.jar Class.forName("org.apache.derby.jdbc.ClientDriver").newInstance(); } catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) { System.out.println("Problem s nactenim driveru - nutne pridat do CLASSPATH - derbyclient.jar"); } } @SuppressWarnings("unused") private static void pripojeniDatabaze(){ try { connect = DriverManager.getConnection("jdbc:derby:D:/Java/JavaJDK08/db/bin/databaze12;create=true;user=Uzivatel1;password=Heslo1" + ";dataEncryption=true;encryptionAlgorithm=DES/ECB/NoPadding;bootPassword=jineHeslo"); System.out.println("Podarilo se vytvorit databazi12"); statement = connect.createStatement(); } catch (Exception e) { System.err.println("Nepodarilo se vytvorit databazi12"); } } private static void opetovnePripojeni(){ try { connect = DriverManager.getConnection("jdbc:derby:D:/Java/JavaJDK08/db/bin/databaze12;user=Uzivatel1;password=Heslo1;bootPassword=jineHeslo"); System.out.println("Podarilo se pripojit k databazi12"); statement = connect.createStatement(); } catch (Exception e) { System.err.println("Nepodarilo se pripojit k databazi12"); } } @SuppressWarnings("unused") private static void vytvoreniTabulky(String s){ try { statement.executeUpdate("create table "+s+" ( " + " id INT PRIMARY KEY, sloupec1 VARCHAR(20), sloupec2 VARCHAR(20), sloupec3 VARCHAR(20), cislo INT )"); System.out.println("Podarilo se vytvorit tabulku : "+s); } catch (SQLException e) { System.out.println("Nepovedlo se vytvorit tabulku :"); e.printStackTrace(); } } @SuppressWarnings("unused") private static void naplneniDatTabulky(String s){ try { // kazdy executeUpdate provede ulozeni do databaze statement.executeUpdate("INSERT INTO "+s+"(id,sloupec1,sloupec2,sloupec3,cislo) VALUES (1,'text11','text12','text13',11)"); statement.executeUpdate("INSERT INTO "+s+"(id,sloupec1,sloupec2,sloupec3,cislo) VALUES (2,'text21','text22','text23',27)"); statement.executeUpdate("INSERT INTO "+s+"(id,sloupec1,sloupec2,sloupec3,cislo) VALUES (3,'text31','text32','text33',33)"); statement.executeUpdate("INSERT INTO "+s+"(id,sloupec1,sloupec2,sloupec3,cislo) VALUES (4,'text41','text42','text43',42)"); statement.executeUpdate("INSERT INTO "+s+"(id,sloupec1,sloupec2,sloupec3,cislo) VALUES (5,'text51','text52','text53',15)"); statement.executeUpdate("INSERT INTO "+s+"(id,sloupec1,sloupec2,sloupec3,cislo) VALUES (6,'text61','text62','text63',45)"); System.out.println("Podarilo se ulozit data :"); } catch (SQLException e) { System.out.println("Nepovedlo se ulozit data :"); e.printStackTrace(); } } private static void vypisemeDatabazi(String s){ ResultSet odpoved = null; try { odpoved = statement.executeQuery("SELECT * FROM "+s+" ORDER BY id"); System.out.println("Podarilo se ziskat data :"); while(odpoved.next()) { System.out.print(odpoved.getString(2)+"\t"+odpoved.getString(3)+"\t"+odpoved.getString(4)+"\t"+odpoved.getInt(5)+"\n"); } } catch (SQLException e) { System.out.println("Nepovedlo se ziskat data :"); e.printStackTrace(); } } private static void odpojimeDatabazi() { try { if (statement != null) { statement.close(); } if (connect != null) { connect.close(); } System.out.println("\nPodarilo se odpojit od databaze"); } catch (SQLException e) { e.printStackTrace(); } } public static void main(String[] args) { System.out.println("Start programu"); // 1 spusteni - vytvoreni databaze, vlozeni dat a jejich zobrazeni /* pripojeniDatabaze(); vytvoreniTabulky("JinaTabulka"); naplneniDatTabulky("JinaTabulka"); vypisemeDatabazi("JinaTabulka"); */ // 2 spusteni - pripojeni k sifrovane databazi a zobrazeni dat opetovnePripojeni(); vypisemeDatabazi("JinaTabulka"); odpojimeDatabazi(); System.out.println("Konec programu"); } }
Samozřejmě nejdříve musíme spustit DerbyDB server, na který se
připojíme. To provedeme skriptem startNetworkServer
. Poté
necháme proběhnout náš program. Jak vidíte, vše funguje. Po úspěšném
proběhnutí musíme DerbyDB opět vypnout a to příkazem
stopNetworkServer
. Pouze tak docílíme, že se daná databáze
zašifruje.

Protože jsme databázi vypnuli, je nutné ji opět spustit pomocí
startNetworkServer
. Nyní se již lze připojit napřímo k
zašifrované databázi a nechat si data uložena v databázi vypsat.

V příští lekci, Derby DB - Bezpečnost - Šifrovaná komunikace SSL/TLS, si pustíme DerbyDB na localhostu.
Měl jsi s čímkoli problém? Stáhni si vzorovou aplikaci níže a porovnej ji se svým projektem, chybu tak snadno najdeš.
Stáhnout
Stažením následujícího souboru souhlasíš s licenčními podmínkami
Staženo 3x (6.27 kB)
Aplikace je včetně zdrojových kódů v jazyce Java