SSL (c, java)

web 2007. 9. 28. 03:50

SSL 통신과 관련되어 좋은 정보가 있어서 소개한다.

c언어를 이용하여 SSL 통신을 java로 컨버팅한것이다. HTTPClient를 이용하면 굉장히 쉽게 사용할 수 있다.

 

c언어 공부용으로 발췌함~^^

 

출처

http://kldp.org/node/28194

 

 

서버(serv.c)

/* serv.cpp  -  Minimal ssleay server for Unix 
   30.9.1996, Sampo Kellomaki <sampo@iki.fi> */ 


/* mangled to work with SSLeay-0.9.0b and OpenSSL 0.9.2b 
   Simplified to be even more minimal 
   12/98 - 4/99 Wade Scholine <wades@mail.cybg.com> */ 

#include <stdio.h> 
#include <unistd.h> 
#include <stdlib.h> 
#include <memory.h> 
#include <errno.h> 
#include <sys/types.h> 
#include <sys/socket.h> 
#include <netinet/in.h> 
#include <arpa/inet.h> 
#include <netdb.h> 

#include <openssl/rsa.h>       /* SSLeay stuff */ 
#include <openssl/crypto.h> 
#include <openssl/x509.h> 
#include <openssl/pem.h> 
#include <openssl/ssl.h> 
#include <openssl/err.h> 


/* define HOME to be dir for key and cert files... */ 
#define HOME "./" 

/* Make these what you want for cert & key files */ 
#define CERTF  HOME "server-req.pem" 
#define KEYF  HOME  "server-key.pem" 


#define CHK_NULL(x) if ((x)==NULL) exit (1) 
#define CHK_ERR(err,s) if ((err)==-1) { perror(s); exit(1); } 
#define CHK_SSL(err) if ((err)==-1) { ERR_print_errors_fp(stderr); exit(2); } 

void main () 
{ 
    int err; 
    int listen_sd; 
    int sd; 
    struct sockaddr_in sa_serv; 
    struct sockaddr_in sa_cli; 
    size_t client_len; 

    SSL_CTX* ctx; 
    SSL*     ssl; 
    X509*    client_cert; 

    char*    str; 
    char     buf [4096]; 

    SSL_METHOD *meth; 

    /* SSL preliminaries. We keep the certificate and key with the context. */ 
    SSL_load_error_strings(); 
    SSLeay_add_ssl_algorithms(); 
    meth = TLSv1_server_method(); 

    // create a new SSL_CTX object as framework for TLS/SSL enabled functions 
    ctx = SSL_CTX_new (meth); 
    if (!ctx) { 
        ERR_print_errors_fp(stderr); 
        exit(2); 
    } 

    if (SSL_CTX_use_certificate_file(ctx, CERTF, SSL_FILETYPE_PEM) <= 0) { 
        ERR_print_errors_fp(stderr); 
        exit(3); 
    } 
    if (SSL_CTX_use_PrivateKey_file(ctx, KEYF, SSL_FILETYPE_PEM) <= 0) { 
        ERR_print_errors_fp(stderr); 
        exit(4); 
    } 
    if (!SSL_CTX_check_private_key(ctx)) { 
        fprintf(stderr,"Private key does not match the certificate public key\n"); 
        exit(5); 
    } 




    /* Prepare TCP socket for receiving connections */ 

    // Create Internet Socket and Fill sockaddr_in 
    listen_sd = socket (AF_INET, SOCK_STREAM, 0);    
    CHK_ERR(listen_sd, "socket"); 

    memset (&sa_serv, '\0', sizeof(sa_serv)); 
    sa_serv.sin_family      = AF_INET; 
    sa_serv.sin_addr.s_addr = INADDR_ANY; 
    sa_serv.sin_port        = htons (1111);          /* Server Port number */ 

    // Bind 
    err = bind(listen_sd, (struct sockaddr*) &sa_serv, sizeof (sa_serv));                    
    CHK_ERR(err, "bind"); 

    // Listening 
    err = listen (listen_sd, 5);  
    CHK_ERR(err, "listen"); 

    // Accept 
    client_len = sizeof(sa_cli); 
    sd = accept (listen_sd, (struct sockaddr*) &sa_cli, &client_len); 
    CHK_ERR(sd, "accept"); 

    // Close Listening socket 
    close (listen_sd); 

    // Connected-client info. 
    printf ("Connection from %lx, port %d\n", 
    sa_cli.sin_addr.s_addr, sa_cli.sin_port); 



    // TCP connection is ready. Do server side SSL. 
    // create a new SSL structure for a connection 
    ssl = SSL_new (ctx);                     
    CHK_NULL(ssl); 

    // connect the SSL object with a file descriptor 
    SSL_set_fd (ssl, sd); 
    err = SSL_accept (ssl);    
    CHK_SSL(err); 

    // Get the cipher - opt  
    printf ("SSL connection using %s\n", SSL_get_cipher (ssl)); 

    // Get client's certificate (note: beware of dynamic allocation) - opt 
    client_cert = SSL_get_peer_certificate (ssl); 
    if (client_cert != NULL)  
    { 
        printf ("Client certificate:\n"); 

        str = X509_NAME_oneline (X509_get_subject_name (client_cert), 0, 0); 
        CHK_NULL(str); 
        printf ("\t subject: %s\n", str); 
        free (str); 

        str = X509_NAME_oneline (X509_get_issuer_name  (client_cert), 0, 0); 
        CHK_NULL(str); 
        printf ("\t issuer: %s\n", str); 
        free (str); 

        /* We could do all sorts of certificate verification stuff here before 
           deallocating the certificate. */ 

        X509_free (client_cert); 
    }  
    else 
    { 
        printf ("Client does not have certificate.\n"); 
    } 

    // DATA EXCHANGE - Receive message and send reply. 
    err = SSL_read (ssl, buf, sizeof(buf) - 1);        
    CHK_SSL(err); 

    buf[err] = '\0'; 
    printf ("Got %d chars:'%s'\n", err, buf); 

    err = SSL_write (ssl, "I hear you.", strlen("I hear you.")); 
    CHK_SSL(err); 

    /* Clean up. */ 
    close (sd); 
    SSL_free (ssl); 
    SSL_CTX_free (ctx); 
} 

클라이언트(cli.c)

/* cli.cpp  -  Minimal ssleay client for Unix 
   30.9.1996, Sampo Kellomaki <sampo@iki.fi> */ 

/* mangled to work with SSLeay-0.9.0b and OpenSSL 0.9.2b 
   Simplified to be even more minimal 
   12/98 - 4/99 Wade Scholine <wades@mail.cybg.com> */ 

#include <stdio.h> 
#include <memory.h> 
#include <errno.h> 
#include <sys/types.h> 
#include <sys/socket.h> 
#include <netinet/in.h> 
#include <arpa/inet.h> 
#include <netdb.h> 

#include <unistd.h> 

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


#define CHK_NULL(x) if ((x)==NULL) exit (1) 
#define CHK_ERR(err,s) if ((err)==-1) { perror(s); exit(1); } 
#define CHK_SSL(err) if ((err)==-1) { ERR_print_errors_fp(stderr); exit(2); } 

void print_x509(SSL *ssl) 
{ 
    char *ascii_cert; 
    X509 *cert = SSL_get_peer_certificate(ssl); 
    BIO *b; 
    BUF_MEM *bptr; 

    b = BIO_new(BIO_s_mem()); 

    if(X509_print(b, cert) > 0) 
    { 
        BIO_get_mem_ptr(b, &bptr); 
        ascii_cert = (char *)malloc(1 + bptr->length); 
        memcpy(ascii_cert, bptr->data, bptr->length); 
    } 
    else 
    { 
        ascii_cert = (char *)malloc(1024); 
        sprintf(ascii_cert, "This certificate has never been seen before and can't be shown\n"); 
    } 
    BIO_free(b); 

    /* X.509 인증서 출력 */ 
    printf("X.509:\n%s\n", ascii_cert); 
} 

void main (int argc, char **argv) 
{ 
    int err; 
    int sd; 
    struct sockaddr_in sa; 

    SSL_CTX* ctx; 
    SSL*     ssl; 
    X509*    server_cert; 

    char*    str; 
    char     buf [4096]; 
    char hello[80]; 

    SSL_METHOD *meth; 

    printf ("Message to send: "); 
    fgets (hello, 80, stdin); 

    SSLeay_add_ssl_algorithms(); 
    //meth = SSLv2_client_method(); 
    meth = TLSv1_client_method(); 
    SSL_load_error_strings(); 
    ctx = SSL_CTX_new (meth); 
    CHK_NULL(ctx); 
    CHK_SSL(err); 


    /////////////////////////////////////////////////////////////////////////////////////// 
    // Create a socket and connect to server using normal socket calls. 
    sd = socket (AF_INET, SOCK_STREAM, 0);   
    CHK_ERR(sd, "socket"); 

    memset (&sa, '\0', sizeof(sa)); 
    sa.sin_family      = AF_INET; 
    sa.sin_addr.s_addr = inet_addr ("203.247.40.139");   /* Server IP */ 
    sa.sin_port        = htons     (atoi(argv[1]));          /* Server Port number */ 

    // Normal-connect 
    err = connect(sd, (struct sockaddr*) &sa, sizeof(sa));      
    CHK_ERR(err, "connect"); 

    // Now we have TCP conncetion. Start SSL negotiation. 
    // create a new SSL structure for a connection 
    ssl = SSL_new (ctx);     
    CHK_NULL(ssl);     

    // connect the SSL object with a file descriptor 
    SSL_set_fd (ssl, sd); 

    // initiate the TLS/SSL handshake with an TLS/SSL server 
    err = SSL_connect (ssl);     
    CHK_SSL(err); 

    /*  
     * Following two steps are optional and not required for 
     * data exchange to be successful.  
     */ 

    printf ("SSL connection using %s\n", SSL_get_cipher (ssl)); 

    // Get server's certificate (note: beware of dynamic allocation) - opt  
    server_cert = SSL_get_peer_certificate (ssl);     
    CHK_NULL(server_cert); 
    print_x509(ssl); 

    /* 
    printf ("Server certificate:\n"); 
    str = X509_NAME_oneline (X509_get_subject_name (server_cert),0,0); 
    CHK_NULL(str); 
    printf ("\t subject: %s\n", str); 
    free (str); 

    str = X509_NAME_oneline (X509_get_issuer_name  (server_cert),0,0); 
    CHK_NULL(str); 
    printf ("\t issuer: %s\n", str); 
    free (str); 
    */ 

    /*  
     * We could do all sorts of certificate verification stuff here before 
     * deallocating the certificate.  
     */ 
    X509_free (server_cert); 

    /////////////////////////////////////////////////////////////////////////////////////// 
    // DATA EXCHANGE - Send a message and receive a reply. 
     
    //err = SSL_write (ssl, "Hello World!", strlen("Hello World!")); 
    //CHK_SSL(err);  
    err = SSL_write (ssl, hello, strlen(hello));   
    CHK_SSL(err); 

    err = SSL_read (ssl, buf, sizeof(buf) - 1);     
    CHK_SSL(err); 
    buf[err] = '\0'; 
    printf ("Got %d chars:'%s'\n", err, buf); 

    // send SSL/TLS close_notify 
    SSL_shutdown (ssl);   

    /* Clean up. */ 

    close (sd); 
    SSL_free (ssl); 
    SSL_CTX_free (ctx); 
} 

서버의 개인키와 셀프 사인된 인증서 입니다. 그리고 인증서의 패스워드는 abcd 입니다.

*server-key.pem

-----BEGIN RSA PRIVATE KEY----- 
Proc-Type: 4,ENCRYPTED 
DEK-Info: DES-EDE3-CBC,AA26BA50E01C1524 

oXXeC9GyPwvJxVyqRXROLXp5Ew5n/gS6+vpDXXSIsuE8RIk17Mtx1NpOGWxmQ/hd 
L9zAvDniOoavTyKyVI0VZx13A1RzptaSP1CqEBeQUlT5DU3IFhwdj8BRXfpaWrve 
NnCZiyvr5CNgSLg0pVvcUhI9CmQtj94bLN6KATbZ/FyQZTSkrN6kh1jBwHd04RvZ 
2YvxtuzevtxvLLHOvxCxocIYnPWJzViEyuKbz/co3ywZibFXrqu2Xpgrk3lHN21M 
ZXWPeVJ2DzAIWMhqjpOX98w0hLHvZHVWpiO09avH/wzR7YbCKZpwFZUdjcUuzETZ 
OzJO3p97mf9D64KbAOmq7h6Pme8pkT/io8Qe24XyaV0C7ca67t3J3NSBVmyPNgEP 
MsfTctk98FfKmOXD7fm1y5k4YZ7weDuDRs09KNFxn6sm94Q5ssQmaAPNCVZTpGSW 
p6/sDb1dU8/2lTFFU67tquwR679h2AouF/Ea0Sa2l0o8HfcAS8YJxp77+W2qB9Zt 
ymkSJlvX7s5qt4oBryTOyDKVFzmzksjJHaar7tRJ00fTh+AICxxQvfzUE82oH49R 
+07clHW7DCh4FMevvflSujfIChVIwvwg2Vr1b63QIp8tlZ0BiPAZH8TV6cPDROVQ 
5dWDQlcOrvfNsC1qI17kvn/N4ZzKyvdB4cEZh6xMH8UfFJ+qpNhJ1gwJFe9XI11z 
90hSa9lsFqBBw2sGmnN9QxKC1UpdvjoZV1VTBEqGWYDR4S1lx575gm77nF9XcJAr 
pW7Fs9qKLWi7trWQxI0ncKQ6DGuGbh/ctebgchKe/kIA/4E0nQjTcg== 
-----END RSA PRIVATE KEY----- 

*server-req.pem

-----BEGIN CERTIFICATE----- 
MIIDYjCCAsugAwIBAgIBADANBgkqhkiG9w0BAQQFADCBgzELMAkGA1UEBhMCQVUx 
EzARBgNVBAgTClNvbWUtU3RhdGUxDDAKBgNVBAcTA0ZvbzENMAsGA1UEChMETGFu 
ZDEMMAoGA1UECxMDRm9vMRYwFAYDVQQDEw1zbmFmdS5taXQuZWR1MRwwGgYJKoZI 
hvcNAQkBFg1mdWJvYkBtaXQuZWR1MB4XDTAwMTExMzAyMjgxMVoXDTAwMTIxMzAy 
MjgxMVowgYMxCzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMQwwCgYD 
VQQHEwNGb28xDTALBgNVBAoTBExhbmQxDDAKBgNVBAsTA0ZvbzEWMBQGA1UEAxMN 
c25hZnUubWl0LmVkdTEcMBoGCSqGSIb3DQEJARYNZnVib2JAbWl0LmVkdTCBnzAN 
BgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAtvnaO7XYscdlXoTwql5uHa5Vx1ecKZOc 
pSAqGQx4LnMGbRvnb+m2OxUZ3xFc8V1a2eKlUyTKoo0bebTeS5/BLBik47TjfUlw 
/P1hLYhIFZNb+o/g3ZeWG/9QZYs0favV+0iAoqpy/scHwHZg2Vnpjivsit543s+x 
uuWuD4fpUIsCAwEAAaOB4zCB4DAdBgNVHQ4EFgQUTkDDGu1osvRIZY/LlZdmRbAs 
rM0wgbAGA1UdIwSBqDCBpYAUTkDDGu1osvRIZY/LlZdmRbAsrM2hgYmkgYYwgYMx 
CzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMQwwCgYDVQQHEwNGb28x 
DTALBgNVBAoTBExhbmQxDDAKBgNVBAsTA0ZvbzEWMBQGA1UEAxMNc25hZnUubWl0 
LmVkdTEcMBoGCSqGSIb3DQEJARYNZnVib2JAbWl0LmVkdYIBADAMBgNVHRMEBTAD 
AQH/MA0GCSqGSIb3DQEBBAUAA4GBAEmPUSEShK6tHGdwPUvwYwp7cR0mwRNi3peB 
vu5nqwjCnCFaQaUdBfh/18kT5uEpGB48A/6BImov36Aiq9cq6dVNGQkVI0y/FJ0J 
28zSYrRHXGOS5qMoWRaqCabuYbDUsx/bvCr21ET948jMawdd8nq+3ungEQzlIZtO 
oWcmJYad 
-----END CERTIFICATE----- 

*컴파일

# gcc -o serv serv.c -lssl -lcrypto 
# gcc -o serv cli.c -lssl -lcrypto 

*실행

# ./serv 
Enter PEM pass phrase: abcd 

# ./cli 
Message to send: hello!! 

이번에는 자바 클라이언트 입니다.. 여기에서 에러가 나는데.. 봐주세요..^^;

*SunSSLSocketClient.java

import java.io.*; 
import java.net.*; 
import java.security.*; 
import javax.net.ssl.*; 

public class SunSSLSocketClient { 
    private static final String HOST = "127.0.0.1"; 
    private static final int    PORT = 1111; 
    private static String [] socketProtocolVersion = new String[] {"TLSv1"}; 

    public static void main(String [] args) throws Exception { 
        char [] passPhrase = "abcd1234".toCharArray(); 

        KeyStore keystore = KeyStore.getInstance("JKS"); 
        keystore.load(new FileInputStream(".keystore"), passPhrase); 

        TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509"); 
        tmf.init(keystore); 

        SSLContext context = SSLContext.getInstance("TLS"); 
        TrustManager [] trustManagers = tmf.getTrustManagers(); 

        context.init(null, trustManagers, null); 

        SSLSocketFactory sf = context.getSocketFactory(); 
        SSLSocket socket = (SSLSocket)sf.createSocket(HOST, PORT); 
        socket.setEnabledProtocols(socketProtocolVersion); 
     
        System.out.println(socket); 

        PrintWriter pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())), true); 
        BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream())); 

        pw.println("Hello!!"); 
        String fromServer = br.readLine(); 
        System.out.println(fromServer); 
    } 
} 

책에보니까 보통 이렇게 하더라구요.. 이렇게 코드를 작성하고 keytool을 사용해서 키저장소를 만들었습니다..
# keytool -genkey -keystore .keystore
.....

자바 클라이언트 실행
# java SunSSLSocketClient

Posted by '김용환'
,