[라이브러리 개발] 자바에서 암호화 통신을 기본 내장하는 클라이언트 및 TCP 통신 라이브러리 - Whitmem
[라이브러리 개발] 자바에서 암호화 통신을 기본 내장하는 클라이언트 및 TCP 통신 라이브러리
기록 기타
2023-05-16 02:06 게시 4bc13d1afce15c040b1d

0
0
214
이 페이지는 외부 공간에 무단 복제할 수 없으며 오직 있는 그대로 게시되며 부정확한 내용을 포함할 수 있습니다. 법률이 허용하는 한 가이드 라인에 맞춰 게시 내용을 인용하거나 출처로 표기할 수 있습니다.
This page is not to be distributed to external services; it is provided as is and may contain inaccuracies.
기본 소켓 통신
기본적으로 자바에서 제공하는 기본 통신 모듈을 사용하면, 암호화하지 않는 방식으로 데이터를 송수신할 수 있습니다. 하지만 이 방법을 활용하여 보안이 요구되는 통신 소프트웨어를 개발하기 위해 AES, RSA 암호를 직접 구축하고 통신 프로토콜을 개발해야 한다는 귀찮음이 존재합니다. 따라서 이러한 작업을 대신 해 주는 간단한 라이브러리를 개발하였고, 그 기록을 여기에 작성합니다.
라이브러리 종류
기본적으로 서버를 열 수 있는 서버 라이브러리와, 서버에 접속할 수 있는 클라이언트 라이브러리로 구성됩니다. 각 라이브러리는 다음과 같은 패키지로 구성되어있습니다.
암호화 서버쪽 라이브러리
암호화 클라이언트쪽 라이브러리
기본적으로 라이브러리에 포함된 공용 클래스가 존재하는데 이러한 클래스들은 재사용 가능한 형태로 개발되어 다른 프로젝트나 라이브러리에 그대로 이식하여 활용하게끔 사용하였습니다. 예를 들어 바이트를 묶어주거나 분할해주는 기능을 가지는 클래스가 존재합니다.
의존성
기본적으로 이 두 라이브러리는 파일 암호화와 파일 저장 처리등을 진행합니다. 이러한 파일 구현을 위해 과거 직접 개발 해 두었던 암호 라이브러리와 파일 관리 라이브러리를 재 사용합니다.
이 두 라이브러리를 활용하여 필요한 데이터를 암호화하거나, 단방향 암호화 또는 해시화 할 수 있으며 파일을 다양하게 저장하고 관리할 수 있는 Configuration 관리를 구현합니다.
서버측 라이브러리
기본적으로 서버측에서 라이브러리를 활용하여 서버를 열면, 비동기 NonBlocking 방식으로 서버 온 오프 및 송수신을 시작합니다. NonBlocking 방식으로 서버가 구현되어 있기 때문에 필요한 처리자를 위한 쓰레드만 생성하며, 각 클라이언트 컨넥션마다 쓰레드를 처리하지 않습니다. 많은 클라이언트 접속이 요구될 때 유용한 처리 방식으로 서버 시피유의 부하를 줄였습니다.
사용 방법
서버를 열기 전 각종 이벤트를 수신할 인터페이스를 지정합니다. 서버 내에서 어떤 작업이나 처리가 진행되면 이 인터페이스의 콜백 메소드를 통해 신호가 전달됩니다. 서버 개발자 입장에서는 이 신호를 받아 데이터를 처리하면 됩니다.
인터페이스 구현
userDisconnected
@Override public void userDisconnected(Client client) { // TODO Auto-generated method stub System.out.println("Connection disconnected " + client.getIPPort()); }
userDisconnected 는 서버에서 클라이언트 연결이 어떠한 이유로든 끊겼을 때 호출되는 부분입니다. 로그인 오류에 의해 연결이 중지 된 경우는 userDisconnected 에 의해 처리자가 호출되지 않습니다. 매개변수에 Client 객체를 같이 넘김으로써 어떤 클라이언트가 연결 스트림을 종료했는지 알 수 있습니다.
userConnected
@Override public void userConnected(Client client) { // TODO Auto-generated method stub System.out.println("Connection connected " + client.getIPPort()); }
userConnected는 클라이언트 세션의 연결이 수락되었을 때 호출합니다. 단지 네트워크 TCP 스트림이 접속되면 콜백되는 함수입니다.
userLoginError
@Override public void userLoginError(Client client) { // TODO Auto-generated method stub System.out.println("Connection login error " + client.getIPPort()); }
userLoginError는 서버에 접속한 클라이언트와의 패킷을 주고 받을 때 잘못된 암호화 정보로 접속을 하는 경우 발생합니다. 서버에 클라이언트가 접속을 하였더라도 최초 암호화 절차가 성사되지 않으면 Login 할 수 없으며, 이 경우 LoginError 됩니다.
userLogined
@Override public void userLogined(EncryptedClient client) { // TODO Auto-generated method stub System.out.println("Connection logined " + client.getIPPort()); }
userLogined는 클라이언트가 서버에 접속하고 성공적으로 암호키를 교환하고 인증이 성사되면 호출됩니다. 이 때 부터 본격적으로 라이브러리를 활용하여 클라이언트와 안전한 암호화 통신을 수행할 수 있습니다.
packetQueueReceived
@Override public void packetQueueReceived(EncryptedClient client, ByteWrapper byteWrapper) { // TODO Auto-generated method stub System.out.println("Connection data receive " + client.getIPPort() + " : " + new ByteToRawString(byteWrapper.getBuffer())); } });
packetQueueReceived는 클라이언트가 암호화된 메시지를 보내왔을 때 해독하여 알려주는 콜백 함수입니다. 이 메소드로 넘어오는 매개변수를 통해 해독된 바이트를 바로 받아볼 수 있기 때문에 라이브러리를 사용하는 입장에서는 암호화를 처리하거나 복호를 처리 할 필요가 전혀 없습니다. 모든 것은 라이브러리를 통해 처리됩니다. 이 때 Client 정보와 같이 넘어옴으로써 어떤 클라이언트가 어떤 데이터를 보냈는지 분류하여 확인할 수 있습니다.
클라이언트측 라이브러리
서버 라이브러리를 통해 서버를 구축한 경우 클라이언트 라이브러리를 통해 암호화된 서버에 접속하고 통신할 수 있도록 구현하였습니다.
클라이언트측 라이브러리
클라이언트측에서 역시 각 이벤트에 해당되는 요청을 처리할 수 있습니다. 클라이언트에서는 다수개의 세션을 쓰레드별로 처리할 필요가 없으므로, 기본적인 동기 쓰레드 방식을 통해 구현됩니다. 하지만 이벤트 콜백으로 처리하기 때문에 사용자 입장에서는 비동기 형식처럼 사용할 수 있습니다.
인터페이스 구현
connected
@Override public void connected() { // TODO Auto-generated method stub System.out.println("서버 접속 완료."); }
connected는 서버에 접속한 경우 콜백합니다. 다만 어떤 서버든 tcp 연결에 성공하면 콜백되는 함수입니다.
disconnected
@Override public void disconnected() { // TODO Auto-generated method stub System.out.println("연결 끊김."); }
disconnected는 서버와의 연결이 종료된 경우 호출되는 부분입니다. 실제 데이터를 통신하다 서버와의 연결이 끊기는 경우 호출됩니다.
logined
@Override public void logined() { // TODO Auto-generated method stub System.out.println("서버에 로그인 했습니다."); }
logined는 서버에 로그인된 경우 콜백하는 함수입니다. 로그인을 했다는 것은 암호화 정보를 성공적으로 주고 받았다는 의미이며 인증을 성사했다는 것을 의미합니다.
loginError
@Override public void loginError() { // TODO Auto-generated method stub System.out.println("로그인 오류."); }
loginError은 암호 정보를 송수신하는 로그인 작업 중 오류가 발생한 경우 호출됩니다.
packetReceived
@Override public void packetReceived(ByteWrapper bytes) { // TODO Auto-generated method stub System.out.println("데이터 수신 : " + bytes.getBuffer().length+ ": " + new ByteToRawString(bytes.getBuffer()).toString()); }
packetReceived는 암호화 데이터의 인증 성사 작업인 로그인이 완료된 후 암호화된 데이터가 들어오는 경우 복호화된 값을 반환합니다.
특이점
client.getClientMemory().setPublicKey("GfMA0GC...");
기본적으로 모든 암호 통신의 기본은 암호키라고 생각합니다. 원격 서버와 클라이언트가 안전하게 통신을 하기 위해서는 서로간의 동일한 암호키를 가지고 있어야 하는데 동일한 암호키를 가지기 위해서는 서로간 암호키를 교환하는 작업이 필요합니다. 하지만 기본적으로 암호키를 교환하기 위해 평문 통신을 통해 전송하게 되는 경우 중간자 공격 또는 패킷 스니핑에 의해 암호키가 노출 또는 유출될 우려가 존재합니다. 그렇기 때문에 두 접속 세션이 일면식이 있는 경우 미리 만나서... 비밀키를 교환하는 방법으로 암호화를 성사할 수 있습니다만... 네트워크에서는 기본적으로 서버와 접속자의 일면식이 전혀 없는 익명성이 강조되기 때문에 평문 통신 환경에서 안전하게 암호키를 전달할 수 있어야 합니다. 그러기 위해서는 RSA 단방향 암호화 방식을 활용하여 안전하게 키를 전송할 수 있지만 이 중간자 공격 기법을 활용하여 위조된 단방향 암호키로 실제 키를 받아내서 데이터를 중계할 수 있다는 문제가 존재합니다. 이를 방지하기 위해서는 신뢰할 수 있는 루트인증서를 활용하여 인증된 공개키인지 확인하는 작업이 필요하지만, 이 도구는 기본적으로 내부 또는 개인용임을 고려하여 공개키를 직접 검증하는식으로 구현하였습니다. 서버에서 제공한 공개키와 사전에 배포된 공개가 일치하지 않는 경우 위조된 서버로 판단하며, 공개키가 일치하는 경우만 안전하게 소켓 통신을 수행합니다. 이로써 접속자를 모르는 상황이라고 하더라도 인증된 서버에 접속함으로써 안전하게 AES 암호키를 교환하고 그 후 AES 암호키로 암호화된 데이터를 송수신하며 안전하게 데이터를 보호합니다. 특히 데이터는 IV 벡터를 랜덤 난수로 활용하여 같은 데이터에 대해서도 유추할 수 없도록 차단하는식으로 구현하였습니다. (기본 방식) 오우 쓰고나니까 길다
사용예
서버단 구축
클라이언트 구축
클라이언트에서 1초마다 "Hi 날짜"이라는 데이터를 보내면 서버측에서는 수신 후 "Hello~ 날짜"로 응답하도록 구축하였습니다.
서버 열기 (서버측)
서버를 열면 라이브러리에 의해 RSA 키가 자동으로 생성 관리되고 쓰레드를 시작합니다.
새 클라이언트 접속 (서버측)
새 클라이언트가 접속하면 암호화된 정보를 받고 개인키로 해독합니다. AES 키를 받은 뒤 로그인을 성사시킵니다.
클라이언트 접속 (클라이언트측)
클라이언트 측에서는 서버에 접속하고 서버에서 보내온 인증 정보로 공개키로 암호화 한 뒤 보냅니다. 서버에서는 이 정보를 복호화 해 정보가 일치하는지 확인합니다.
인증키를 추가로 넣은 이유는 로그인시 같은 패킷을 한 번더 활용하는 것을 방지하기 위함입니다. 로그인 과정에서 스니핑에서 얻은 자료를 통해 똑같은 네트워크 패킷을 서버에 복사하여 보내는 경우를 방지할 수 있습니다. 단, 전송 과정에서 발생하는 복사 부분에 대해서는 조사하고 방지 대책을 세워야 할 것으로 보입니다.
댓글 0개
댓글을 작성하는 경우 댓글 처리 방침에 동의하는 것으로 간주됩니다. 댓글을 작성하면 일회용 인증키가 발급되며, 해당 키를 분실하는 경우 댓글을 제거할 수 없습니다. 댓글을 작성하면 사용자 IP가 영구적으로 기록 및 부분 공개됩니다.
확인
Whitmemit 개인 일지 블로그는 개인이 운영하는 정보 공유 공간으로 사용자의 민감한 개인 정보를 직접 요구하거나 요청하지 않습니다. 기본적인 사이트 방문시 처리되는 처리 정보에 대해서는 '사이트 처리 방침'을 참고하십시오. 추가적인 기능의 제공을 위하여 쿠키 정보를 사용하고 있습니다. Whitmemit 에서 처리하는 정보는 식별 용도로 사용되며 기타 글꼴 및 폰트 라이브러리에서 쿠키 정보를 사용할 수 있습니다.
이 자료는 모두 필수 자료로 간주되며, 사이트 이용을 하거나, 탐색하는 경우 동의로 간주합니다.