Java, bazy danych i SSL
1
1. HSQLDB i SSL.
2. MySQL
●
własności połączenia,
●
transmija z wykorzystaniem SSL.
3. Własne programy wykorzystujące SSL.
HSQLDB – SSL
2
HSQLDB umożliwia szyfrowanie transmisji. Aby taka transmisja była możliwa
zarówno serwer jak i klient muszą być odpowiednio skonfigurowane. Konfiguracja
serwera:
1. Generowanie certyfikatu serwera:
>keytool -genkey -alias hsqldb -keyalg RSA -validity 30 -keystore
hsqlserver.store
Enter keystore password:
hsqldb
What is your first and last name?
[Unknown]:
localhost
...
Is CN=localhost, OU=Unknown, O=Unknown, L=Unknown, ST=Unknown,
C=Unknown correct?
[no]:
yes
Enter key password for <hsqldb>
(RETURN if same as keystore password):
HSQLDB – SSL
3
Inny sposób generowania klucza – gdy posiadamy certyfikat podpisany przez
zewnętrzne Centrum Autoryzacji”
openssl pkcs8 -topk8 -outform DER -in Xpvk.pem -inform PEM
-out Xpvk.pk8 -nocrypt
openssl x509 -in Xcert.pem -out Xcert.der -outform DER
java DERImport server.store NEWALIAS Xpvk.pk8 Xcert.der
UWAGA: hasło dla klucza musi być takie samo jak hasło dla
server.store
!
HSQLDB – SSL
4
2. Uruchomienie serwera:
>java
-Djavax.net.ssl.keyStorePassword=hsqldb
-Djavax.net.ssl.keyStore=hsqlserver.store
-cp hsqldb.jar
org.hsqldb.Server
[Server@13c5982]: [Thread[main,5,main]]: checkRunning(false)
entered
...
[Server@13c5982]: Using TLS/SSL-encrypted JDBC
...
[Server@13c5982]: Startup sequence completed in 978 ms.
[Server@13c5982]: 2006-03-19 10:16:28.100 HSQLDB server 1.8.0
is online
HSQLDB – SSL
5
Po stronie klienta należy:
1. Uzyskać certyfikat serwera np:
>keytool -export -keystore server.store -alias hsqldb
-file server.cer
Enter keystore password: hsqldb
Certificate stored in file <server.cer>
Jeśli nie mamy dostępu do
server.store
możemy użyć dowolnego narzędzia do
połączenia się z serwerem i odebrania certyfikatu np:
openssl s_client -connect host:port
- zakodowany w Base64 certyfikat
pojawi sie na ekranie pomiędzy liniami
-----BEGIN CERTIFICATE-----
...
----END CERTIFICATE-----
.
Ten fragment zapisujemy do pliku
server.cer
.
HSQLDB – SSL
6
2. Dodać certyfikat serwera jako zaufany:
>keytool -import -trustcacerts -keystore hsqlclient.store -alias hsql
-file hsqlserver.cer
Enter keystore password:
hsqldb
Owner: CN=localhost, OU=Unknown, O=Unknown, L=Unknown, ST=Unknown,
C=Unknown
Issuer: CN=localhost, OU=Unknown, O=Unknown, L=Unknown, ST=Unknown,
C=Unknown
Serial number: 441d1f43
Valid from: Sun Mar 19 10:07:15 CET 2006 until: Tue Apr 18 11:07:15
CEST 2006
Certificate fingerprints:
MD5: 68:A2:33:BA:FA:4B:08:4A:E2:21:DD:E5:F6:7B:E3:8A
SHA1:
59:6A:4D:66:03:C8:D6:B0:D1:4C:0B:1B:30:E2:90:0F:88:66:EC:40
Trust this certificate? [no]:
yes
Certificate was added to keystore
HSQLDB – SSL
7
JVM musi zostać poinformowana, ze w pliku
hsqlclient.store
znajdują się
zaufane certyfikaty. Można to zrobić na trzy sposoby:
a) plik
hsqlclient.store
należy dodać do katalogu z zaufanymi certyfikatami (w
JDK zwykle:
JAVA_HOME/jre/lib/security/cacerts
),
b) wywołać program kliencki z opcją:
-Djavax.net.ssl.trustStore=/sciezka/do/hsqlclient.store
c) w kodzie programu klienckiego użyć instrukcji:
System.getProperties().put("javax.net.ssl.trustStore",
"/sciezka/do/hsqlclient.store");
HSQLDB – SSL
8
Przykładowy program klienta:
import java.sql.*;
public class HSQLDb {
public static void main(String[] args){
System.getProperties().put("javax.net.ssl.trustStore",
"/sciezka/do/hsqlclient.store");
System.getProperties().put("javax.net.debug","all");
try {
Class.forName("org.hsqldb.jdbcDriver").newInstance();
} catch (Exception e) { e.printStackTrace(); return; }
HSQLDB – SSL
9
try {
Connection con = DriverManager.getConnection(
"jdbc:hsqldb:
hsqls
://localhost/test", "sa", "");
Statement stmt = con.createStatement();
ResultSet rs = stmt.executeQuery("SELECT NOW()");
rs.next();
System.out.println(rs.getString(1));
con.close();
} catch (SQLException ex) { ex.printStackTrace(); }
}
}
}
MySQL i JDBC
Driver JDBC do bazy MySQL:
http://www.mysql.com/products/connector/j/
Dokumentacja:
http://dev.mysql.com/doc/refman/5.0/en/java-connector.html
.
Nawiązanie połączenia:
Connection con = DriverManager.getConnection(
"jdbc:mysql://localhost/test?user=monty&password=");
Ogólnie "connectString" ma postać:
jdbc:mysql://[host][,failoverhost...][:port]/[database]
[?propertyName1][=propertyValue1][&propertyName2][=propertyValue2]...
Domyślny adres host'a to '127.0.0.1'. Domyślny port to '3306'.
jdbc:mysql://[host:port],[host:port].../[database]
[?propertyName1][=propertyValue1][&propertyName2][=propertyValue2]...
Jeśli nie zostanie podana nazwa bazy danych, w przyszłości należy ją ustawić
metodą
setCatalog()
na rzecz obiektu
Connection
lub w zapytaniach przesyłać
pełne nazwy tabel: (np.
SELECT dbname.tablename.colname FROM
dbname.tablename
).
10
Wybrane własności połączenia
●
user
– nazwa użytkownika (domyślnie
''
),
●
password
- hasło (
''
),
●
useCompression
– czy używać kompresji podczas komunikacji z serwerem
(
false
),
●
autoReconnect
– czy odtwarzać nieaktywne połączenia (
false
),
●
useSSL
– czy używać SSL'a podczas komunikacji (
false
),
●
requireSSL
– wymagać SSL'a gdy
useSSL=true
? (
false
),
●
logger
– nazwa klasy logującej implementującej
com.mysql.jdbc.log.Log
,
używanej do logowania zdarzeń (
com.mysql.jdbc.log.StandardLogger
),
●
zeroDateTimeBehavior
– jak obsługiwać daty wypełnione zerami, opcje:
exception
,
round
oraz
convertToNull
(
exception
).
11
Kodowanie znaków
Wszystkie teksty wysyłane przez sterownik do bazy danych są automatycznie
konwertowane z Unikodu (natywne kodowanie w Javie) do kodowania używanego
przez komputer klienta.
Kodowanie znaków między klientem i serwerem jest wykrywane automatycznie
podczas nawiązania połączenia. Kodowanie używane przez sterownik jest ustawiane
po stronie serwera poprzez
character_set
(przed 4.1.0) lub
character_set_server
(od 4.1.0). Aby zmienić automatyczne kodowanie należy
ustawić własność
characterEncoding
w
connectString'u
.
Najczęstsze kodowania nazwa MySQL (nazwa Java):
usa7 (US-ASCII), latin1 (ISO8859_1), latin2 (ISO8859_2), win1250ch (Cp1250),
utf8 (UTF-8), ucs2 (UnicodeBig).
12
Używanie SSL'a
SSL szyfruje wszystkie przesyłane dane. Wydajność komunikacji spada o 35-50%.
Konfiguracja serwera:
http://dev.mysql.com/doc/refman/5.0/en/secure-connections.html.
W skrócie:
●
konfiguracja kompilacji:
SHOW VARIABLES LIKE 'have_openssl';
●
wygenerowanie certyfikatów SSL:
http://dev.mysql.com/doc/refman/5.0/en/secure-create-certs.html?ff=nopfpls
●
zmiany w pliku
my.cnf
,
●
uruchomienie serwera.
13
Używanie SSL'a
14
1. Import certyfikatu serwera MySQL:
>cp /etc/mysql/openssl/cacert.pem cacert.pem
>keytool -import -alias mysql -file cacert.pem -keystore mysql.store
Enter keystore password:
mysqljava
Owner: CN=Michal Ciesla, O=Internet Widgits Pty Ltd, ST=Some-State, C=pl
Issuer: CN=Michal Ciesla, O=Internet Widgits Pty Ltd, ST=Some-State, C=pl
Serial number: c55bf7ad0557670b
Valid from: Sun Mar 19 20:54:21 CET 2006 until: Tue Apr 18 21:54:21 CEST 2006
Certificate fingerprints:
MD5: FA:3C:B9:34:9E:11:FB:0E:D9:1C:C9:40:A5:3E:CB:E8
SHA1: 4F:9F:A8:C9:B1:3B:8F:CE:0F:7D:B0:CC:C6:E6:5A:53:EA:4B:B7:FC
Trust this certificate? [no]:
yes
Certificate was added to keystore
>
Używanie SSL'a
15
2. Ewentualne wygenerowanie certyfikatu klienta:
>keytool -genkey -keyalg rsa -alias client -keystore
client.store
3. Ustawienie właściwości JVM:
-Djavax.net.ssl.keyStore=/sciezka/do/mysql.store
-Djavax.net.ssl.keyStorePassword=*********
-Djavax.net.ssl.trustStore=/sciezka/do/client.store
-Djavax.net.ssl.trustStorePassword=*********
4. Dodanie do przy połączeniu opcji:
useSSL=true
5. Sprawdzenie – uruchomienie JVM z opcją
-Djavax.net.debug=all
Używanie SSL'a
16
import java.sql.*;
public class MySQLDb {
public static void main(String[] args){
String sString =
"jdbc:mysql://localhost/test?" +
"user=root&password=haslo&useSSL=true";
System.getProperties().put("javax.net.ssl.trustStore",
"/path/to/mysql.store");
System.getProperties().put("javax.net.debug","all");
try {
Class.forName(com.mysql.jdbc.Driver).newInstance();
} catch (Exception e) { e.printStackTrace(); return; }
Używanie SSL'a
17
try {
Connection con =
DriverManager.getConnection(sString);
Statement stmt =con.createStatement();
ResultSet rs = stmt.executeQuery("SELECT NOW()");
rs.next();
System.out.println(rs.getString(1));
con.close();
} catch (SQLException ex) { // ex.printStackTrace(); }
}
}
SSL i Java
18
Protokół SSL umożliwia bezpieczną (szyfrowaną) transmisję danych poprzez
niezabezpieczoną sieć. Dodatkowo SSL umożliwia autoryzację stron komunikacji.
W tym celu wykorzystywany jest mechanizm certyfikatów. Za transmisję z użyciem
protokołu SSL odpowiedzialnesą klasy zgrupowane w pakiecie javax.net.SSL.
Implementacja SSH jest dostępna poprzez zewnętrzne biblioteki. Jedną z nich jest
jsch (http://www.jcraft.com/jsch/).
Przykład: serwer echo
19
Przykład ze strony: http://tvilda.stilius.net/java/java_ssl.php
Piersza czynność to wygenerowanie klucza:
keytool -genkey -keystore mySrvKeystore -keyalg RSA
Kod programu serwera:
import javax.net.ssl.*;
import java.io.*;
public class EchoServer {
public static void main(String[] args) {
t
ry {
SSLServerSocketFactory factory =
(SSLServerSocketFactory)
SSLServerSocketFactory.getDefault();
SSLServerSocket ss = (SSLServerSocket)
factory.createServerSocket(9999);
Przykład: serwer echo
20
SSLSocket s = (SSLSocket) ss.accept();
InputStreamReader isr =
new InputStreamReader(s.getInputStream());
BufferedReader br = new BufferedReader(isr);
String sTmp = null;
while ((sTmp = bufferedreader.readLine()) != null) {
System.out.println(sTmp);
System.out.flush();
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
Przykład: klient echo
21
import javax.net.ssl.*;
import java.io.*;
public class EchoClient {
public static void main(String[] args) {
try {
SSLSocketFactory factory =
(SSLSocketFactory) SSLSocketFactory.getDefault();
SSLSocket s = (SSLSocket)
factory.createSocket("localhost", 9999);
InputStream inputstream =
InputStreamReader isr =
new InputStreamReader(System.in);
BufferedReader br = new BufferedReader(isr);
Przykład: klient echo
22
OutputStreamWriter osw =
new OutputStreamWriter(s.getOutputStream());
BufferedWriter bw = new BufferedWriter(osw);
String sTmp = null;
while ((sTmp = br.readLine()) != null) {
bw.write(sTmp + '\n');
bw.flush();
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
Przykład: uruchomienie programów
23
Uruchomienie serwera:
java -Djavax.net.ssl.keyStore=mySrvKeystore -Djavax
.net.ssl.keyStorePassword=123456 EchoServer
Uruchomienie klienta:
java -Djavax.net.ssl.trustStore=mySrvKeystore -Djavax
.net.ssl.trustStorePassword=123456 EchoClient
Dodatkowe parametry wywołania pozwolą zobaczyć informacje związane z
połączeniem SSL:
-Djava.protocol.handler.pkgs=
com.sun.net.ssl.internal.www.protocol
-Djavax.net.debug=ssl
SSL i autoryzacja
24
Domyślnie tylko jedna strona komunikacji (serwer) musi potwierdzać swoją
tożsamość. Jeśli konieczne jest potwierdzenie toższności klienta należy użyć
medod:
setNeedClientAuth(true)
lub
setWantClientAuth(true)
wywołanych na rzecz obiektu
SSLServerSocket
.
Jeśli chcemy aby żadna ze stron nie musiała potwierdzać swojej tożsamości musimy
zmienić domyślne algorytmy kodowania. Najłatwiej zrobić to tworząc własne
rozszerzenie klasy
SSLSocketFactory
.
Listę obsługiwanych i domyślnych algorytmów uzyskamy za pomocą metod:
getSuppotredCipherSuites()
oraz
getDefaultCipherSuites()
.
SSL i autoryzacja – listy algorytmów
25
Domyślne:
SSL_RSA_WITH_RC4_128_MD5
SSL_RSA_WITH_RC4_128_SHA
TLS_RSA_WITH_AES_128_CBC_SHA
TLS_DHE_RSA_WITH_AES_128_CBC_SHA
TLS_DHE_DSS_WITH_AES_128_CBC_SHA
SSL_RSA_WITH_3DES_EDE_CBC_SHA
SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA
SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA
SSL_RSA_WITH_DES_CBC_SHA
SSL_DHE_RSA_WITH_DES_CBC_SHA
SSL_DHE_DSS_WITH_DES_CBC_SHA
SSL_RSA_EXPORT_WITH_RC4_40_MD5
SSL_RSA_EXPORT_WITH_DES40_CBC_SHA
SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA
SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA
Wspierane: wszystkie domyślne oraz:
SSL_DHE_RSA_WITH_DES_CBC_SHA
SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA
SSL_RSA_WITH_NULL_MD5
SSL_RSA_WITH_NULL_SHA
SSL_DH_anon_WITH_RC4_128_MD5
TLS_DH_anon_WITH_AES_128_CBC_SHA
SSL_DH_anon_WITH_3DES_EDE_CBC_SHA
SSL_DH_anon_WITH_DES_CBC_SHA
SSL_DH_anon_EXPORT_WITH_RC4_40_MD5
SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA
Pdsumowanie
26
Bezpieczeństwo jest się jednym z priorytetów przy tworzeniu oprogramowania.
Częściowo może być ono zapewnione poprzez szyfrowanie przesyłanych informacji.
Java standardowo wspiera protokół TLS-SSL jak również jest przygotowana do
łatwej implementacji nowych rozwiązań w tej dziedzinie.