개발관련/(과거)메모

자바 Keytool(셀프 사인) Server/Client 소켓 통신 방법

동팡 2017. 3. 31. 16:12

참고

http://zero-gravity.tistory.com/199

https://docs.oracle.com/cd/E19159-01/820-4605/ablrb/index.html

http://m.blog.naver.com/onestone73/110106304935

http://stilius.net/java/java_ssl.php

https://www.sslshopper.com/article-how-to-create-a-self-signed-certificate-using-java-keytool.html

http://stackoverflow.com/questions/11617210/how-to-properly-import-a-selfsigned-certificate-into-java-keystore-that-is-avail

http://docs.oracle.com/javase/7/docs/technotes/tools/solaris/keytool.html

http://stackoverflow.com/questions/3508050/how-can-i-get-a-list-of-trusted-root-certificates-in-java

http://stackoverflow.com/questions/6365209/java-and-ssl-java-security-nosuchalgorithmexception 


많은 참고: http://m.blog.naver.com/wndrlf2003/220649843082


좋은 글 감사합니다



Keytool

- 자바 키툴은 키, 인증서 생성,관리 유틸리티이다. 

- 공개/개인 키 쌍, 인증서를 생성한다.

- Keytool은 키스토어(keystore)에 키, 인증서를 저장하여 자바 keystore를 통해 구현할 수 있다.


시작 + R => Run : cmd  OR 작업 폴더 쉬프트 + 우 클릭 open command windows here(현재 창에서 명령프롬포트 실행) 


프롬포트에서 Keytool 입력시 에러 뜨면 패스 설정 안했음. 설정하거나 jdk 폴더에 keytool 파일 있는 경로가서 cmd 실행해야함.(전자 추천)






그리고 쓸 기능은 

- genkey: 키, 인증서 생성

- import: 인증서 추출

- export: 인증서 키 스토어에 저장





-help를 통해 명령어 옵션 확인 바람.


키쌍 생성


서버: keytool -genkey -v -keystore server.jks -alias server_private -keyalg RSA -sigalg MD5withRSA -keysize 1024 -validity 365

클라: keytool -genkey -v -keystore client.jks -alias client_private -keyalg RSA -sigalg MD5withRSA -keysize 1024 -validity 365 

- genkey의 옵션은 help 통해 참고.

- keystore 비밀번호, info 입력, key 비밀번호 입력

- 이미지



인증서 추출

서버: keytool -export -alias server_private -keystore server.jks -rfc -file trustServer.cer

클라: keytool -export -alias client_private -keystore client.jks -rfc -file trustClient.cer

- help 참고

- keystore 비밀번호 입력

- 여기까지 서버, 클라 같이 진행 서버만 진행하고 인증서 키 스토어 삽입하면 당연히 안됨

- 이미지는 위와 비슷할것이다.


인증서 키 스토어 삽입

서버: keytool -import -alias trustClient -file trustClient.cer -keystore server.jks

클라: keytool -import -alias trustServer -file trustServer.cer -keystore client.jks


- 서버는 클라이언트의 키스토어 비밀번호 입력

- 클라는 서버의 키스토어 비밀번호 입력

- 이미지




중간 정리

- server.jks 파일에 서버의 개인키 server_private, 클라의 공개키(인증서) 저장했다.

- client.jks 파일에 클라의 개인키 client_private, 서버의 공개키(인증서) 저장했다.

- 솔직히 나도 헷갈리다.

http://m.blog.naver.com/wndrlf2003/220649843082 -> 이 친구가 위의 사항을 잘 정리 했다. 정말 고맙습니다.

(이 사이트의 그림 3을 보면 이해가 좀 더 쉬울것이다.)


일단 셀프 서명은 보안에 취약함.


이 방법 외 

1. java\jdk[version]\jre\lib\security 폴더 안에 cacerts에 저장하여 하는 방식도 있다.

 - 위의 경로에 들어가 cmd 창을 연다.

 - keytool -list -keystore cacerts 입력

 - default 비밀번호는 changeit(ㅋㅋㅋㅋ)

2. 또는 cacerts 파일을 따로 생성하여 관리하는 방식도 있다.

3. 또는 OpenSSL 툴을 사용하는 것도 있고, 다양하다.




이제 이 jks 파일을 통해 소켓 통신을 해보자.


TLSServerTestDrive.java

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.text.SimpleDateFormat;
import java.util.Date;

import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLServerSocketFactory;
import javax.net.ssl.SSLSocket;

public class TLSServerTestDrive {
	public static void main(String[] args) {
		try {
			System.setProperty("javax.net.ssl.keyStore", "D:\\tlstest\\keytool\\server.jks");
			System.setProperty("javax.net.ssl.keyStorePassword", "server123");
			System.setProperty("javax.net.debug", "ssl");
			
			SSLServerSocketFactory sslserversocketfactory =
					(SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
			SSLServerSocket sslserversocket =
					(SSLServerSocket) sslserversocketfactory.createServerSocket(5000);
			System.out.println(getTime() + "wait to request the client"); 
			SSLSocket sslsocket = (SSLSocket) sslserversocket.accept();

			InputStream inputstream = sslsocket.getInputStream();
			InputStreamReader inputstreamreader = new InputStreamReader(inputstream);
			BufferedReader bufferedreader = new BufferedReader(inputstreamreader);

			String str = null;
			while((str = bufferedreader.readLine()) != null) {
				System.out.println(str);
				System.out.flush();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

		public static String getTime() {
			SimpleDateFormat f = new SimpleDateFormat("[hh:mm:ss]");
			return f.format(new Date());
		}
	}



TLSClientTestDrive.java

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;

import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;

public class TLSClientTestDrive {
	public static void main(String[] args) {
		try {
			System.setProperty("javax.net.ssl.trustStore", "D:\\tlstest\\keytool\\client.jks");
			System.setProperty("javax.net.ssl.trustStorePassword", "client123");
			System.setProperty("javax.net.debug", "ssl");
			
			String serverIP = "본인 아이피(로컬호스트)";
			SSLSocketFactory sslsocketfactory = (SSLSocketFactory) SSLSocketFactory.getDefault();
			SSLSocket sslsocket = (SSLSocket) sslsocketfactory.createSocket(serverIP, 5000);
			
			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 str = null;
			while((str = bufferedreader.readLine()) != null) {
				bufferedwriter.write(str + '\n');
				bufferedwriter.flush();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}



포인트

위 코드 싹 다 복붙 하면 안된다. 수정 좀 해야 한다.

서버

System.setProperty("javax.net.ssl.keyStore", "D:\\tlstest\\keytool\\server.jks");
			System.setProperty("javax.net.ssl.keyStorePassword", "server123");

클라

System.setProperty("javax.net.ssl.trustStore", "D:\\tlstest\\keytool\\client.jks");
			System.setProperty("javax.net.ssl.trustStorePassword", "client123");
String serverIP = "본인 아이피(로컬호스트)";

위의 사항은 본인의 환경에 맞게 수정하자.


그러면 컴파일을 해보자. 

해당 폴더에서 cmd 2개(Server, Client)를 킨다.


Java 파일을 컴파일 한다.(이것도 패스 설정 안하면 안됨)

Class 파일이 생성 됐으면, 실행한다.



실행 화면

위의 cmd는 server : 서버는 클라 접속 대기 하다, 클라가 접속(handshake: true(UDP가 아니라서)) 

아래 cmd는 client : 클라도 접속 클라는 타이핑해서 서버에게 메시지를 보낸다.

클라가 "한글도 보내진다 " 입력




서버는 한글도 보내진다를 입력 받는다.





이렇게 끝. 근데 현장, 제품을 저렇게 구성하면 진짜 ㅈ 된다.(불편, 보안 때문) ㅎㅎㅎㅎㅎㅎㅎ 이 예제는 그저 예제, 연습용이다.


한달 전에 한거 정리해서 올리는데 도중에 에러 떠서 (ㅆㅂ)

코드에 대한 리뷰는 상단 참고 링크 누르거나, 레퍼런스 구글링을 통해 알아보면 된다.

위의 코드를 응용하면 일단 1:1, 1:N SSL 적용, 간략한 채팅프로그램 구현할 수 있다.


다음에는 OpenSSL 키쌍 인증서를 만든 후 jks 포멧으로 변경 후 소켓 통신을 해보겠다.