background image

 

 

Java, bazy danych i SSL

1

1. MySQL:

 własności połączenia,

 transmisja z wykorzystaniem SSL.

2. Własne programy wykorzystujące SSL.

3. RMI i SSL

- implementacja własnych wersji klas 

ServerSocket

 i 

Socket

.

- wykorzystanie własnych gniazd w ramach RMI

- implementacja własnej klasy 

RMIClientSocketFactory

.

- implementacja własnej klasy 

RMIServerSocketFactory

.

- szyfrowanie SSL w RMI

background image

 

 

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

).

2

background image

 

 

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

).

3

background image

 

 

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).

4

background image

 

 

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.

5

background image

 

 

Używanie SSL'a

6

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

>

background image

 

 

Używanie SSL'a

7

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

background image

 

 

Używanie SSL'a

8

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.setProperty(

"javax.net.ssl.trustStore"

,

"/path/to/mysql.store"

);

System.setProperty(

"javax.net.debug"

,

"all"

);

try

 {

Class.forName(

"com.mysql.jdbc.Driver"

).newInstance();

catch

 (Exception ex) {

ex.printStackTrace();

return

}

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(); 

}

}

background image

 

 

SSL i Java

9

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 odpowiedzialne są 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/).

background image

 

 

Przykład: serwer echo

10

import

 javax.net.ssl.*;

import

 java.io.*;

public

 

class

 EchoServer {

public

 

static

 

void

 main(String[] args) {

try

 {

SSLServerSocketFactory factory = (SSLServerSocketFactory)

SSLServerSocketFactory.getDefault();

SSLServerSocket ss = (SSLServerSocket) factory

.createServerSocket(9999);

SSLSocket s = (SSLSocket) ss.accept();

InputStreamReader isr = 

new

 

InputStreamReader(s.getInputStream());

BufferedReader br = 

new

 BufferedReader(isr);

String sTmp = 

null

;

while

 ((sTmp = br.readLine()) != 

null

) {

System.

out

.println(sTmp);

System.

out

.flush();

}

catch

 (Exception ex) {

ex.printStackTrace();

}

}

}

background image

 

 

Przykład: klient echo

11

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);

InputStreamReader isr = 

new

 InputStreamReader(System.

in

);

BufferedReader br = 

new

 BufferedReader(isr);

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();

}

}

}

background image

 

 

Przykład: uruchomienie programów

12

Pierwsza czynność to wygenerowanie klucza:

keytool -genkey -keystore mySrvKeystore -keyalg RSA

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

Przykład ze strony: http://tvilda.stilius.net/java/java_ssl.php

background image

 

 

SSL i autoryzacja

13

Domyślnie tylko jedna strona komunikacji (serwer) musi potwierdzać swoją 

tożsamość.  Jeśli konieczne jest potwierdzenie tożsamości klienta należy użyć 

metod: 

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()

.

background image

 

 

SSL i autoryzacja – listy algorytmów

14

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

background image

 

 

Szyfrowanie w RMI

15

Technologia RMI została zaprojektowana tak, aby można było jej używać z 

dowolnymi mechanizmami zapewniającymi transport danych przez sieć, 

działającymi ponad protokołem TCP. W praktyce odbywa się to poprzez 

implementację własnego obiektu (tzw. socket factory) dostarczającego gniazda 

wykorzystywane do komunikacji RMI. 

Implementacja własnego socket factory składa się z trzech kroków:

1. Implementacja własnych wersji klas 

ServerSocket

 i 

Socket

.

2. Implementacja własnej klasy 

ClientSocketFactory

.

3. Implementacja własnej klasy  

ServerSocketFactory

.

background image

 

 

1. Implementacja własnych gniazd

16

Przygotujemy gniazda, które będą umożliwiały przesyłanie przez siec danych 

kodowanych za pomocą operacji 

XOR

 i ustalonego, ośmiobitowego wzorca.

XorServerSocket

Socket

XorSocket

ServerSocket

background image

 

 

Implementacja XorSocket

17

import

 java.io.*;

import

 java.net.*;

class

 XorSocket 

extends

 Socket {

private

 

final

 

byte

 

pattern

// wzorzec kodowania

private

 InputStream 

in

 = 

null

;

private

 OutputStream 

out

 = 

null

;

public

 XorSocket(

byte

 pattern) 

throws

 IOException {

super

();

this

.

pattern

 = pattern;

    }

public

 XorSocket(String host, 

int

 port, 

byte

 pattern) 

throws

 IOException {

        

super

(host, port);

        

this

.

pattern

 = pattern;

    }

background image

 

 

Implementacja XorSocket

18

public

 

synchronized

 InputStream getInputStream() 

throws

 IOException {

if

 (

in

 == 

null

) {

in

 = 

new

 XorInputStream(

super

.getInputStream(), 

pattern

);

}

return

 

in

;

}

public

 

synchronized

 OutputStream getOutputStream() 

throws

 IOException {

if

 (

out

 == 

null

) {

out

 = 

new

 XorOutputStream(

super

.getOutputStream(),

 

pattern

);

}

return

 

out

;

}

}

background image

 

 

Implementacja XorServerSocket

19

import

 java.io.*;

import

 java.net.*;

class

 XorServerSocket 

extends

 ServerSocket {

private

 

final

 

byte

 

pattern

;

public

 XorServerSocket(

int

 port, 

byte

 pattern) 

throws

 IOException {

super

(port);

this

.

pattern

 = pattern;

}

public

 Socket accept() 

throws

 IOException {

Socket s = 

new

 XorSocket(

pattern

);

this

.implAccept(s);

return

 s;

}

}

background image

 

 

Implementacja strumieni

20

import

 java.io.*;

class

 XorOutputStream 

extends

 FilterOutputStream {

private

 

final

 

byte

 

pattern

;

public

 XorOutputStream(OutputStream out, 

byte

 pattern) {

super

(out); 

this

.

pattern

 = pattern;

}

public

 

void

 write(

int

 b) 

throws

 IOException {

out

.write((b ^ 

pattern

) & 0xFF);

}

}

background image

 

 

Implementacja strumieni

21

import

 java.io.*;

class

 XorInputStream 

extends

 FilterInputStream {

private

 

final

 

byte

 

pattern

;

public

 XorInputStream(InputStream in, 

byte

 pattern) {

super

(in);

this

.

pattern

 = pattern;

    }

public

 

int

 read() 

throws

 IOException {

int

 b = 

in

.read();

if

 (b != -1)  b = (b ^ 

pattern

) & 0xFF;

return

 b;

}

public

 

int

 read(

byte

 b[], 

int

 off, 

int

 len) 

throws

 IOException {

int

 n = 

in

.read(b, off, len);

if

 (n <= 0) 

return

 n;

for

(

int

 i = 0; i < n; i++)

b[off + i] = (

byte

)((b[off + i] ^ 

pattern

) & 0xFF);

return

 n;

}

}

background image

 

 

Implementacja socket factories

22

W technologi RMI serwer (zdalny obiekt) decyduje o rodzaju transmisji. Dzięki 

temu kod programu klienckiego jest niezależny od zmian (np. szyfrowanie) w 

protokole komunikacji. Z drugiej strony wszelkie dane potrzebne do zainicjowania 

połączenia są do klienta przesyłane przez sieć – czyli muszą być serializowalne.

Aby utworzyć gniazdo służące do komunikacji RMI korzysta z interfejsów 

SocketFactory

 oraz 

ServerSocketFactory

, które udostępniają odpowiednio 

metody 

createSocket(String host, int port)

 i 

createServerSocket(int port)

.

background image

 

 

Implementacja 

RMIClientSocketFactory

23

import

 java.io.*;

import

 java.net.*;

import

 java.rmi.server.*;

public

 

class

 XorClientSocketFactory 

implements 

RMIClientSocketFactory, 

Serializable

 {

private

 

byte

 

pattern

;

public

 XorClientSocketFactory(

byte

 pattern) { 

this

.

pattern

 = pattern; 

}

public

 Socket createSocket(String host, 

int

 port) 

throws

 IOException {

return

 

new

 XorSocket(host, port, 

pattern

);

}

public

 

int

 hashCode() { 

return

 (

int

pattern

; }

public

 

boolean

 equals(Object obj) {

return

 (getClass() == obj.getClass() &&

 

pattern

 == ((XorClientSocketFactory) obj).

pattern

);

}

}

background image

 

 

Implementacja 

RMIServerSocketFactory

24

import

 java.io.*;

import

 java.net.*;

import

 java.rmi.server.*;

public

 

class

 XorServerSocketFactory 

implements

 RMIServerSocketFactory {

private

 

byte

 

pattern

;

public

 XorServerSocketFactory(

byte

 pattern) {

this

.

pattern

 = pattern; 

}

public

 ServerSocket createServerSocket(

int

 port) 

throws

 IOException {

return

 

new

 XorServerSocket(port, 

pattern

);

}

public

 

int

 hashCode() { 

return

 (

int

pattern

; }

public

 

boolean

 equals(Object obj) {

return

 (getClass() == obj.getClass() &&

 

pattern

 == ((XorServerSocketFactory) obj).

pattern

);

}

}

background image

 

 

Program serwera

25

import

 java.io.*;

import

 java.rmi.*;

import

 java.rmi.server.*;

import

 java.rmi.registry.*;

public

 

class

 HelloImpl 

implements

 Hello {

public

 HelloImpl() {

}

public

 String getHello() {

return

 

"Hello World!"

;

}

public

 

static

 

void

 main(String args[]) {

byte

 pattern = (

byte

) 0xC5; 

// 10100101

try

 {

HelloImpl obj = 

new

 HelloImpl();

RMIClientSocketFactory csf = 

new

 XorClientSocketFactory(pattern);

RMIServerSocketFactory ssf = 

new

 XorServerSocketFactory(pattern);

background image

 

 

Program serwera

26

Hello stub = (Hello) UnicastRemoteObject.exportObject(obj,

 

0, csf, ssf);

try

 {

reg = LocateRegistry.getRegistry();

catch

 (RemoteException ex1) {

try

 {

reg = LocateRegistry.createRegistry(1099);

catch

 (RemoteException ex2) {

return

;

}

}

reg.rebind(

"HelloService"

, stub);

catch

 (Exception e) {

e.printStackTrace();

}

}

}

background image

 

 

Program klienta

27

import

 java.rmi.Naming;

import

 java.rmi.registry.LocateRegistry;

import

 java.rmi.registry.Registry;

public

 

class

 HelloClient {

public

 

static

 

void

 main(String args[]) {

try

 {

Hello obj = (Hello)

 Naming.lookup(

"rmi://localhost/HelloService"

);

System.

out

.println(obj.getHello());

catch

 (Exception e) { 

e.printStackTrace();

}

    }

}

background image

 

 

Kompilacja i uruchomienie

28

1. Kompilacja:

javac *.java

2. Wygenerowanie klas łącznikowych:

rmic HelloImpl

3. Uruchomienie programu serwera:

java HelloImpl

4. Uruchomienie programu klienta:

java HelloClient

background image

 

 

RMI i SSL

29

import

 java.io.IOException;

import

 java.io.Serializable;

import

 java.net.Socket;

import

 java.rmi.server.RMIClientSocketFactory;

import

 javax.net.ssl.SSLSocketFactory;

public

 

class

 RMISSLClientSocketFactory 

implements

 RMIClientSocketFactory, Serializable {

public

 Socket createSocket(String arg0, 

int

 arg1) 

throws

 IOException {

SSLSocketFactory factory = (SSLSocketFactory)

SSLSocketFactory.getDefault();

return

 factory.createSocket(arg0, arg1);

}

}

background image

 

 

RMI i SSL

30

import

 java.io.IOException;

import

 java.io.Serializable;

import

 java.net.ServerSocket;

import

 java.rmi.server.RMIServerSocketFactory;

import

 javax.net.ssl.SSLServerSocketFactory;

public

 

class

 RMISSLServerSocketFactory 

implements

 RMIServerSocketFactory, Serializable{

public

 ServerSocket createServerSocket(

int

 arg0) 

throws

 IOException {

SSLServerSocketFactory factory = (SSLServerSocketFactory)

SSLServerSocketFactory.getDefault();

return

 factory.createServerSocket(arg0);

}

}

background image

 

 

RMI i SSL

31

W programie serwera (

HelloImpl.java

) należy zmienić linie odpowiadające za 

tworzenie obiektów 

SocketFactory

 na:

RMIClientSocketFactory csf = new RMISSLClientSocketFactory();

RMIServerSocketFactory ssf = new RMISSLServerSocketFactory();

UWAGA: przy uruchomieniu programu korzystającego z SSL należy uwzględnić 

magazyn kluczy używanych do autoryzacji i szyfrowania.

background image

 

 

Podsumowanie

32

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.