관리 메뉴

kisoo

[펌]SSL 클라이언트 인증 정리 본문

01.About Programming /1.Network Lab

[펌]SSL 클라이언트 인증 정리

JamesK78 2012. 1. 31. 16:50
정확하게 말하자면 클라이언트도 서버 인증서를 확인하고, 서버도 클라이언트 인증서를 확인하는 것이니 상호 인증(mutual verification)이라 말할 수 있으나 보통 클라이언트 인증을 추가로 하기 때문에 다들 클라이언트 인증이라고 말하는 것 같다. 일단 기본적으로 필요한 사항은 자신의 공개키/비밀키와 상대방의 공개키이다. 그래야 자기 비밀키로 암호화해서 보내면 공개키로 풀어보고 확인할 수 있을테니 말이다.

OpenSSL로 생성하고 자바 JKS 타입 키스토어에 임포트해서 사용하려고 했으나 비밀키를 집어넣는 것도 인터페이스 문제로 별도의 변환 작업을 해야 해서 골 아프고 (Import private key and certificate into JKS 참조 - 아직 제대로 안 해봤음) 아예 PKCS#12로 KeyStore 객체를 로딩하려고도 해봤으나 잘 안 됐다. (원인 파악 안 됨) 속 편하게 가려면 처음부터 KeyTool을 이용해서 생성하고 익스포트해서 사용하는 것이 가장 나은 방법인 것 같다. 아무튼 나중에 BouncyCastle을 이용해서 인증서 발급 관리를 자동화 하려고 생각만 하고 있다.

위 문단을 풀어서 설명하면 아래와 같다. (Java SSL - sslecho 참조)

인증서 생성
서버: keytool -genkey -keystore server.jks -alias server
클라이언트: keytool -genkey -keystore client.jks -alias client

인증서 익스포트
서버: keytool -export -keystore server.jks -alias server -file server.cer
클라이언트: keytool -export -keystore client.jks -alias client -file client.cer

인증서 익스포트 시 위와 같이 하면 바이너리로 나와버리는데, -rfc 옵션을 끝에 추가해주면 사람이 읽을 수 있는 포맷으로 익스포트 된다. 예를 들어 클라이언트를 C와 OpenSSL로 구현할 때 서버 인증서를 -rfc 옵션 붙여서 익스포트하여 들고 와야 읽어들이는 것이 가능하다.

인증서 임포트: 각자 상대편 인증서를 trustStore에 임포트한다
서버: keytool -import -keystore clients.jks -alias client1 -file client.cer
클라이언트: keytool -import -keystore servers.jks -alias server1 -file server.cer

구현
위와 같이 해놓고 각자 아래처럼 프로퍼티를 설정한다. (동적으로 하는 방법은 아직 테스트해보지 않았음)

서버
System.setProperty("javax.net.ssl.keyStore", "server.jks");
System.setProperty("javax.net.ssl.keyStorePassword", "password");
System.setProperty("javax.net.ssl.trustStore", "clients.jks");
System.setProperty("javax.net.ssl.trustStorePassword", "password");
클라이언트
System.setProperty("javax.net.ssl.trustStore", "servers.jks");
System.setProperty("javax.net.ssl.trustStorePassword", "password");
System.setProperty("javax.net.ssl.keyStore", "client.jks");
System.setProperty("javax.net.ssl.keyStorePassword", "password");
이 때 사용 가능한 Cipher 설정 때문에 문제가 될 수 있다. SSLServerSocket.setEnabledCipherSuites() 호출할 때 단순히 SSLServerSocket.getSupportedCipherSuites()를 사용하면 제대로 인증서 설정 안 한 상황에서도 의도와 달리 다음과 같은 방식의 Cipher Suite를 사용하게 되면서 클라이언트 인증이 안 될 수 있다: (한마디로 anon 접두어 붙은 것들)
  • SSL_DH_anon_WITH_RC4_128_MD5
  • TLS_DH_anon_WITH_AES_128_CBC_SHA
  • SSL_DH_anon_WITH_3DES_EDE_CBC_SHA
  • SSL_DH_anon_EXPORT_WITH_RC4_40_MD5
  • SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA 
그러므로 확실히 하기 위해 지원되는 Cipher 목록을 다 찍어보고 원하는 것만 활성화 시켜서 사용하도록 하자. 마지막으로 반드시 SSLSocket.setNeedClientAuth(true) 를 추가해줘야 한다. SSLSocket.startHandshake()는 굳이 안 넣어도 getSession() 하면 알아서 호출된다. SSLSession.getPeerCertificates() 호출해서 X509Certificate로 캐스팅해다가 getSubjectX500Principal().getName() 하면 상대편 인증서 정보를 확인할 수 있다.

OpenSSL 이용한 클라이언트 기본 구현
링크할 때 -lssl 옵션을 붙여서 하면 되고, 간결한 기록을 위해 흐름을 알아볼 정도로 뼈대만 남겼으니 예외처리는 알아서 넣자.

#include <stdio.h>
#include <openssl/bio.h>
#include <openssl/ssl.h>
#include <openssl/err.h>

int main(int argc, char *argv[])
{
  BIO *bio = NULL;
  SSL *ssl = NULL;
  SSL_CTX *ctx = NULL;
  X509 *peer_cert = NULL;
  X509_NAME *x509_name = NULL;
  char common_name[512];

  // 라이브러리 초기화를 빠뜨리면 ctx가 생성 안 되는 현상을 보게 됨
  SSL_library_init();
  SSL_load_error_strings();
  ERR_load_BIO_strings();
  OpenSSL_add_all_algorithms();

  ctx = SSL_CTX_new(SSLv23_client_method());

  // 서버 공개키 로딩
  SSL_CTX_load_verify_locations(ctx, "server.cer", NULL);

  // 클라이언트 공개키/비밀키 로딩, 리턴값 0이면 에러이므로 확인할 것
  SSL_CTX_use_certificate_file(ctx, "client.crt", SSL_FILETYPE_PEM);
  SSL_CTX_use_PrivateKey_file(ctx, "client.key", SSL_FILETYPE_PEM);
  SSL_CTX_check_private_key(ctx);

  // 접속
  bio = BIO_new_ssl_connect(ctx);
  BIO_get_ssl(bio, &ssl);
  SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);

  BIO_set_conn_hostname(bio, "localhost:9096");
  BIO_do_connect(bio);

  // X509_V_OK인지 검증 결과 확인할 것
  SSL_get_verify_result(ssl);

  // 상대방 인증서 정보 출력 (CN)
  peer_cert = SSL_get_peer_certificate(ssl);
  x509_name = X509_get_subject_name(peer_cert);
  X509_NAME_get_text_by_NID(x509_name, NID_commonName, common_name, 512);
  printf("Common Name: %s\n", common_name);

  // 해제
  SSL_CTX_free(ctx);
  BIO_free_all(bio);

  return 0;
}

[펌] : http://www.xeraph.com/5273945
Comments