1. 程式人生 > 其它 >ssl客戶端原始碼

ssl客戶端原始碼

目錄結構

- ssl_client.h      # 標頭檔案
- ssl_client.c      # 程式碼檔案
- Makefile          # makefile
- ca                # 證書目錄
  - ca.crt          # CA證書
  - client.crt      # 服務端證書
  - client.key      # 服務端金鑰

ssl_client.h

#ifndef _SSL_CLIENT_H
#define _SSL_CLIENT_H

#include <stdio.h>
#include <strings.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/ssl.h>
#include <openssl/err.h>

#define MAXBUF 1024

#if 0
#define PORT 6789
#define IPADDR "127.0.0.1"
#else
#define PORT 9487
#define IPADDR "10.0.0.51"
#endif

#define log(fmt,...) printf("%s:%d:"fmt"\n",__func__,__LINE__,##__VA_ARGS__)
#define BUFFER_SIZE 1024
#define HTTP_GET_KEEPALIVE "GET %s HTTP/1.1\r\nHost: %s\r\nAccept: */*\r\nConnection: keep-alive\r\n\r\n"

typedef struct connect_node{
	int sockfd;
	SSL *ssl;
}Connect_Node_T, *Connect_pNode;

SSL_CTX *glbCTX = NULL;

int connect_socket(Connect_pNode pNode);
int connect_ssl(Connect_pNode pNode);
int close_connect(Connect_pNode pNode);
int init_connect_node(Connect_pNode pNode);
int test_ssl_io(Connect_pNode pNode);
int init_ssl();
int cleanup_ssl();
int ShowCerts(SSL * ssl);


#endif

ssl_client.c

#include "ssl_client.h" 
 
void test()
{
	int ret = 0;
	Connect_Node_T client_node;
    ret = init_ssl();
	if(ret != 0)
	{
		printf("init ssl error\n");
		return;
	}

	ret = init_connect_node(&client_node);
	if(ret != 0)
	{
		printf("init connect node error\n");
		return;
	}
	
    ret = connect_socket(&client_node);
	if(ret != 0)
	{
		printf("connect socket error\n");
		return;
	}
	
	ret = connect_ssl(&client_node);
	if(ret != 0)
	{
		printf("connect ssl error\n");
		return;
	}
	
	ret = test_ssl_io(&client_node);
	if(ret != 0)
	{
		printf("test ssl io error\n");
		return;
	}
	
	close_connect(&client_node);
	cleanup_ssl();
}

int main(int argc, char **argv)
{
    test();

	return 0;
}

int connect_socket(Connect_pNode pNode)
{
	struct sockaddr_in server_addr;
	/* 建立一個 socket 用於 tcp 通訊 */
    if ((pNode->sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) 
    {
        perror("Socket");
        return -1;
    }
    printf("socket created\n");
 
    /* 初始化伺服器端(對方)的地址和埠資訊 */
    bzero(&server_addr, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(PORT);
    if(inet_pton(AF_INET, IPADDR, &server_addr.sin_addr) <= 0)
	{
		perror("inet_pton : ");
		return -1;
	}
    printf("address created\n");
 
    /* 連線伺服器 */
    if (connect(pNode->sockfd, (struct sockaddr *) &server_addr, sizeof(server_addr)) < 0) 
    {
        perror("Connect ");
        return -1;
    }
    printf("server connected at fd :%d\n", pNode->sockfd);
	
	return 0;
}

int close_connect(Connect_pNode pNode)
{
	/* 關閉連線 */
	if(pNode->ssl != NULL)
	{
	    SSL_shutdown(pNode->ssl);
	    SSL_free(pNode->ssl);
		pNode->ssl = NULL;
	}
	if(pNode->sockfd != -1)
	{
	    close(pNode->sockfd);	
		pNode->sockfd = -1;
	}
	
	return 0;
}

int init_connect_node(Connect_pNode pNode)
{
	pNode->sockfd = -1;
	pNode->ssl = NULL;
	return 0;
}


int init_ssl()
{
	char CAPATH[BUFFER_SIZE];
	char *CANAME = "ca.crt";
	char *clientCrtName = "client.crt";
	char *clientKeyName = "client.key";
	char clientCrt[BUFFER_SIZE], clientKey[BUFFER_SIZE], CAFILE[BUFFER_SIZE];
	
	SSL_library_init();
    SSL_load_error_strings();
    glbCTX = SSL_CTX_new(SSLv23_client_method());
    if (glbCTX == NULL) 
    {
        perror("SSL new ctx ");
        return -1;
    }

	memset(CAPATH, 0, sizeof(CAPATH));
	if(getcwd(CAPATH, sizeof(CAPATH)) == NULL)
	{
		perror("getcwd");
		return -1;
	}
	strcat(CAPATH, "/ca/");

	memset(CAFILE, 0, sizeof(CAFILE));
	sprintf(CAFILE, "%s%s", CAPATH, CANAME);
	printf("%s\n", CAFILE);
 	if (SSL_CTX_load_verify_locations(glbCTX, CAFILE, NULL)<=0)
	{
        perror("load ca ");
        return -1;
    }
    else
    {
    	printf("load root crt success\n");
    }

	/* 載入使用者的數字證書, 此證書用來發送給服務端。 證書裡包含有公鑰 */
    memset(clientCrt, 0, sizeof(clientCrt));
	sprintf(clientCrt, "%s%s", CAPATH, clientCrtName);
	if (SSL_CTX_use_certificate_file(glbCTX, clientCrt, SSL_FILETYPE_PEM) <= 0) 
    {
        perror("load crt ");
        return -1;
    }
    else
    {
    	printf("load user crt successful\n");
    }

    /* 載入使用者私鑰 */
	memset(clientKey, 0, sizeof(clientKey));
	sprintf(clientKey, "%s%s", CAPATH, clientKeyName);
    if (SSL_CTX_use_PrivateKey_file(glbCTX, clientKey, SSL_FILETYPE_PEM) <= 0) 
    {
        perror("load user key :");
        return -1;
    }
    else
    {
    	printf("load user key successful\n");
    }

    /* 檢查使用者私鑰是否正確 */
    if (!SSL_CTX_check_private_key(glbCTX)) 
    {
        perror("check user key:");
        return -1;
    }
    else
    {
    	printf("checking user key successful\n");
    }
	
	SSL_CTX_set_verify(glbCTX, SSL_VERIFY_PEER, NULL);
	
	return 0;
}

int cleanup_ssl()
{
	if(glbCTX != NULL)
	{
		SSL_CTX_free(glbCTX);
	}
	return 0;
}

int test_ssl_io(Connect_pNode pNode)
{
	int len;
    char buffer[MAXBUF + 1];

	bzero(buffer, MAXBUF + 1);
    strcpy(buffer, "from client->server");
    /* 發訊息給伺服器 */
    len = SSL_write(pNode->ssl, buffer, strlen(buffer));
    if (len < 0)
    {
        printf("訊息'%s'傳送失敗!錯誤程式碼是%d,錯誤資訊是'%s'\n",buffer, errno, strerror(errno));
    }
    else
    {
        printf("訊息'%s'傳送成功,共傳送了%d個位元組!\n",buffer, len);
    }
 
 	/* 接收對方發過來的訊息,最多接收 MAXBUF 個位元組 */
    bzero(buffer, MAXBUF + 1);
    /* 接收伺服器來的訊息 */
    len = SSL_read(pNode->ssl, buffer, MAXBUF);
    if (len > 0)
    {
        printf("接收訊息成功:'%s',共%d個位元組的資料\n", buffer, len);
    }
    else 
    {
        printf("訊息接收失敗!錯誤程式碼是%d,錯誤資訊是'%s'\n",errno, strerror(errno));
        return -1;
    }

	return 0;
}

int ShowCerts(SSL * ssl)
{
    X509 *cert;
    char *line;
 
    cert = SSL_get_peer_certificate(ssl);
    if(SSL_get_verify_result(ssl) == X509_V_OK)
    {
        printf("證書驗證通過\n");
	}
	else
	{
		printf("證書驗證失敗\n");
		return -1;
	}
	if (cert != NULL) 
	{
        printf("數字證書資訊:\n");
        line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
        printf("證書: %s\n", line);
        free(line);
        line = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
        printf("頒發者: %s\n", line);
        free(line);
        X509_free(cert);
    } 
	else
	{
        printf("無證書資訊!\n");
		return -1;
	}
	return 0;
}

int connect_ssl(Connect_pNode pNode)
{
	/* 基於 ctx 產生一個新的 SSL */
    pNode->ssl = SSL_new(glbCTX);
	if(pNode->ssl == NULL)
	{
		perror("new ssl ");
		return -1;
	}
    if(SSL_set_fd(pNode->ssl, pNode->sockfd) != 1)
    {
		perror("ssl set fd ");
		return -1;
	}
    /* 建立 SSL 連線 */
    if (SSL_connect(pNode->ssl) == -1)
    {
    	perror("ssl connect ");
    	return -1;
    }    
    else 
    {
        printf("Connected with %s encryption\n", SSL_get_cipher(pNode->ssl));
    }

	if(ShowCerts(pNode->ssl) != 0)
	{
		return -1;
	}

	return 0;
}


 

makefile


EXE = ssl_client
OBJ = ssl_client.o

CC = gcc
CURRDIR = $(shell pwd)
CFLAGS = -Wall -lssl -lcrypto
INCLUDE = -I$(CURRDIR)

all : $(EXE)

$(EXE):$(OBJ)
	$(CC) $(CFLAGS) $(INCLUDE) -o $(EXE) $(OBJ)

clean:
	-rm -rf $(EXE) $(OBJ)


ca.crt

-----BEGIN CERTIFICATE-----
MIICZjCCAc+gAwIBAgIUFSyoO8SzaUFgjnlOZVRak6T2oqUwDQYJKoZIhvcNAQEL
BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMTEwMjgwMzM3MjBaFw0yMjEw
MjgwMzM3MjBaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw
HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwgZ8wDQYJKoZIhvcNAQEB
BQADgY0AMIGJAoGBAMV/aMOCdB2xaWC5Cr26R8rLJsK+JdC2otzs/o3qyWXggRQ6
g8vBQPFZ+OGldJtueJgrDbzrPhsJKF8eGfseXFlPg73iHkRjJ8nPe5pa3SCuaznU
3WwwzifSm4goE+x7rJyT8eTlGTdB2ONXkOR3d8kgTvLFnWPxM/6M6XUfBHszAgMB
AAGjUzBRMB0GA1UdDgQWBBTiHglejO5eKy4HlhV8bY3n3Wg1LjAfBgNVHSMEGDAW
gBTiHglejO5eKy4HlhV8bY3n3Wg1LjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3
DQEBCwUAA4GBAF7QCa9Gb5PTYHbLTAULr1lpuuSgY89iD4l1EiAiMYq6KvbI5xmz
OoQMfcb4b286PzME71NNwNqc7i/c4iy/qZ51yDfTA7coAU+zprbp6fHUgxghmNZB
iNmqkNF0v6FYFie+BTSAU4HZbPmi6dpdg3zTVQGnv3Sx5gxjgJj7MJrK
-----END CERTIFICATE-----

client.crt

-----BEGIN CERTIFICATE-----
MIIB8TCCAVoCAQEwDQYJKoZIhvcNAQELBQAwRTELMAkGA1UEBhMCQVUxEzARBgNV
BAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0
ZDAeFw0yMTEwMjkwMzAxMjVaFw0yMjEwMjkwMzAxMjVaMD0xCzAJBgNVBAYTAkNO
MQswCQYDVQQIDAJHRDEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRk
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDuhC7M0TCJftIVK43iUFGdsgQe
tTJjQmGcVD1/f25UPgyf2HGSMSKiy+/KdAxeAGmuDQcmbYomnOYMIycZZOo8/P+a
YetBvzdw1wTVha+Pmak7eJglKXguYng6YGLj4oHhxG3kaGd9JWejouLVR5dKTDqq
cdMUawLAzuRFD/fKkQIDAQABMA0GCSqGSIb3DQEBCwUAA4GBAKUaJDr5hkzF5isg
9/CG130K3ROQcif7+fvLu/2v36ujdbfh6hqvN2FvfofRFyK1m3rFYLTT7h2vfift
6mGg+2l+s2z1S2tYVnHNggR3WG191rXEr9nf+0wBj04S+et1YTfll2DuD2wi3YVA
G/W4AMpItfg1eGpaCeZVE4M0EE9R
-----END CERTIFICATE-----

client.key

-----BEGIN RSA PRIVATE KEY-----
MIICXgIBAAKBgQDuhC7M0TCJftIVK43iUFGdsgQetTJjQmGcVD1/f25UPgyf2HGS
MSKiy+/KdAxeAGmuDQcmbYomnOYMIycZZOo8/P+aYetBvzdw1wTVha+Pmak7eJgl
KXguYng6YGLj4oHhxG3kaGd9JWejouLVR5dKTDqqcdMUawLAzuRFD/fKkQIDAQAB
AoGBAIbYDglXPsSNAUJctEM9O1cW/ENMF2eMcNjLu1Toezx/M+3ulQ6cXsOA3lkr
0I4YV6bB0MgF57O6wkgcW498wPuQdI31IRKNdZcvTElOy4O+Xwv6DPJwJee8mSe+
fd+oHkVXDyvKJyHqXnzuD4Nzw3ptnmMQWDYgJoIfCzrFXYr9AkEA+jkpu9Bi7dxe
C4sfV8Y9b9rdYVKk4AYje2dngj4d6rndsTtiUZpob3Ocy29d4KJU2SK71slAzkB/
X2e8zc5pywJBAPQF1LS7YLl9XgEd4a/vArS/pCbImoAfwmffFKJBcj8u+f+yCTFx
uzeAaOVdDkKPBCi1TXk1RQcOM6lH94QtwZMCQCGYUSkdNlsXLi1AlYm0XQVKjlSF
wwss59Cmtnf/HQcpw0ELZwzrvT/Rdui9YA5L3TZ1+mBBDwliEXfetrOvFfcCQQDx
e7+2JuQeS9E2O7L23wkHg8rXUpeKiWNiVWHe+/MqUbu27SGp24nQ7/NeX4tYFJ2i
B2EmvK+VvtkjAYQROxKfAkEAlOZlJFAIRkjpROy1tfZ87aTXbShrctq4Ayt31cvE
Um5/lY1B8ZIduZM3P3X6G2cVZSPEDJ0P9R6LZkoL3kHMpg==
-----END RSA PRIVATE KEY-----