2016-10-15 4 views
1

OpenSSL을 C++ 프로그래밍 언어로 사용하는 SSL/TLS 클라이언트 프로그램이 있습니다. SSL_get_peer_certificate 함수 호출에 의해 반환 된 서버 인증서 (X509)의 유효성을 검사하는 방법을 찾고 있습니다. 또한 SSL_CTX_load_verify_locations 기능을 사용하여로드 된 자체 CA 인증서가 있습니다. CA는 서버 인증서를 인증했습니다.SSL_get_peer_certificate에서 반환 한 서버 인증서의 유효성을 검사하는 클라이언트 프로그램은 무엇입니까?

내 서버에 SSL 세션을 만들 수 있습니다. 자, 내 자신의 CA를 사용하여 SSL 핸드 셰이크 중에받은 서버 인증서의 유효성을 검사하고 싶습니다. 나는 C 또는 C++로 그것을 할 수있는 방법을 찾을 수 없었다.

#include <iostream> 
#include <string.h> 

#include <unistd.h> 
#include <sys/socket.h> 
#include <resolv.h> 
#include <netdb.h> 
#include <netinet/in.h> 
#include <arpa/inet.h> 

#include <openssl/bio.h> 
#include <openssl/ssl.h> 
#include <openssl/err.h> 
#include <openssl/pem.h> 
#include <openssl/x509.h> 
#include <openssl/x509_vfy.h> 


#define DEFAULT_PORT_NUMBER      443 

int create_socket(char *, uint16_t port_num); 

int openSSL_client_init() 
{ 
    OpenSSL_add_all_algorithms(); 
    ERR_load_BIO_strings(); 
    ERR_load_crypto_strings(); 
    SSL_load_error_strings(); 

    if (SSL_library_init() < 0) 
     return -1; 
    return 0; 
} 

int openSSL_create_client_ctx(SSL_CTX **ctx) 
{ 
    const SSL_METHOD *method = SSLv23_client_method(); 

    if ((*ctx = SSL_CTX_new(method)) == NULL) 
     return -1; 


    //SSL_CTX_set_options(*ctx, SSL_OP_NO_SSLv2); 

    return 0; 
} 

int main(int argc, char *argv[]) 
{ 
BIO *outbio = NULL; 

X509 *cert; 
X509_NAME *certname = NULL; 

SSL_CTX *ctx; 
SSL *ssl; 
int server = 0; 
int ret, i; 

if (openSSL_client_init()) { 
    std :: cerr << "Could not initialize the OpenSSL library !" << std  :: endl; 
    return -1; 
} 

outbio = BIO_new_fp(stdout, BIO_NOCLOSE); 

if (openSSL_create_client_ctx(&ctx)) { 
    std :: cerr << "Unable to create a new SSL context structure." << std :: endl; 
    return -1; 
} 


std :: cout << "Adding Certifcate" << std :: endl; 
if (SSL_CTX_load_verify_locations(ctx, "ca-cert.pem", NULL) <= 0) { 
    std :: cerr << "Unable to Load certificate" << std :: endl; 
    return -1; 
} 


ssl = SSL_new(ctx); 
server = create_socket(argv[1], atoi(argv[2])); 

if (server < 0) { 
    std :: cerr << "Error: Can't create TCP session" << std :: endl; 
    return -1; 
} 
std :: cout << "Successfully made the TCP connection to: " << argv[1] << " port: " << atoi(argv[2]) << std :: endl; 

SSL_set_fd(ssl, server); 

if (SSL_connect(ssl) != 1) { 
    std :: cerr << "Error: Could not build a SSL session to: " << argv[1] << std :: endl; 
    return -1; 
} 

std :: cout << "Successfully enabled SSL/TLS session to: " << argv[1] << std :: endl; 
//SSL_SESSION *ss = SSL_get_session(ssl); 

cert = SSL_get_peer_certificate(ssl); 
if (cert == NULL) { 
    std :: cerr << "Error: Could not get a certificate from: " << argv[1] << std :: endl; 
    return -1; 
} 

certname = X509_NAME_new(); 
certname = X509_get_subject_name(cert); 

std :: cout << "Displaying the certificate subject data:" << std :: endl; 
X509_NAME_print_ex(outbio, certname, 0, 0); 
std :: cout << std :: endl; 


char msg[100000] = "GET/HTTP/1.1\r\nHOST: www.siliconbolt.com\r\n\r\n"; 
SSL_write(ssl, msg, strlen(msg)); 
SSL_read(ssl, msg, 100000); 
std :: cout << "Message is " << msg << std :: endl; 


SSL_free(ssl); 
close(server); 
X509_free(cert); 
SSL_CTX_free(ctx); 
std :: cout << "Finished SSL/TLS connection with server" << std :: endl; 
return 0; 
} 


int create_socket(char *ip_cstr, uint16_t port_num) 
{ 
int fd; 
struct sockaddr_in dest_addr; 

fd = socket(AF_INET, SOCK_STREAM, 0); 

dest_addr.sin_family = AF_INET; 
dest_addr.sin_port = htons(port_num); 
dest_addr.sin_addr.s_addr = inet_addr(ip_cstr); 

memset(&(dest_addr.sin_zero), '\0', 8); 

if (connect(fd, (struct sockaddr *)&dest_addr, sizeof(struct sockaddr)) == -1) 
    return -1; 

return fd; 
} 
+2

OpenSSL 위키에서 [SSL/TLS 클라이언트] (http://wiki.openssl.org/index.php/SSL/TLS_Client)를 참조하십시오. 서버 인증서의 유효성을 확인하는 데 필요한 2 ~ 3 단계를 수행합니다. OpenSSL 1.1.0 이상을 사용하는 경우 두 단계를 거쳐야합니다. OpenSSL 1.0.2 이하인 경우 3 단계를 거쳐야합니다. OpenSSL 1.1.0에 대한 예는 아직 업데이트하지 않았습니다. – jww

+0

아마도 C/C++ 언어가 없기 때문일 수 있습니다. 귀하의 코드는 C++가 아니라 C입니다. – Olaf

+0

@Oalf - 그래서 저는 분명합니다 ... 당신은이 질문에 대한 답변이 C 및/또는 C++에서 없다고 말하고 있습니다. 그 맞습니까? – jww

답변

4

올바른 인증서 확인은 복잡한 작업이며 OpenSSL은 약간만 도움이됩니다. 각 단계마다 전체 코드가 필요하다면이 질문은 너무 광범위하다고 생각할 것입니다. 따라서 검증에 필요한 필수 부분에 초점을 맞추고 주로 특정 부분의 구현 세부 사항을위한 다른 리소스를 지적하겠습니다.


서버 인증서를 검증하기위한 첫 번째 단계는 신뢰할 수있는 루트 CA 인증서에 신뢰 체인을 구축 입니다. 신뢰할 수있는 루트 (코드에서 SSL_CTX_load_verify_locations의 호출)를 설정하고 SSL_CTX_set_verify에서 확인 모드를 SSL_VERIFY_PEER으로 설정하면 TLS 핸드 셰이크 내부에서 openssl에 의해 암시 적으로 수행됩니다. 이 기본 제공 유효성 검사에는 리프 및 체인 인증서가 이미 유효하고 만료되지 않았는지 확인하는 작업도 포함됩니다.

다음 단계는 인증서의 제목이 예상 한과 일치하면 의 유효성을 검사하는 것입니다. 서버 인증서의 경우 일반적으로 대상 호스트 이름이 인증서의 일반 이름 또는 주체 대체 이름에 포함되어 있음을 의미합니다. 실제 요구 사항은 애플리케이션 레이어 프로토콜 (예 : HTTP, SMTP, LDAP ...)에 따라 다릅니다. 특히 와일드 카드가 포함 된 경우에는 모두 약간 다른 규칙이 적용됩니다. OpenSSL 1.0.2부터 X509_check_host 기능을 사용할 수 있으며 대부분의 경우 규칙을 확인하는 데 사용할 수 있습니다. 이전 버전의 OpenSSL에서는 이러한 기능을 구현할 수 있습니다. 명시 적으로 호스트 이름의 유효성을 검사해야합니다. 예를 들어 OpenSSL이이 작업을 수행하지 않으며이 단계를 생략하면 중간 공격자가 응용 프로그램을 쉽게 공격하게됩니다.

신뢰할 수있는 루트 CA에서 직접 또는 간접적으로 발급 한 인증서이며 인증서가 예상 호스트 이름과 일치 함을 알고 있습니다. 인증서가 폐기되면 으로 확인해야합니다.. 한 가지 방법은 어딘가에있는 인증서 해지 목록 (CRL)을 사용하는 것이고 다른 하나는 온라인 인증서 상태 프로토콜 (OCSP)을 사용하는 것입니다.

CRL의 경우 이미 다운로드 한 CRL을 사용하는 방법을 보여주는 Does OpenSSL automatically handle CRLs (Certificate Revocation Lists) now?을 참조하십시오. 그러나 OpenSSL은 CRL을 다운로드하는 데 도움이되지 않습니다. 대신 리프 인증서에서 CRL 배포 지점을 추출하고 CRL을 직접 다운로드 한 다음 사용할 수 있어야합니다.

OCSP의 경우 OpenSSL은 OCSP 요청을 작성하고 OCSP 응답의 유효성을 검사하는 데 필요한 API를 제공합니다. 이 OCSP 요청을 보낼 위치를 알아내는 데는 여전히 혼자 있습니다. 리프 인증서를 구문 분석하고 기관 정보 액세스 필드에서 OCSP URL을 추출하여 다시 수행해야합니다. 그리고 나머지 API는 사용하기가 쉽지 않고 적어도 OpenSSL 버전 1.1.0 이전에는 대부분 또는 완전히 문서화되지 않았습니다.OCSP 유효성 검사가이 API를 사용하여 구현되었지만 openssl ocsp 명령과 을 살펴볼 수있는 좋은 이해하기 쉬운 예제를 모르겠습니다.

경우

당신은 클라이언트 응용 프로그램을 작성하고 어쨌든 당신이 PKI에 대해 검증의 모든 복잡한 단계를 생략 할 수 있습니다 단 하나의 인증서를 수락하지만, 인증서 즉, 정확히 인증서 예상 한 경우에만 확인하려면 고정. 이 코드와 예제 코드에 대한 자세한 내용은 OWASP: Certificate and Public Key Pinning을 참조하십시오.

관련 문제