original in en Chianglin Ng
en to de J�rgen Pohl
Ich lebe in Singapore, ein modernes, vielrassiges Land in S�dostasien. Linux benutze ich seit ungef�hr zwei Jahren. Mit Red Hat 6.2 fing ich an, jetzt benutze ich Red Hat 8.0 zu Hause. Gelegentlich arbeite ich auch mit Debian Woody.
Die nachfolgenden Anweisungen gelten f�r Red Hat 8.0, das allgemeine Konzept ist auch f�r andere Distributionen anwendbar. Du mu�t jedoch PostgreSQL und die dazugeh�rigen JDBC-Treiber installieren. Bei Red Hat 8.0 kannst du daf�r den rpm-Befehl oder das Management -Tool benutzen. Du mu�t auch Sun's JDK 1.4.1 herunterladen und installieren. Dank der US-Exportbestimmungen kommt Sun's JDK mit eingeschr�nkten Verschl�sselungsm�glichkeiten. F�r unbegrenzte Verschl�sselung kannst du die Dateien mit den JCE (Java Cryptographic Extensions) herunterladen. Auf Sun's Java Website findest du weitere Einzelheiten.
Ich habe JDK 1.4.1 in /opt installiert und die JAVA_HOME -Environment-Variable zeigt auf mein JDK-Verzeichnis. Mein PATH habe ich ebenfalls aktualisiert, dort befinden sich jetzt die ausf�hrbaren Dateien von JDK. Hier die Zeilen, die ich meiner .bash_profile - Datei hinzugef�gt habe.
JAVA_HOME = /opt/j2sdk1.4.1_01
PATH = /opt/j2sdk1.4.1_01/bin:$PATH
export JAVA_HOME PATH
Die begrenzten Verschl�sselungsdateien von Sun JDK habe ich durch die uneingeschr�nkten JCE-Dateien ersetzt. Damit Java die JDBC-Treiber f�r Postgres finden kann, kopierte ich diese in mein Java-Unterverzeichnis (/opt/j2sdk1.4.1_01/jre/lib/ext). In Red Hat 8.0 sind die Postgres-JDC-Treiber in /usr/share/pgsql zu finden.
Falls das deine erste Postgres-Installation ist, musst du eine neue Datenbasis und ein neues Postgresql - Benutzerkonto einrichten. Als erstes mit su nach root und den Postgres-Service starten, dann zum Default - Postgres - Administratorkonto.
su root
password:******
[root#localhost]#/etc/init.d/postgresql start
[root#localhost]# Starting postgresql service: [ OK ]
[root#localhost]# su postgres
[bash]$
Einrichten eines neuen Postgres-Konto und einer Datenbasis.
[bash]$:createuser
Enter name of user to add: chianglin
Shall the new user be allowed to create databases? (y/n) y
Shall the new user be allowed to create more new users? (y/n)
y
CREATE USER
[bash]$createdb chianglin
CREATE DATABASE
Mein neu eingerichtes Postgres-Administrator-Konto korrespondiert mit meinem Linux-Benutzerkonto und einer Datenbasis gleichen Namens. Das psql - Werkzeug verbindet sich in der Grundeinstellung mit der Datenbasis, die mit dem gegenw�rtigen Linux-Benutzerkonto korrespondiert. Weitere Einzelheiten zur Verwaltung von Konten und Datenbanken sind im Postgres-Handbuch zu finden. Zur Eingabe deines Passworts f�r das neue Konto, starte psql und gib den Befehl ALTER USER ein. Melde dich in deinem normalen Benutzerkonto an und starte psql. Gib folgendes ein
ALTER USER chianglin WITH PASSWORD 'test1234' ;
Um die tcpip -Verbindungen zu erm�glichen, bearbeite postgresql.conf und stelle die tcpip_socket-Option auf "true". In Redhat 8.0 ist diese Datei in /var/lib/pgsql/data zu finden. Wechsle in root und mach die folgende Einstellung
tcpip_socket=true
Der letzte Schritt ist die Bearbeitung der Datei pg_hba.conf. Sie bestimmt, welche Hosts sich mit der Postgres-Datenbank verbinden k�nnen. Mit einer einzigen Eingabe f�r den Host habe ich die Loop-Adresse angepasst, einschliesslich Authentifizierung mittels Passwort. Die Anpassung der Datei muss von root aus erfolgen.
host sameuser 127.0.0.1 255.255.255.255 password
Mit dem Neustart von Postgres werden die neuen Einstellungen
aktiv.
Nach den vorangegangenen Schritten ist Postgres bereit, unsichere JDBC-Verbindungen zu akzeptieren. Der Fernzugriff auf Postgres muss �ber ein Proxy (Relay) erfolgen.
Die folgende Darstellung zeigt, wie die Proxy-�bergabe (Relay) funktionieren sollte.
Die JDPC-Anwendung verbindet sich mit dem Client-Proxy, welcher dann alle Daten �ber eine SSL-Verbindung auf den entfernten Proxy-Server �bermittelt. Der Proxy-Server schickt einfach alle Pakete zu Postgres und die Antwort erfolgt �ber die SSL-Verbindung zur�ck an den Client-Proxy, der sie zur JDBC-Anwendung �bertr�gt. Der gesamte Vorgang geschieht transparent f�r die JDBC-Anwendung.
Wie in der Abbildung angedeutet, besteht f�r den Server
die Notwendigkeit, die eintreffenden Daten in einem sicheren
Stream zu empfangen und an den lokalen Ausgabe-Stream zu
�bergeben, der mit dem aktuellen Server verbunden ist.
Umgekehrt trifft das auch zu: du ben�tigst die Daten vom
lokal eintreffenden Stream des aktuellen Servers und leitest
diese zum sicher �bertragenden Stream. Das gleiche Schema
gilt f�r den Client - es kann mittels Threads
durchgef�hrt werden. Die folgende Abbildung macht das
deutlich.
Eine SSL-Verbindung erfordert Server-Authentifizierung.
Client-Authentifizierung ist wahlweise. In unserem Fall ziehe
ich beides vor, d.h. ich muss Zertifikate und Schl�ssel
f�r den Server und den Client anlegen. Daf�r benutze
ich das Keytool im Java JDK. Auf dem Client und dem Server lege
ich zwei Schl�sselspeicher an. Der erste Speicher wird
f�r den privaten Schl�ssel des Hosts und der zweite
f�r die Zertifikate, denen der Host vertraut, angelegt.
Nachfolgend wird gezeigt, wie man einen Schl�sselspeicher,
einen privaten Schl�ssel und ein �ffentliches,
selbstbest�tigendes Zertifikat f�r den Server
anlegt.
keytool -genkey -alias serverprivate -keystore
servestore -keyalg rsa -keysize 2048
Enter keystore password: storepass1
What is your first and last name?
[Unknown]: ServerMachine
What is the name of your organizational unit?
[Unknown]: ServerOrg
What is the name of your organization?
[Unknown]: ServerOrg
What is the name of your City or Locality?
[Unknown]: Singapore
What is the name of your State or Province?
[Unknown]: Singapore
What is the two-letter country code for this unit?
[Unknown]: SG
Is CN=ServerMachine, OU=ServerOrg, O=ServerOrg, L=Singapore,
ST=Singapore, C= [no]: yes
Enter key password for <serverprivate>
(RETURN if same as keystore password): prikeypass0
</serverprivate>
Hier ist zu bemerken, dass das Passwort zweimal ben�tigt wird. Erstens f�r den Schl�sselspeicher und zweitens f�r den privaten Schl�ssel. Danach exportiere das �ffentliche Zertifikat des Servers, welches der Client zur Authentifizierung des Servers benutzen wird, in eine Datei.
keytool -export -alias serverprivate -keystore -rfc servestore -file server.cer
Hiermit exportieren wir des Servers selbstbest�tigendes, �ffentliches Zertifikat in die Datei server.cer. Auf Seiten des Client importieren wir diese Datei in einen Schl�sselspeicher, der alle �ffentlichen Zertifikate enth�lt, denen der Client vertraut.
keytool -import -alias trustservercert -file server.cer -keystore clienttruststore
Mit diesem Befehl wird das �ffentliche Zertifikat des Servers in einen Schl�sselspeicher namens clienttruststore importiert. Falls dieser noch nicht besteht, wird er erzeugt und du wirst aufgefordert, ein Passwort f�r den Speicher einzugeben.
Jetzt ist dein System bereit, eine SSL-Verbindung mittels
Server-Authentifizierung herzustellen.
Da ich auch den Client authentifizieren will, muss ich auch
einen privaten und einen �ffentlichen Schl�ssel
f�r den Client in einem neuen
Client-Schl�sselspeicher einrichten, danach das
�ffentliche Zertifikat des Client in einen neuen
Server-Schl�sselspeicher auf dem Server exportieren.
Am Ende dieses Prozesses sollten auf dem Server und dem
Client je zwei Schl�sselspeicher zu finden sein, einer
enth�lt den privaten Schl�ssel, der andere das
vertraute Zertifikat.
Um das nachfolgende Code-Beispiel ausf�hren zu k�nnen, ist es notwendig, das gleiche Passwort f�r jeden der Schl�sselspeicher auf der entsprechenden Maschine einzurichten. Das bedeutet, die zwei Schl�sselspeicher des Servers sollten das gleiche Passwort haben, das gleiche gilt f�r die beiden Schl�sselspeicher des Client.
Weitere Informationen zum keytool sind in Sun's Dokumentation zu finden.Meine Klassen werden von Sun's Java Secured Socket Extensions Gebrauch machen. Die JSSE - Referenz finden wir hier. F�r eine SSL-Verbindung ben�tigst du die Instanz eines SSL-Kontext-Objekts, das von JSSE geliefert wird. Initialisiere diesen SSL-Kontext mit den gew�nschten Einstellungen und du erh�ltst daraus eine Secured SocketFactory - Klasse. Mit der SocketFactory kann man die SSL- Sockets erzeugen.
F�r meine Anwendung wird eine Client- und eine Server-
Proxy-Klasse zur Zusammenstellung des SSL-Tunnel ben�tigt.
Da sie beide eine SSL-Verbindung benutzen werden, werden sie
eine SSL-Verbindungsklasse erben. Diese Klasse wird
daf�r zust�ndig sein, den urspr�nglichen
SSL-Kontext einzurichten, der vom Client - sowie vom
Server-Proxy benutzt werden wird. Zus�tzlich
ben�tigen wir noch eine weitere Klasse, um die
�bertragenden Threads aufzubauen. Insgesamt also vier
Klassen.
Hier ein Codeabschnitt aus der SSL-Verbindungsklasse
Codeabschnitt aus der SSL-Verbindungsklasse
/* initKeyStore method to load the keystores
which contain the private key and the trusted certificates
*/
public void initKeyStores(String key , String trust , char[]
storepass)
{
// mykey holding my own certificate and
private key, mytrust holding all the certificates that I
trust
try {
//get instances of the Sun JKS
keystore
mykey = KeyStore.getInstance("JKS" ,
"SUN");
mytrust = KeyStore.getInstance("JKS",
"SUN");
//load the keystores
mykey.load(new
FileInputStream(key) ,storepass);
mytrust.load(new FileInputStream(trust) ,storepass
);
}
catch(Exception e) {
System.err.println(e.getMessage());
System.exit(1);
}
}
/* initSSLContext method to obtain a SSLContext and
initialize it with the SSL protocol and data from the keystores
*/
public void initSSLContext(char[] storepass , char[] keypass)
{
try{
//get a SSLContext from Sun JSSE
ctx = SSLContext.getInstance("TLSv1" , "SunJSSE")
;
//initializes the keystores
initKeyStores(key , trust , storepass) ;
//Create the key and trust manager
factories for handing the cerficates
//in the key and trust stores
TrustManagerFactory tmf =
TrustManagerFactory.getInstance("SunX509" ,
"SunJSSE");
tmf.init(mytrust);
KeyManagerFactory kmf =
KeyManagerFactory.getInstance("SunX509" ,
"SunJSSE");
kmf.init(mykey , keypass);
//initialize the SSLContext with the data from
the keystores
ctx.init(kmf.getKeyManagers() ,
tmf.getTrustManagers() ,null) ;
}
catch(Exception e) {
System.err.println(e.getMessage());
System.exit(1);
}
}
Die initSSL-Kontext-Methode erzeugt einen SSL-Kontext
mittels Sun's JSSE. Dabei kannst du das gew�nschte
SSL-Protokoll angeben, ich habe TLS Version 1 (Transport Layer
Security) ausgew�hlt. Sobald wir eine Instanz des
SSL-Kontext haben, wird diese mit den Daten der
Schl�sselspeicher initialisiert.
Der folgende Codeabschnitt stammt von der
SSLRelayServer-Klasse, die auf der gleichen Maschine laufen
wird wie die Postgres-Datenbank. Damit werden alle Clientdaten
�ber die SSL-Verbindung nach Postgres �bertragen und
umgekehrt.
SSLRelayServer-Klasse
/* initSSLServerSocket method will get the
SSLContext via its super class SSLConnection. It will then
create a SSLServerSocketFactory object that will be used
to create a SSLServerSocket. */
public void initSSLServerSocket(int localport) {
try{
//get
the SSL socket factory
SSLServerSocketFactory
ssf = (getMySSLContext()).getServerSocketFactory();
//create the ssl socket
ss
= ssf.createServerSocket(localport);
((SSLServerSocket)ss).setNeedClientAuth(true);
}
catch(Exception e) {
System.err.println(e.getMessage());
System.exit(1);
}
}
// begin listening on SSLServerSocket and wait for incoming
client connections
public void startListen(int localport , int destport) {
System.out.println("SSLRelay server started
at " + (new Date()) + " " +
"listening
on port " + localport + " " + "relaying to
port " + destport );
while(true) {
try {
SSLSocket incoming
= (SSLSocket) ss.accept();
incoming.setSoTimeout(10*60*1000); // set 10 minutes time
out
System.out.println((new Date() ) + " connection from " +
incoming );
createHandlers(incoming,
destport); // create 2 new threads to handle the incoming
connection
}
catch(IOException e ) {
System.err.println(e);
}
}
}
Die RelayApp-Klasse, d.h. der Client-Proxy, �hnelt dem
SSLRelay-Server. Er erbt von der SSL-Verbindung zwei
Threads und benutzt diese, um die �bertragung
durchzuf�hren. Der Unterschied liegt darin, dass er einen
SSL-Sockel aufbaut, um mit dem entfernten
Host zu verbinden, anstatt einen SSLServer-Sockel, der auf Verbindungsaufforderungen wartet. Die letzte ben�tige Klasse ist der
Thread, der die eigentliche �bertragung durchf�hrt.
Er liest einfach den Eingabestrom und schreibt diesen in einen
Ausgabestrom.
Auf dem Client ben�tigst du die Dateien SSLConnection.java, RelayIntoOut.java und RelayApp.java. Der Server ben�tigt SSLRelayServer.java, RelayIntoOut.java und SSLConnection.java. Speichere alle in einem Verzeichnis. F�hre folgenden Befehl aus, um den Client-Proxy zu kompilieren.
javac RelayApp.java
Um den Server zu kompilieren, gib folgenden Befehl ein
javac SSLRelayServer.java
Mit Postgres auf deinem Server installiert, kannst du den SSLRelayServer mit sechs Kommandozeilen-Argumenten starten. Hier sind sie
java SSLRelayServer servestore trustclientcert storepass1 prikeypass0 2001 5432
Sobald der Server-proxy l�uft, kannst du den Client-proxy starten. Der Client-proxy ben�tigt 7 Argumente, das zus�tzliche ist der hostname oder die IP Adresse des Servers, zu dem du dich verbindest. Die Argumente sind:
java RelayApp clientstore trustservercert clistorepass1 cliprikeypass0 localhost 2001 5432
Sobald der SSL-Tunnel existiert, kannst du die JDBC-Anwendung starten und ganz normal Postgres �ffnen. Der gesamte �bertragungsvorgang wird f�r die JDBC-Anwendung vollst�ndig transparent sein.
Dieser Artikel ist bereits zu lang, ich werde daher keine weiteren Beispiele zur JDBC-Anwendung auff�hren. Das Postgres-Handbuch und die Anleitung von SUN enthalten zahlreiche Beispiele zu JDBC.
Du kannst das Testen auch auf einer einzigen Maschine durchf�hren. Daf�r bestehen zwei M�glichkeiten: entweder du �nderst den Input-Port der Postgres-Datenbank oder du wechselst die Portnummer, auf die RelayApp �bertr�gt. Ich werde den letzteren Fall anwenden, um einen einfachen Test zu demonstrieren. Zuerst schlie�e RelayApp mit dem kill -Befehl [strg] c. Auf die gleiche Weise kann der SSLRelayServer-Proxy geschlossen werden.
Starte wieder RelayApp mit dem folgenden Befehl, der einzige Unterschied ist die letzte Port-Nummer, sie ist jetzt 2002.
java RelayApp clientstore trustservercert clistorepass1 cliprikeypass0 localhost 2001 2002
Die beste Anwendung zum Testen ist psql selbst. Wir werden allen psql-Verkehr zu Postgres durch unseren Tunnel �bertragen. Gib den folgenden Befehl ein, um psql zu f�r den Test zu starten
psql -h localhost -p 2002
Dieser Befehl weist psql an, mit dem Localhost am Port 2002, dem RelayApp zuh�rt, zu verbinden. Nach der Eingabe des Postgres-Passwort kannst du wie gew�hnlich SQL-Befehle ausf�hren und damit die �bertragung der SSL-Verbindung testen.
Es ist keine gute Idee, Passw�rter als Befehlszeilenargumente zu benutzen, wenn deine Maschine auch von anderen benutzt wird. Es ist m�glich, mit dem Befehl ps -auxww den gesamten String des Prozesses, einschliesslich der Passw�rter, einzusehen. Es ist besser, die Passw�rter in verschl�sselter Form in einer anderen Datei zu speichern und deine Java-Anwendung diese von dort lesen zu lassen. Als Alternative besteht die M�glichkeit, mittels Java Swing ein Dialogfeld mit Eingabeaufforderung anzulegen.
Es ist ziemlich einfach, einen SSL-Tunnel mittels Sun-JSSE zu bauen, den
Postgres benutzen kann. Wahrscheinlich kann jede andere
Anwendung, die eine sichere Verbindung ben�tigt, diese Art
SSL-Tunnel benutzen. Es gibt so viele M�glichkeiten, deine
Verbindungen zu verschl�sseln - starte deinen
Linux-Lieblingseditor und fang an zu kodieren! Viel Spass !