SSL握手通信详解及linux下c/c++ SSL Socket代码举例

更新:修改了源码,在ubuntu16.04+OpenSSL 1.0.2环境下编译通过。
SSL(Secure Sockets Layer 安全套接层),及其继任者传输层安全(Transport Layer Security,TLS)是为网络通信提供安全及数据完整性的一种安全协议。TLS与SSL在传输层对网络连接进行加密。

安全证书既包含了用于加密数据的密钥,又包含了用于证实身份的数字签名。安全证书采用公钥加密技术。公钥加密是指使用一对非对称的密钥进行加密或解密。每一对密钥由公钥和私钥组成。公钥被广泛发布。私钥是隐秘的,不公开。用公钥加密的数据只能够被私钥解密。反过来,使用私钥加密的数据只能被公钥解密。这个非对称的特性使得公钥加密很有用。在安全证书中包含着一对非对称的密钥。只有安全证书的所有者才知道私钥。当通信方A将自己的安全证书发送给通信方B时,实际上发给通信方B的是公开密钥,接着通信方B可以向通信方A发送用公钥加密的数据,只有通信方A才能使用私钥对数据进行解密,从而获得通信方B发送的原始数据。安全证书中的数字签名部分是通信方A的电子身份证。数字签名告诉通信方B该信息确实由通信方A发出,不是伪造的,也没有被篡改。

客户与服务器通信时,首先要进行SSL握手,SSL握手主要完成以下任务:

1)协商使用的加密套件。加密套件中包括一组加密参数,这些参数指定了加密算法和密钥的长度等信息。

2)验证对方的身份,此操作是可选的。

3)确定使用的加密算法。

4)SSL握手过程采用非对称加密方法传递数据,由此来建立一个安全的SSL会话。SSL握手完成后,通信双方将采用对称加密方法传递实际的应用数据。

以下是SSL握手的具体流程:

(1)客户将自己的SSL版本号、加密参数、与SSL会话有关的数据及其他一些必要信息发送到服务器。

(2)服务器将自己的SSL版本号、加密参数、与SSL会话有关的数据及其他一些必要信息发送给客户,同时发给客户的还有服务器的证书。如果服务器需要验证客户身份,服务器还会发出要求客户提供安全证书的请求。

(3)客户端验证服务器证书,如果验证失败,就提示不能建立SSL连接。如果成功,那么继续下一步骤。

(4)客户端为本次SSL会话生成预备主密码(pre-master secret),并将其用服务器公钥加密后发送给服务器。

(5)如果服务器要求验证客户身份,客户端还要对另外一些数据签名后,将其与客户端证书一起发送给服务器。

(6)如果服务器要求验证客户身份,则检查签署客户证书的CA(Certificate Authority,证书机构)是否可信。如果不在信任列表中,结束本次会话。如果检查通过,服务器用自己的私钥解密收到的预备主密码(pre-master secret),并用它通过某些算法生成本次会话的主密码(master secret)。

(7)客户端与服务器端均使用此主密码(master secret)生成此次会话的会话密钥(对称密钥)。在双方SSL握手结束后传递任何消息均使用此会话密钥。这样做的主要原因是对称加密比非对称加密的运算量要低一个数量级以上,能够显著提高双方会话时的运算速度。

(8)客户端通知服务器此后发送的消息都使用这个会话密钥进行加密,并通知服务器客户端已经完成本次SSL握手。

(9)服务器通知客户端此后发送的消息都使用这个会话密钥进行加密,并通知客户端服务器已经完成本次SSL握手。

(10)本次握手过程结束,SSL会话已经建立。在接下来的会话过程中,双方使用同一个会话密钥分别对发送和接收的信息进行加密和解密。

以下为 linux c/c++ SSL socket Client和Server的代码参考。

客户端代码如下:

#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <malloc.h>
#include <string.h>
#include <sys/socket.h>
#include <resolv.h>
#include <netdb.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#define FAIL    -1

int OpenConnection(const char* hostname,int port)

{  int sd;

struct hostent *host;

struct sockaddr_in addr;

if( (host = gethostbyname(hostname)) == NULL )

{

    printf("Eroor: %s\n",hostname);

    perror(hostname);

    abort();

}

sd = socket(PF_INET, SOCK_STREAM, 0);

bzero(&addr,sizeof(addr));

addr.sin_family = AF_INET;

addr.sin_port = htons(port);

addr.sin_addr.s_addr = *(long*)(host->h_addr);

if( connect(sd, (struct sockaddr*)&addr,sizeof(addr)) != 0 )

{

    close(sd);

    perror(hostname);

    abort();

}

return sd;

}

SSL_CTX* InitCTX(void)

{   
const SSL_METHOD *method;

SSL_CTX *ctx;

OpenSSL_add_all_algorithms(); /* Load cryptos, et.al. */

SSL_load_error_strings();  /* Bring in and register error messages */

method = SSLv2_client_method(); /* Create new client-method instance */

ctx = SSL_CTX_new(method);  /* Create new context */

if( ctx == NULL )

{

    ERR_print_errors_fp(stderr);

    printf("Eroor: %s\n",stderr);

    abort();

}

return ctx;

}

void ShowCerts(SSL* ssl)

{   X509 *cert;

    char*line;

    cert = SSL_get_peer_certificate(ssl);/* get the server's certificate */

    if( cert != NULL )

    {

    printf("Server certificates:\n");

    line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);

    printf("Subject: %s\n", line);

    free(line);      /* free the malloc'ed string */

    line = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);

    printf("Issuer: %s\n", line);

    free(line);      /* free the malloc'ed string */

    X509_free(cert);    /* free the malloc'ed certificate copy */

}

else

    printf("No certificates.\n");

}

int main(int count,char*strings[])

{   SSL_CTX *ctx;

int server;

SSL *ssl;

char buf[1024];

int bytes;

char*hostname, *portnum;

if( count != 3 )

{

    printf("usage: %s <hostname> <portnum>\n", strings[0]);

    exit(0);

}

SSL_library_init();

hostname=strings[1];

portnum=strings[2];

ctx = InitCTX();

server = OpenConnection(hostname,atoi(portnum));

ssl = SSL_new(ctx);     /* create new SSL connection state */

SSL_set_fd(ssl, server);   /* attach the socket descriptor */

if( SSL_connect(ssl) == FAIL )  /* perform the connection */

{

    printf("Eroor: %s\n",stderr);

    ERR_print_errors_fp(stderr);

}

else

{  char*msg ="HelloWorld";

    printf("Connected with %s encryption\n", SSL_get_cipher(ssl));

    ShowCerts(ssl);       /* get any certs */

    SSL_write(ssl, msg,strlen(msg));  /* encrypt & send message */

    bytes = SSL_read(ssl, buf,sizeof(buf));/* get reply & decrypt */

    buf[bytes] = 0;

    printf("Received: \"%s\"\n", buf);

    SSL_free(ssl);       /* release connection state */

}

close(server);        /* close socket */

SSL_CTX_free(ctx);       /* release context */

return 0;

}

服务端代码如下:

#include <errno.h>

#include <unistd.h>

#include <malloc.h>

#include <string.h>

#include <arpa/inet.h>

#include <sys/socket.h>

#include <sys/types.h>

#include <netinet/in.h>

#include <resolv.h>

#include "openssl/ssl.h"

#include "openssl/err.h"

#define FAIL    -1

usingnamespacestd;

intOpenListener(intport)

{  intsd;

structsockaddr_in addr;

sd = socket(PF_INET, SOCK_STREAM, 0);

bzero(&addr,sizeof(addr));

addr.sin_family = AF_INET;

addr.sin_port = htons(port);

addr.sin_addr.s_addr = INADDR_ANY;

if( bind(sd, (structsockaddr*)&addr,sizeof(addr)) != 0 )

{

    perror("can't bind port");

    abort();

}

if( listen(sd, 10) != 0 )

{

    perror("Can't configure listening port");

    abort();

}

returnsd;

}

SSL_CTX* InitServerCTX(void)

{

SSL_CTX *ctx = NULL;

    #if OPENSSL_VERSION_NUMBER >= 0x10000000L

           constSSL_METHOD *method;

    #else

            SSL_METHOD *method;

    #endif

    SSL_library_init();

    OpenSSL_add_all_algorithms(); /* load & register all cryptos, etc. */

    SSL_load_error_strings();  /* load all error messages */

    method = SSLv23_client_method();/* create new server-method instance */

    ctx = SSL_CTX_new(method);  /* create new context from method */

    if( ctx == NULL )

    {

        ERR_print_errors_fp(stderr);

        abort();

    }

    returnctx;

}

voidLoadCertificates(SSL_CTX* ctx,char* CertFile,char* KeyFile)

{

//New lines

    if(SSL_CTX_load_verify_locations(ctx, CertFile, KeyFile) != 1)

        ERR_print_errors_fp(stderr);

    if(SSL_CTX_set_default_verify_paths(ctx) != 1)

        ERR_print_errors_fp(stderr);

    //End new lines

/* set the local certificate from CertFile */

if( SSL_CTX_use_certificate_file(ctx, CertFile, SSL_FILETYPE_PEM) <= 0 )

{

    ERR_print_errors_fp(stderr);

    abort();

}

/* set the private key from KeyFile (may be the same as CertFile) */

if( SSL_CTX_use_PrivateKey_file(ctx, KeyFile, SSL_FILETYPE_PEM) <= 0 )

{

    ERR_print_errors_fp(stderr);

    abort();

}

/* verify private key */

if( !SSL_CTX_check_private_key(ctx) )

{

    fprintf(stderr,"Private key does not match the public certificate\n");

    abort();

}

printf("LoadCertificates Compleate Successfully.....\n");

}

voidShowCerts(SSL* ssl)

{   X509 *cert;

char*line;

cert = SSL_get_peer_certificate(ssl);/* Get certificates (if available) */

if( cert != NULL )

{

    printf("Server certificates:\n");

    line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);

    printf("Subject: %s\n", line);

    free(line);

    line = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);

    printf("Issuer: %s\n", line);

    free(line);

    X509_free(cert);

}

else

    printf("No certificates.\n");

}

voidServlet(SSL* ssl)/* Serve the connection -- threadable */

{  charbuf[1024];

charreply[1024];

intsd, bytes;

constchar* HTMLecho="<html><body><pre>%s</pre></body></html>\n\n";

if( SSL_accept(ssl) == FAIL )    /* do SSL-protocol accept */

    ERR_print_errors_fp(stderr);

else

{

    ShowCerts(ssl);       /* get any certificates */

    bytes = SSL_read(ssl, buf,sizeof(buf));/* get request */

    if( bytes > 0 )

    {

        buf[bytes] = 0;

        printf("Client msg: \"%s\"\n", buf);

        sprintf(reply, HTMLecho, buf);  /* construct reply */

        SSL_write(ssl, reply,strlen(reply));/* send reply */

    }

    else

        ERR_print_errors_fp(stderr);

}

sd = SSL_get_fd(ssl);      /* get socket connection */

SSL_free(ssl);        /* release SSL state */

close(sd);         /* close connection */

}

intmain(intcount,char*strings[])

{   SSL_CTX *ctx;

intserver;

char*portnum;

if( count != 2 )

{

    printf("Usage: %s <portnum>\n", strings[0]);

    exit(0);

}

else

{

    printf("Usage: %s <portnum>\n", strings[1]);

}

SSL_library_init();

portnum = strings[1];

ctx = InitServerCTX();       /* initialize SSL */

LoadCertificates(ctx,"/home/stud/kawsar/mycert.pem","/home/stud/kawsar/mycert.pem"); /* load certs */

server = OpenListener(atoi(portnum));   /* create server socket */

while(1)

{  structsockaddr_in addr;

    socklen_t len =sizeof(addr);

    SSL *ssl;

    intclient = accept(server, (structsockaddr*)&addr, &len); /* accept connection   as usual */

    printf("Connection: %s:%d\n",inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));

    ssl = SSL_new(ctx);             /* get new SSL state with context */

    SSL_set_fd(ssl, client);     /* set connection socket to SSL state */

    Servlet(ssl);        /* service connection */

}

close(server);         /* close server socket */

SSL_CTX_free(ctx);        /* release context */

}

转自SSL握手通信详解及linux C Socket代码举例

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值