Использование защищенного соединения на основе SSL сокетов.
Протокол SSL предназначен для организации защищенного сетевого соединения. Протокол SSL может быть использован как промежуточный протокол между HTTP и TCP/IP, поэтому его применение HTTP клиентами будет "прозрачно". Чтобы установить связь по протоколу SSL средствами JAVA требуется создать SSL сокеты, которые в последствии используются как обыкновенные сокеты.
Для этого требуется выполнить след. шаги.
1) Создание защищенных сертификатов для клиента и сервера.
2) Создание сервера (имеет серверный SSL сокет).
3) Создание клиента (имеет клиентский SSL сокет).
Создание защищенных сертификатов для клиента и сервера.Создание защищенных сертификатов можно произвести с помощью утилиты KeyTool входящей в состав JDK. Нам потребуется создать два хранилища ключей: для клиента и для сервера. В каждых из них должны находиться по сертификату: серверному и клиентскому, что бы оде стороны, обменивающиеся информацией могли друг друга опознать.
Создадим хранилище ключей для сервера, сгенерируем ключ, подпишем его, и экспортируем клиенту.
Для клиента выполним аналогичные действия.
/// Создаем ключ для сервера
Код |
C:\Program Files\Java\jdk1.5.0_06\bin>keytool -genkey -alias serverKey -keystore ServerKeyStore Enter keystore password: serverpassword What is your first and last name? [Unknown]: myFirstName What is the name of your organizational unit? [Unknown]: myOrg What is the name of your organization? [Unknown]: myOrg What is the name of your City or Locality? [Unknown]: myCity What is the name of your State or Province? [Unknown]: myProvince What is the two-letter country code for this unit? [Unknown]: myCountryCode Is CN=myFirstName, OU=myOrg, O=myOrg, L=myCity, ST=myProvince, C=myCountryCode correct? [no]: y
Enter key password for <serverKey> (RETURN if same as keystore password): serverkeypassword
|
/// Создаем ключ для клиента
Код |
C:\Program Files\Java\jdk1.5.0_06\bin>keytool -genkey -alias clientKey -keystore ClientKeyStore Enter keystore password: clientpassword What is your first and last name? [Unknown]: ClientName What is the name of your organizational unit? [Unknown]: ClientOrgUnit What is the name of your organization? [Unknown]: ClientOrg What is the name of your City or Locality? [Unknown]: ClientCity What is the name of your State or Province? [Unknown]: ClientProvince What is the two-letter country code for this unit? [Unknown]: ClientCountryCode Is CN=ClientName, OU=ClientOrgUnit, O=ClientOrg, L=ClientCity, ST=ClientProvince, C=ClientCountryCod e correct? [no]: y
Enter key password for <clientKey> (RETURN if same as keystore password): clientkeypassword
|
/// Подписываем ключ клиента.
Код |
C:\Program Files\Java\jdk1.5.0_06\bin>keytool -selfcert -alias clientKey -keystore ClientKeyStore Enter keystore password: clientpassword Enter key password for <clientKey>clientkeypassword
|
/// Подписываем ключ сервера.
Код |
C:\Program Files\Java\jdk1.5.0_06\bin>keytool -selfcert -alias serverKey -keystore ServerKeyStore Enter keystore password: serverpassword Enter key password for <serverKey>serverkeypassword
|
/// Экспортируем сертификат сервера в хранилище клиента.
Код |
C:\Program Files\Java\jdk1.5.0_06\bin>keytool -export -file ServerCert.txt -alias serverKey -keystore ServerKeyStore Enter keystore password: serverpassword Certificate stored in file <ServerCert.txt>
C:\Program Files\Java\jdk1.5.0_06\bin>keytool -import -file ServerCert.txt -alias serverKey -keystore ClientKeyStore Enter keystore password: clientpassword Owner: CN=myFirstName, OU=myOrg, O=myOrg, L=myCity, ST=myProvince, C=myCountryCode Issuer: CN=myFirstName, OU=myOrg, O=myOrg, L=myCity, ST=myProvince, C=myCountryCode Serial number: 443ff9b9 Valid from: Fri Apr 14 23:36:25 MSD 2006 until: Thu Jul 13 23:36:25 MSD 2006 Certificate fingerprints: MD5: 0D:6D:EC:1A:FD:06:BE:DA:C3:BC:F5:DF:72:29:89:5F SHA1: 87:4F:4E:F2:DD:55:05:39:84:66:F9:98:7B:D6:C0:64:C0:3E:90:8E Trust this certificate? [no]: y Certificate was added to keystore
|
/// Экспортируем сертификат клиента в хранилище сервера.
Код |
C:\Program Files\Java\jdk1.5.0_06\bin>keytool -export -file ClientCert.txt -alias clientKey -keystore ClientKeyStore Enter keystore password: clientpassword Certificate stored in file <ClientCert.txt>
C:\Program Files\Java\jdk1.5.0_06\bin>keytool -import -file ClientCert.txt -alias clientKey -keystore ServerKeyStore Enter keystore password: serverpassword Owner: CN=ClientName, OU=ClientOrgUnit, O=ClientOrg, L=ClientCity, ST=ClientProvince, C=ClientCountryCode Issuer: CN=ClientName, OU=ClientOrgUnit, O=ClientOrg, L=ClientCity, ST=ClientProvince, C=ClientCountryCode Serial number: 443ff966 Valid from: Fri Apr 14 23:35:02 MSD 2006 until: Thu Jul 13 23:35:02 MSD 2006 Certificate fingerprints: MD5: CB:74:08:EB:EE:D0:B2:C2:AE:F6:73:E3:BE:39:9E:8A SHA1: CC:A3:86:AE:96:31:61:0C:12:CA:0A:A5:57:8F:7D:A2:EE:FD:37:72 Trust this certificate? [no]: y Certificate was added to keystore
|
Далее просмотрим содержимое хранилищ, для уверенности.
/// Для серверного хранилища.
Код |
C:\Program Files\Java\jdk1.5.0_06\bin>keytool -list -keystore ServerKeyStore Enter keystore password: serverpassword
Keystore type: jks Keystore provider: SUN
Your keystore contains 2 entries
serverkey, 14.04.2006, keyEntry, Certificate fingerprint (MD5): 0D:6D:EC:1A:FD:06:BE:DA:C3:BC:F5:DF:72:29:89:5F clientkey, 14.04.2006, trustedCertEntry, Certificate fingerprint (MD5): CB:74:08:EB:EE:D0:B2:C2:AE:F6:73:E3:BE:39:9E:8A
|
/// Для клиентского хранилища.
Код |
C:\Program Files\Java\jdk1.5.0_06\bin>keytool -list -keystore ClientKeyStore Enter keystore password: clientpassword
Keystore type: jks Keystore provider: SUN
Your keystore contains 2 entries
serverkey, 14.04.2006, trustedCertEntry, Certificate fingerprint (MD5): 0D:6D:EC:1A:FD:06:BE:DA:C3:BC:F5:DF:72:29:89:5F clientkey, 14.04.2006, keyEntry, Certificate fingerprint (MD5): CB:74:08:EB:EE:D0:B2:C2:AE:F6:73:E3:BE:39:9E:8A
|
Очевидно, что каждое из хранилищ содержит по собственному подписанному ключу и доверенному сертификату будущего собеседника.
Осталось раздать хранилища клиенту и серверу, т.е. чтобы во время выполнения программы они могли найти свои хранилища ключей.
Поместите их, к примеру, в одну папку с клиентом и сервером соответственно.
Создание СервераКод |
package ssltest;
import java.io.BufferedReader; import java.io.DataOutputStream; import java.io.FileInputStream; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.security.KeyStore; import java.security.SecureRandom; import java.security.spec.KeySpec; import javax.net.ssl.KeyManager; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLServerSocket; import javax.net.ssl.SSLSocket; import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory;
/** * * @author MoonCat */ public class SSLServer { /** Creates a new instance of Main */ public SSLServer() { } /** * @param args the command line arguments */ public static void main(String[] args) { // TODO code application logic here try { // Получить экземпляр хранилища ключей. KeyStore keyStore = KeyStore.getInstance("JKS"); FileInputStream fis = new FileInputStream("ServerKeyStore"); keyStore.load(fis, "serverpassword".toCharArray()); // Получить диспетчеры ключей базовой реализации для заданного хранилища ключей. KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509"); keyManagerFactory.init(keyStore, "serverkeypassword".toCharArray()); KeyManager [] keyManagers = keyManagerFactory.getKeyManagers(); // Получить доверенные диспетчеры базовой реализации. TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("SunX509"); trustManagerFactory.init(keyStore); TrustManager [] trustManagers = trustManagerFactory.getTrustManagers(); // Получить защищенное случайное число. SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG", "SUN"); // Создание SSL контекста SSLContext sslContext = SSLContext.getInstance("SSLv3"); sslContext.init(keyManagers, trustManagers, secureRandom); // Создание фабрики SSL сокетов. javax.net.ssl.SSLServerSocketFactory sslSocketFactory = sslContext.getServerSocketFactory(); System.out.println("Creating Server Socket : 1234"); SSLServerSocket sslServerSocket = (SSLServerSocket) sslSocketFactory.createServerSocket(1234); sslServerSocket.setNeedClientAuth(true); // Далее работаем как с обычным сокетом. System.out.println("Start Listenning Server Socket..."); SSLSocket sslClientSocket = (SSLSocket) sslServerSocket.accept(); System.out.println("Client connetion detected."); System.out.println("Sending callback message..."); DataOutputStream os = new DataOutputStream(sslClientSocket.getOutputStream()); os.write("Sending simple string to socket".getBytes()); System.out.println("Test connection complite successfully"); os.close(); } catch (Exception e) { e.printStackTrace(); } } } |
Создание КлиентаКод |
package ssltest;
import java.io.BufferedInputStream; import java.io.BufferedReader; import java.io.BufferedReader; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.security.KeyManagementException; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.SecureRandom; import java.security.UnrecoverableKeyException; import java.security.cert.CertificateException; import javax.net.ssl.KeyManager; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLServerSocket; import javax.net.ssl.SSLSocket; import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory;
/** * * @author MoonCat */ public class SSLClient { /** Creates a new instance of SSLClient */ public SSLClient() { } public static void main(String[] args) { try { // Получить экземпляр хранилища ключей. KeyStore keyStore = KeyStore.getInstance("JKS"); FileInputStream fis = new FileInputStream("ClientKeyStore"); keyStore.load(fis, "clientpassword".toCharArray()); // Получить диспетчеры ключей базовой реализации для заданного хранилища ключей. KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509"); keyManagerFactory.init(keyStore, "clientkeypassword".toCharArray()); KeyManager [] keyManagers = keyManagerFactory.getKeyManagers(); // Получить доверенные диспетчеры базовой реализации. TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("SunX509"); trustManagerFactory.init(keyStore); TrustManager [] trustManagers = trustManagerFactory.getTrustManagers(); // Получить защищенное случайное число. SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG", "SUN"); // Создание SSL контекста SSLContext sslContext = SSLContext.getInstance("SSLv3"); sslContext.init(keyManagers, trustManagers, secureRandom); // Создание фабрики SSL сокетов. javax.net.ssl.SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory(); // Создаем сокет. System.out.println("Creating Server Socket : 1234"); SSLSocket sslClientSocket = (SSLSocket) sslSocketFactory.createSocket("localhost", 1234); BufferedReader br = new BufferedReader( new InputStreamReader(sslClientSocket.getInputStream())); while (true) { String s = br.readLine(); if (s.length() > 0) { System.out.println( s ); break; } } br.close(); System.out.println("Test connection complite successfully"); } catch (KeyManagementException ex) { ex.printStackTrace(); } catch (UnrecoverableKeyException ex) { ex.printStackTrace(); } catch (NoSuchAlgorithmException ex) { ex.printStackTrace(); } catch (NoSuchProviderException ex) { ex.printStackTrace(); } catch (FileNotFoundException ex) { ex.printStackTrace(); } catch (KeyStoreException ex) { ex.printStackTrace(); } catch (CertificateException ex) { ex.printStackTrace(); } catch (IOException ex) { ex.printStackTrace(); } } } |
Код подробно прокомментирован, но суть в следующем: сначала происходит настройка SSLContext необходимыми для аутентификации сертификатами, а потом создаются клиентский и серверный SSL сокеты. После создания с ними можно работать как с простыми сокетами, передавать в них информацию, но она будет шифроваться не заметно для нас. Только если скорость передачи данных несколько упадет.
Запустите сервер а потом клиент. Клиент должен получить ответ от сервера в виде строки: Sending simple string to socket.
Желаю успеха!