SSL 및 JSSE API로 보안 네트워크 애플리케이션 구축

인터넷은 위험한 곳입니다. 보호되지 않은 정보가 유선을 통해 이동할 때 스누핑, 스푸핑 및 훔치기가 너무 쉽습니다. 지난 달에 저는 인터넷에서 대부분의 전자 상거래 활동을 보호하는 기술인 X.509 인증서와 공개 키 인프라 (PKI)에 대한 시리즈의 마지막 기사를 썼습니다. 기사의 마지막 부분에서 실제로 X.509 인증서가 어떻게 사용되는지 배우기 위해 SSL (Secure Socket Layer) 프로토콜을 살펴볼 것을 제안했습니다. SSL은 X.509 킬러 앱으로 거의 모든 브라우저와 가장 인기있는 웹 및 애플리케이션 서버가이를 지원합니다.

이번 달에는 JSSE (Java Secure Socket Extension)에 의해 구현 된 SSL을 살펴보고 SSL 및 JSSE를 사용하여 Java에서 보안 네트워크 애플리케이션을 구축하는 방법을 보여 드리겠습니다.

간단한 데모로 시작하겠습니다. JSSE는 Java 애플리케이션을위한 SSL 툴킷을 제공합니다. 필요한 클래스 및 인터페이스 외에도 JSSE는 작동중인 SSL 프로토콜 을 관찰 하는 데 사용할 수있는 편리한 명령 줄 디버깅 스위치를 제공합니다 . 난해한 애플리케이션을 디버깅하는 데 유용한 정보를 제공하는 것 외에도 툴킷을 가지고 노는 것은 SSL 및 JSSE에 발을 담그는 좋은 방법입니다.

데모를 실행하려면 먼저 다음 클래스를 컴파일해야합니다.

public class Test {public static void main (String [] arstring) {try {new java.net.URL ( "//"+ arstring [0] + "/"). getContent (); } catch (예외 예외) {exception.printStackTrace (); }}}

다음으로 SSL 디버깅을 켜고 위의 애플리케이션을 실행해야합니다. 애플리케이션은 HTTPS를 통해 SSL 프로토콜을 사용하여 명령 줄에서 지정한 보안 웹 사이트에 연결합니다. 첫 번째 옵션은 HTTPS 프로토콜 핸들러를로드합니다. 두 번째 옵션 인 디버그 옵션은 프로그램이 동작을 인쇄하도록합니다. 다음은 명령입니다 ( 보안 웹 서버의 이름으로 대체 ).

 java -Djava.protocol.handler.pkgs = com.sun.net.ssl.internal.www.protocol -Djavax.net.debug = ssl 테스트  

JSSE를 설치해야합니다. 방법을 잘 모르겠 으면 리소스를 참조하십시오.

이제 비즈니스를 시작하고 SSL 및 JSSE에 대해 이야기하겠습니다.

SSL에 대한 간략한 설명

소개의 코드는 java.net.URL클래스 를 통해 SSL을 애플리케이션에 추가하는 가장 쉬운 방법을 보여줍니다 . 이 접근 방식은 유용하지만 일반 소켓을 사용하는 보안 응용 프로그램을 만들 수있을만큼 유연하지 않습니다.

유연성을 추가하는 방법을 보여 드리기 전에 SSL의 기능에 대해 간단히 살펴 보겠습니다.

이름에서 알 수 있듯이 SSL은 애플리케이션에 보안 소켓과 같은 툴킷을 제공하는 것을 목표로합니다. 이상적으로는 일반 소켓을 사용하는 애플리케이션을 SSL을 사용하는 애플리케이션으로 쉽게 변환 할 수 있어야합니다.

SSL은 세 가지 중요한 보안 문제를 해결합니다.

  1. 대화에 포함 된 엔티티의 정당성을 보장하는 데 도움이되는 인증을 제공합니다.
  2. 프라이버시를 제공합니다. SSL은 제 3자가 두 엔티티 간의 대화를 해독 할 수 없음을 보증합니다.
  3. 그것은 무결성을 유지합니다. 체크섬과 유사한 MAC (메시지 인증 코드)를 사용하면 두 엔터티 간의 대화가 제 3 자에 의해 수정되지 않도록 보장 할 수 있습니다.

SSL은 공개 키와 비밀 키 암호화에 크게 의존합니다. 비밀 키 암호화를 사용하여 두 응용 프로그램간에 교환되는 데이터를 대량 암호화합니다. SSL은 비밀 키 알고리즘이 안전하고 빠르기 때문에 이상적인 솔루션을 제공합니다. 비밀 키 암호화보다 느린 공개 키 암호화는 인증 및 키 교환을위한 더 나은 선택입니다.

Sun의 JSSE 참조 구현에는 애플리케이션에 SSL을 추가하는 데 필요한 모든 기술이 함께 제공됩니다. 여기에는 인터넷 보안을위한 사실상의 표준 인 RSA (Rivest-Shamir-Adleman) 암호화 지원이 포함됩니다. 여기에는 현재 SSL 표준 인 SSL 3.0과 차세대 SSL 인 TLS (Transport Layer Security) 1.0의 구현이 포함됩니다. JSSE는 보안 소켓을 만들고 사용하기위한 API 제품군도 제공합니다.

JSSE API

Java 보안 아키텍처는 Factory 디자인 패턴을 많이 사용합니다 . 시작되지 않은 경우 Factory 디자인 패턴은 생성자를 직접 호출하는 대신 특수 팩토리 객체를 사용 하여 인스턴스를 생성합니다. (팩토리 클래스의 장단점에 대한 리소스를 참조하십시오.)

JSSE에서는 모든 것이 공장에서 시작됩니다. SSL 소켓을위한 공장과 SSL 서버 소켓을위한 공장이 있습니다. 일반 소켓과 서버 소켓은 이미 Java 네트워크 프로그래밍의 기본이되므로 두 가지에 대해 잘 알고 있고 그 역할과 차이점을 이해하고 있다고 가정합니다. 그렇지 않다면 Java 네트워크 프로그래밍에 대한 좋은 책을 선택하는 것이 좋습니다.

SSLSocketFactory

javax.net.ssl.SSLSocketFactory수업의 방법은 세 가지 범주로 나뉩니다. 첫 번째는 기본 SSL 소켓 팩토리를 검색하는 단일 정적 메소드로 구성됩니다 static SocketFactory getDefault()..

두 번째 범주는 클래스 javax.net.SocketFactory에있는 4 개의 키 생성자를 미러링하는 4 개의 java.net.Socket메서드와 SSL 소켓으로 기존 소켓을 래핑하는 하나의 메서드로 구성됩니다 . 각각 SSL 소켓을 반환합니다.

  1. Socket createSocket(String host, int port)
  2. Socket createSocket(String host, int port, InetAddress clientHost, int clientPort)
  3. Socket createSocket(InetAddress host, int port)
  4. Socket createSocket(InetAddress host, int port, InetAddress clientHost, int clientPort)
  5. Socket createSocket(Socket socket, String host, int port, boolean autoClose)

세 번째 범주의 두 가지 방법은 기본적으로 활성화 된 SSL 암호 그룹 목록과 지원되는 SSL 암호 그룹의 전체 목록을 반환합니다.

  1. String [] getDefaultCipherSuites()
  2. String [] getSupportedCipherSuites()

암호 제품군은 SSL 연결에 대한 특정 보안 수준을 정의하는 암호화 알고리즘의 조합입니다. 암호 그룹은 연결 암호화 여부, 콘텐츠 무결성 확인 여부 및 인증 발생 방식을 정의합니다.

SSLServerSocketFactory

javax.net.ssl.SSLServerSocketFactory클래스의 메서드 는 SSLSocketFactory. 첫째, 기본 SSL 서버 소켓 팩토리를 검색하는 단일 정적 메서드가 static ServerSocketFactory getDefault()있습니다..

SSL 서버 소켓을 반환하는 메서드는 java.net.ServerSocket클래스 에있는 생성자를 미러링합니다 .

  1. ServerSocket createServerSocket(int port)
  2. ServerSocket createServerSocket(int port, int backlog)
  3. ServerSocket createServerSocket(int port, int backlog, InetAddress address)

Finally, the SSLServerSocketFactory features the two methods that return the list of ciphers enabled by default and the list of supported ciphers, respectively:

  1. String [] getDefaultCipherSuites()
  2. String [] getSupportedCipherSuites()

So far, the API is pretty straightforward.

SSLSocket

Things get interesting in the javax.net.ssl.SSLSocket class. I assume you are already familiar with the methods provided by its parent, the Socket class, so I will concentrate on the methods that provide SSL-related functionality.

Like the two SSL factory classes, the first two methods listed below retrieve the enabled and supported SSL cipher suites, respectively. The third method sets the enabled cipher suites. An application can use the third operation to upgrade or downgrade the range of acceptable security that the application will allow:

  1. String [] getEnabledCipherSuites()
  2. String [] getSupportedCipherSuites()
  3. void setEnabledCipherSuites(String [] suites)

These two methods determine whether the socket can establish new SSL sessions, which maintain connection details -- like the shared secret key -- between connections:

  1. boolean getEnableSessionCreation()
  2. void setEnableSessionCreation(boolean flag)

The next two methods determine whether the socket will require client authentication. The methods only make sense when invoked on server mode sockets. Remember, according to the SSL specification, client authentication is optional. For example, most Web applications don't require it:

  1. boolean getNeedClientAuth()
  2. void setNeedClientAuth(boolean need)

The methods below change the socket from client mode to server mode. This affects who initiates the SSL handshake and who authenticates first:

  1. boolean getUseClientMode()
  2. void setUseClientMode(boolean mode)

Method void startHandshake() forces an SSL handshake. It's possible, but not common, to force a new handshake operation in an existing connection.

Method SSLSession getSession() retrieves the SSL session. You will seldom need to access the SSL session directly.

The two methods listed below add and remove an SSL handshake listener object. The handshake listener object is notified whenever an SSL handshake operation completes on the socket.

  1. void addHandshakeCompletedListener(HandshakeCompletedListener listener)
  2. void removeHandshakeCompletedListener(HandshakeCompletedListener listener)

SSLServerSocket

The javax.net.ssl.SSLServerSocket class is similar to the javax.net.ssl.SSLSocket class; it doesn't require much individual attention. In fact, the set of methods on javax.net.ssl.SSLServerSocket class is a subset of the methods on the javax.net.ssl.SSLSocket class.

The first two methods listed below retrieve the enabled and supported SSL cipher suites. The third method sets the enabled cipher suite:

  1. String [] getEnabledCipherSuites()
  2. String [] getSupportedCipherSuites()
  3. void setEnabledCipherSuites(String [] suites)

These two methods control whether or not the server socket can establish new SSL sessions:

  1. boolean getEnableSessionCreation()
  2. void setEnableSessionCreation(boolean flag)

The following methods determine whether the accepted sockets will require client authentication:

  1. boolean getNeedClientAuth()
  2. void setNeedClientAuth(boolean flag)

The methods below change the accepted socket from client mode to server mode:

  1. boolean getUseClientMode()
  2. void setUseClientMode(boolean flag)

A simple example

To make this toolkit tutorial clearer, I've included the source code for a simple server and a compatible client below. It's a secure variation on the typical echo application that many introductory networking texts provide.

The server, shown below, uses JSSE to create a secure server socket. It listens on the server socket for connections from secure clients. When running the server, you must specify the keystore to use. The keystore contains the server's certificate. I have created a simple keystore that contains a single certificate. (See Resources to download the certificate.)

import java.io.InputStream; import java.io.InputStreamReader; import java.io.BufferedReader; import java.io.IOException; import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLServerSocket; import javax.net.ssl.SSLServerSocketFactory; public class EchoServer { public static void main(String [] arstring) { try { SSLServerSocketFactory sslserversocketfactory = (SSLServerSocketFactory)SSLServerSocketFactory.getDefault(); SSLServerSocket sslserversocket = (SSLServerSocket)sslserversocketfactory.createServerSocket(9999); SSLSocket sslsocket = (SSLSocket)sslserversocket.accept(); InputStream inputstream = sslsocket.getInputStream(); InputStreamReader inputstreamreader = new InputStreamReader(inputstream); BufferedReader bufferedreader = new BufferedReader(inputstreamreader); String string = null; while ((string = bufferedreader.readLine()) != null) { System.out.println(string); System.out.flush(); } } catch (Exception exception) { exception.printStackTrace(); } } } 

Use the following command to start the server (foobar is both the name of the keystore file and its password):

 java -Djavax.net.ssl.keyStore=foobar -Djavax.net.ssl.keyStorePassword=foobar EchoServer 

The client, shown below, uses JSSE to securely connect to the server. When running the client, you must specify the truststore to use, which contains the list of trusted certificates. I have created a simple truststore that contains a single certificate. (See Resources to download the certificate.)

import java.io.InputStream; import java.io.OutputStream; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLSocketFactory; public class EchoClient { public static void main(String [] arstring) { try { SSLSocketFactory sslsocketfactory = (SSLSocketFactory)SSLSocketFactory.getDefault(); SSLSocket sslsocket = (SSLSocket)sslsocketfactory.createSocket("localhost", 9999); InputStream inputstream = System.in; InputStreamReader inputstreamreader = new InputStreamReader(inputstream); BufferedReader bufferedreader = new BufferedReader(inputstreamreader); OutputStream outputstream = sslsocket.getOutputStream(); OutputStreamWriter outputstreamwriter = new OutputStreamWriter(outputstream); BufferedWriter bufferedwriter = new BufferedWriter(outputstreamwriter); String string = null; while ((string = bufferedreader.readLine()) != null) { bufferedwriter.write(string + '\n'); bufferedwriter.flush(); } } catch (Exception exception) { exception.printStackTrace(); } } } 

다음 명령을 사용하여 클라이언트를 시작하십시오 ( foobar신뢰 저장소 파일의 이름과 암호 모두 임).

 java -Djavax.net.ssl.trustStore = foobar -Djavax.net.ssl.trustStorePassword = foobar EchoClient