Использование RMI совместно с SSL.
-------------------------------------------Предполагается, что читатель знаком с данными темами:
1) Основы технологии RMI (создание простых RMI компонентов и серверов).
Для ознакомления:
http://vingrad.ru/JAVA-JAV-000103http://vingrad.ru/JAVA-JAV-002305http://vingrad.ru/JAVA-JAV-0023412) Основы SSL (что это и зачем это нужно)
3) Создание и использование хранилища ключей ( утилита keytool )
Для ознакомления:
http://vingrad.ru/JAVA-JAV-003023-------------------------------------------Не так давно мною была опубликована статья о создании защищенного соединения на основе SSL сокетов. Теперь я решился дополнить её, и рассказать, как использовать SSL сокеты совместно c RMI. Я не буду рассматривать вопросы создания RMI клиента и сервера, а лишь затрону вопрос об организации безопасного соединения.
И так, у нас имеется RMI сервер и RMI клиент, которые активно обмениваются информацией. Мы хотим обеспечить секретность данной информации, а так же, аутентификацию участников её обмена.
Что нам необходимо сделать, чтобы клиент и сервер общались через SSL сокеты:
1) Сгенерировать ключи и сертификаты для клиента и сервера.
2) Создать фабрику защищенных сокетов для RMI клиента.
3) Создать фабрику защищенных сокетов для RMI сервера.
4) Примененить полученные фабрики в коде клиента и сервера.
Сгенерировать ключи и сертификаты для клиента и сервера.
О том как создать хранилище ключей, сгенерировать ключи, подписать ключи, а импортировать и экспортировать ключи, подробно описано здесь:
http://vingrad.ru/JAVA-JAV-003023Напомню, что клиент должен обладать собственным (подписанным сертификатом) ключом, которым будет производиться шифрование данных и сертификатом ключа сервера, что бы иметь возможность аутентифицировать сервер. Напротив, сервер должен обладать собственным (подписанным сертификатом) ключом, которым будет производиться шифрование и сертификатом ключа клиента, что бы иметь возможность аутентифицировать клиента.
Хранилище ключей клиента будет содержать:
1) Ключ клиента К1.
2) Сертификат С1 для ключа клиента К1.
3) Сертификат С2 для ключа сервера К2.
Хранилище ключей сервера будет содержать:
1) Ключ сервера К2.
2) Сертификат С2 для ключа сервера К2.
3) Сертификат С1 для ключа клиента К1.
Создание фабрик защищенных сокетов для RMI клиента и сервера.Технология RMI позволяет нам самостоятельно определить фабрику, которая будет создавать сокеты для соединения клиента и сервера. Одним из способов является реализация интерфейсов RMIServerSocketFactory и RMIClientSocketFactory, для сервера и клиента соответственно.
1) Создадим фабрику SSL сокетов для клиента.
Код |
class MySslRMIClientSocketFactory implements RMIClientSocketFactory { public Socket createSocket(String host, int port) throws IOException { System.out.println("create client socket......"); return createClientSocketFactory().createSocket(host, port); } private SSLSocketFactory createClientSocketFactory() { 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(); return sslSocketFactory;
} 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(); } return null; } }
|
2) Создадим фабрику SSL сокетов для сервера.
Код |
class MySslRMIServerSocketFactory extends SslRMIServerSocketFactory { public ServerSocket createServerSocket(int port) throws IOException { System.out.println("creating ssl socket......"); return createServerSocketFactory().createServerSocket(port); } public SSLServerSocketFactory createServerSocketFactory() { 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(); return sslSocketFactory; } catch (KeyManagementException ex) { ex.printStackTrace(); } catch (NoSuchAlgorithmException ex) { ex.printStackTrace(); } catch (CertificateException ex) { ex.printStackTrace(); } catch (FileNotFoundException ex) { ex.printStackTrace(); } catch (NoSuchProviderException ex) { ex.printStackTrace(); } catch (KeyStoreException ex) { ex.printStackTrace(); } catch (UnrecoverableKeyException ex) { ex.printStackTrace(); } catch (IOException ex) { ex.printStackTrace(); } return null; } }
|
3) Применение фабрики для сервера во время создания компонента локального регистра:
Код |
public class SimpleRegistrator { /** Creates a new instance of SimpleRegistrator */ public SimpleRegistrator() { } public static void main(String[] args) { try { int port = 1098; // Создаем Registry используя нашу SSL фабрику серверных сокетов. Registry r = LocateRegistry.createRegistry(port, new SslRMIClientSocketFactory(), new MySslRMIServerSocketFactory()); r.rebind("rmi://localhost:" + port + "/MyService", new NewInterfaceImpl()); System.out.println("bind complite."); } catch (Exception e) { e.printStackTrace(); } } }
|
4) Применение фабрики для клиента:
Код |
public class SimpleClient { /** Creates a new instance of SimpleClient */ public SimpleClient() { } public static void main(String[] args) { try { int port = 1098; // Получаем Registry используя нашу SSL фабрику клиентских сокетов. Registry r = LocateRegistry.getRegistry("localhost", 1098, new MySslRMIClientSocketFactory()); NewInterface remoteComponent = (NewInterface) r.lookup("rmi://localhost:" + port + "/MyService"); } catch (RemoteException ex) { ex.printStackTrace(); } catch (NotBoundException ex) { ex.printStackTrace(); } } }
|