openSSL解析证书及建立安全通道通信实验
写在前面
-
实验一可根据输入证书路径做解析
-
实验二需要配套证书,文件夹结构如下
certs │ ├─ca │ ca.crt │ ca.csr │ ca.key │ ├─client │ client.crt │ client.csr │ client.key │ client.pem │ └─server server.crt server.csr server.key server.pem
实验一
实验内容
利用openSSL或其他密码库(如python,go等),编写一个解码X.509数字证书的程序,能够解析证书中的基本ASN1项内容并打印输出
实验分析
该实验我尝试了多种方式来实现,在本实验报告中两种简单一种难:
-
Simple
1.用pyOpenssl实现 2.用Openssl把DER格式的证书转成X509格式,然后使用x509_print函数直接输出整个证书
-
hard
把DER格式转为X509格式再 逐项 使用函数分析输出(效果不如x509_print好)
实验代码
第一种 用pyOpenssl
import OpenSSL
from dateutil import parser
print("请输入证书路径")
path = input()
content = open(path,"rb").read()
cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_ASN1, content)
print ("证书版本:",cert.get_version() + 1)
print ("证书***:",hex(cert.get_serial_number()))
print ("证书中使用的签名算法:",cert.get_signature_algorithm().decode("UTF-8"))
certIssue = cert.get_issuer()
print ("颁发者:",certIssue.commonName)
datetime_struct = parser.parse(cert.get_notBefore().decode("UTF-8"))
print ("有效期从:",datetime_struct.strftime('%Y-%m-%d %H:%M:%S'))
datetime_struct = parser.parse(cert.get_notAfter().decode("UTF-8"))
print ("到:",datetime_struct.strftime('%Y-%m-%d %H:%M:%S'))
certSubject = cert.get_subject()
print("使用者:",certSubject.commonName)
print ("证书是否已经过期:",cert.has_expired())
print("公钥长度:" ,cert.get_pubkey().bits())
print("公钥:\n" ,OpenSSL.crypto.dump_publickey(OpenSSL.crypto.FILETYPE_PEM, cert.get_pubkey()).decode("utf-8"))
print("扩展项数目:",cert.get_extension_count())
运行结果
第二种 用x509_print
#define _CRT_SECURE_NO_WARNINGS
#define MAX_LENGTH 4096
#include <openssl/x509.h>
extern "C" {
#include "openssl/applink.c"
};
int my_load_cert(unsigned char *str, unsigned long *str_len,
const char *verify_cert, const unsigned int cert_len)
{
FILE *fp;
fp = fopen(verify_cert, "rb");
if (NULL == fp)
{
fprintf(stderr, "fopen fail\n");
return -1;
}
*str_len = fread(str, 1, cert_len, fp);
fclose(fp);
return 0;
}
X509 *der_to_x509(const unsigned char *der_str, unsigned int der_str_len)
{
X509 *x509;
x509 = d2i_X509(NULL, &der_str, der_str_len);
if (NULL == x509)
{
fprintf(stderr, "d2i_X509 fail\n");
return NULL;
}
return x509;
}
int main()
{
OpenSSL_add_all_algorithms();
char cert[MAX_LENGTH];
unsigned char ca_der[MAX_LENGTH];
unsigned long ca_der_len;
X509 *ca = NULL;
cout << "请输入证书地址" << endl;
char *path = new char[80];
cin.getline(path, 80);
/* x509初始化 */
sprintf(cert, path);
my_load_cert(ca_der, &ca_der_len, cert, MAX_LENGTH);
ca = der_to_x509(ca_der, ca_der_len);
int ret;
BIO *b;
b = BIO_new(BIO_s_file());
BIO_set_fp(b, stdout, BIO_NOCLOSE);
ret = X509_print(b, ca);
BIO_free(b);
X509_free(ca);
return 0;
}
运行结果
第三种 逐项解析输出
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <fstream>
#include <ctime>
#include <iomanip>
#include <openssl/ossl_typ.h>
#include <openssl/evp.h>
#include <openssl/x509.h>
#include <openssl/x509v3.h>
#include <openssl/pem.h>
#include <openssl/conf.h>
extern "C" {
#include "openssl/applink.c"
};
using namespace std;
//#pragma comment(lib,"libssl.lib")
//#pragma comment(lib,"libcrypto.lib")
#define MAX_LEGTH 4096
int my_load_cert(unsigned char *str, unsigned long *str_len,
const char *verify_cert, const unsigned int cert_len)
{
FILE *fp;
fp = fopen(verify_cert, "rb");
if (NULL == fp)
{
fprintf(stderr, "fopen fail\n");
return -1;
}
*str_len = fread(str, 1, cert_len, fp);
fclose(fp);
return 0;
}
X509 *der_to_x509(const unsigned char *der_str, unsigned int der_str_len)
{
X509 *x509;
x509 = d2i_X509(NULL, &der_str, der_str_len);
if (NULL == x509)
{
fprintf(stderr, "d2i_X509 fail\n");
return NULL;
}
return x509;
}
void hex_encode(unsigned char* readbuf, void *writebuf, size_t len)
{
for (size_t i = 0; i < len; i++) {
char *l = (char*)(2 * i + ((intptr_t)writebuf));
sprintf(l, "%02x", readbuf[i]);
}
}
void version(X509* cert) {
//版本号
int ver = X509_get_version(cert);
cout << "Version: V" << ver + 1 << endl;
}
void serial(X509* cert) {
//***
ASN1_INTEGER *asn1_i = NULL;
BIGNUM *bignum = NULL;
char *serial = NULL;
asn1_i = X509_get_serialNumber(cert);
bignum = ASN1_INTEGER_to_BN(asn1_i, NULL);
serial = BN_bn2hex(bignum);
cout << "Serial: " << serial << endl;
}
void signAlgo(X509* cert) {
//签名算法
EVP_PKEY *pubkey = X509_get_pubkey(cert);
int sig_nid = X509_get_signature_nid(cert);
if (sig_nid == NID_undef) {
cout << "unable to find specified signature algorithm name." << endl;
}
else {
cout << "Signature Algorithm: " << string(OBJ_nid2ln(sig_nid)) << endl;
}
}
string Entity(X509_NAME *sub) {
BIO * bio_out = BIO_new(BIO_s_mem());
X509_NAME_print(bio_out, sub, 0);
BUF_MEM *bio_buf;
BIO_get_mem_ptr(bio_out, &bio_buf);
string ject = string(bio_buf->data, bio_buf->length);
BIO_free(bio_out);
return ject;
}
void iss(X509* cert) {
//颁发者
X509_NAME *issuer = X509_get_subject_name(cert);
cout << "Issuer: " << Entity(issuer) << endl;
}
int mypint(const char ** s, int n, int min, int max, int e)
{
int retval = 0;
while (n) {
if (**s < '0' || **s > '9') { e = 1; return 0; }
retval *= 10;
retval += **s - '0';
--n; ++(*s);
}
if (retval < min || retval > max) e = 1;
return retval;
}
void asnTotime(ASN1_TIME *t) {
const char *s;
int generalized;
struct tm time;
int err = 0;
if (t->type == V_ASN1_GENERALIZEDTIME) {
generalized = 1;
}
else if (t->type == V_ASN1_UTCTIME) {
generalized = 0;
}
else {
cout << "error" << endl;
return;
}
s = (char *)t->data; // Data should be always null terminated
if (s == NULL || s[t->length] != '\0') {
cout << "error" << endl;
}
if (generalized) {
time.tm_year = mypint(&s, 4, 0, 9999, err) - 1900;
}
else {
time.tm_year = mypint(&s, 2, 0, 99, err);
if (time.tm_year < 50)
time.tm_year += 100;
}
time.tm_mon = mypint(&s, 2, 1, 12, err) - 1;
time.tm_mday = mypint(&s, 2, 1, 31, err);
time.tm_hour = mypint(&s, 2, 0, 23, err);
time.tm_min = mypint(&s, 2, 0, 59, err);
if (*s >= '0' && *s <= '9') {
time.tm_sec = mypint(&s, 2, 0, 59, err);
}
else {
time.tm_sec = 0;
}
time.tm_year = time.tm_year + 1900;
time.tm_mon = time.tm_mon + 1;
time.tm_hour = time.tm_hour + 8;
cout << time.tm_year << "年" << time.tm_mon << "月" << time.tm_mday << "日 ";
cout << time.tm_hour << ":" << time.tm_min << ":" << time.tm_sec << endl;
}
void validTime(X509* cert) {
//时间
int err = 0;
ASN1_TIME *start = X509_get_notBefore(cert);
ASN1_TIME *end = X509_get_notAfter(cert);
cout << "Validate " << endl;
cout << setw(10) << "from: "; asnTotime(start);
cout << setw(10) << "to: "; asnTotime(end);
}
void sub(X509* cert) {
//使用者
X509_NAME *subject = X509_get_subject_name(cert);
cout << "Subject: " << Entity(subject) << endl;
}
void pubKey(X509* cert) {
//公钥
EVP_PKEY *pubkey = X509_get_pubkey(cert);
X509_PUBKEY *key = X509_get_X509_PUBKEY(cert);
ASN1_OBJECT *ppkalg;
const unsigned char *pk;
int ppklen;
X509_ALGOR *pa;
X509_PUBKEY_get0_param(&ppkalg, &pk, &ppklen, &pa, key);
//cout << ppklen << endl;
RSA *rsa_key;
rsa_key = EVP_PKEY_get1_RSA(pubkey);
cout << "Subject Public Key Info: " << endl;
RSA_print_fp(stdout, rsa_key, 0);
}
void issuKeyHash(X509* cert) {
//颁发机构**标识符
int i = 0;
int crit = 0;
char value[512] = { 0 };
AUTHORITY_KEYID *akeyid = NULL;
akeyid = (AUTHORITY_KEYID*)X509_get_ext_d2i(cert, NID_authority_key_identifier, &crit, NULL);
strcat_s(value, 512, "KeyID=");
for (i = 0; i < akeyid->keyid->length; i++)
{
char keyid[8] = { 0 };
sprintf_s(keyid, 8, "%x ", akeyid->keyid->data[i]);
strcat_s(value, 512, keyid);
}
cout << "Issuer Key Identifier: " << value << endl;
}
void subKeyHash(X509* cert) {
//使用者**标识符
int i = 0;
int crit = 0;
char value[512] = { 0 };
ASN1_OCTET_STRING *skid = NULL;
skid = (ASN1_OCTET_STRING*)X509_get_ext_d2i(cert, NID_subject_key_identifier, &crit, NULL);
for (i = 0; i < skid->length; i++)
{
char keyid[8] = { 0 };
sprintf_s(keyid, 8, "%x ", skid->data[i]);
strcat_s(value, 512, keyid);
}
cout << "User Key Identifier: " << value << endl;
}
void keyUse(X509* cert) {
//**用法
char use[512] = { 0 };
ASN1_BIT_STRING* lASN1UsageStr;
lASN1UsageStr = (ASN1_BIT_STRING *)X509_get_ext_d2i(cert, NID_key_usage, NULL, NULL);
if (lASN1UsageStr)
{
char temp[32] = { 0 };
unsigned short usage = lASN1UsageStr->data[0];
if (lASN1UsageStr->length > 1)
{
usage |= lASN1UsageStr->data[1] << 8;
}
sprintf_s(temp, 32, "(%x)", usage);
if (usage & KU_DIGITAL_SIGNATURE)
{
strcat_s(use, 512, "Digital Signature, ");
}
if (usage & KU_NON_REPUDIATION)
{
strcat_s(use, 512, "Non-Repudiation, ");
}
if (usage & KU_KEY_ENCIPHERMENT)
{
strcat_s(use, 512, "Key Encipherment, ");
}
if (usage & KU_DATA_ENCIPHERMENT)
{
strcat_s(use, 512, "Data Encipherment, ");
}
if (usage & KU_KEY_AGREEMENT)
{
strcat_s(use, 512, "Key Agreement, ");
}
if (usage & KU_KEY_CERT_SIGN)
{
strcat_s(use, 512, "Certificate Signature, ");
}
if (usage & KU_CRL_SIGN)
{
strcat_s(use, 512, "CRL Signature, ");
}
strcat_s(use, 512, temp);
}
else {
cout << "Null Usage" << endl;
}
cout << "Usage:" << use << endl;
}
void basicCon(X509* cert) {
//基本约束
int crit = 0;
char constraint[512] = { 0 };
BASIC_CONSTRAINTS *bcons = NULL;
bcons = (BASIC_CONSTRAINTS*)X509_get_ext_d2i(cert, NID_basic_constraints, &crit, NULL);
if (!bcons->ca)
{
strcat_s(constraint, 512, "Subject Type=End Entity; ");
strcat_s(constraint, 512, "Path Length Constraint=None");
}
else
{
char temp[128] = { 0 };
sprintf_s(temp, 128, "Path Length Constraint=%d", bcons->pathlen);
strcat_s(constraint, 512, "Subject Type=CA; ");
strcat_s(constraint, 512, temp);
}
BASIC_CONSTRAINTS_free(bcons);
cout << "Basic Constraint: " << constraint << endl;
}
void fingerPrint(X509* cert) {
//指纹
int SHA1LEN = 20;
char *buf = new char[SHA1LEN];
const EVP_MD *digest = EVP_sha1();
unsigned len;
int rc = X509_digest(cert, digest, (unsigned char*)buf, &len);
if (rc == 0 || len != SHA1LEN) {
cout << "fail" << endl;
}
char *strbuf = new char[2 * SHA1LEN + 1];
hex_encode((unsigned char*)buf, strbuf, SHA1LEN);
cout << "Fingerprint: "<< strbuf << endl;
}
void isValid(X509* cert) {
//证书状态,任何>= 1的值都被认为是CA证书,而0不是CA证书
int raw = X509_check_ca(cert);
if (raw >= 1) {
cout << "Valid Cert" << endl;
}
else {
cout << "Invalid Cert" << endl;
}
}
int main()
{
OpenSSL_add_all_algorithms();
char cert[MAX_LEGTH];
unsigned char ca_der[MAX_LEGTH];
unsigned long ca_der_len;
X509 *ca = NULL;
cout << "请输入证书地址" << endl;
char *path = new char[80];
cin.getline(path, 80);
/* x509初始化 */
sprintf(cert, path);
my_load_cert(ca_der, &ca_der_len, cert, MAX_LEGTH);
ca = der_to_x509(ca_der, ca_der_len);
version(ca);
serial(ca);
signAlgo(ca);
iss(ca);
validTime(ca);
sub(ca);
pubKey(ca);
subKeyHash(ca);
keyUse(ca);
basicCon(ca);
fingerPrint(ca);
return 0;
}
运行结果
心得体会
一开始觉得难点在于转格式,后来发现难点在于调用函数,对OpenSSL库函数的不了解对于调用解析造成困难。网上搜索到的解决方案并不适用于我,总显示“不允许使用不完整的类类型”,网上解释是头文件的定义没找到对应的cpp文件,我觉得可能是我安装或在vs中引入OpenSSL的过程有什么问题,因为OpenSSL包中app文件夹里的示例代码不报错,另外建立项目调用就有问题。
最后我放弃使用类指针调用返回属性值,改用get和set函数,对于想得到的属性值先输一个get_,然后vs会返回可得的所有函数,选中较相关的函数进行尝试,最后得到现在的逐项解析证书基本项的代码。
实验二
实验内容
利用 openssl 开源库(或其他开源实现),编写一个 c/s 应用要求客户端能输入任意消息,服务器端能将该消息显示,或者记录到文件中。
实验分析
基于OpenSSL的程序要遵循以下几个步骤:
-
OpenSSL初始化
OpenSSL_add_ssl_algorithms(); SSL_load_error_strings(); SSL_library_init();
-
选择会话协议
在利用OpenSSL开始SSL会话之前,需要为客户端和服务器制定本次会话采用的协议,目前能够使用的协议包括TLSv1.0、SSLv2、SSLv3、SSLv2/v3。客户端和服务器必须使用相互兼容的协议,否则SSL会话将无法正常进行。
-
创建会话环境
在OpenSSL中创建的SSL会话环境称为CTX,使用不同的协议会话,其环境也不一样的。申请SSL会话环境的OpenSSL函数是:
SSL_CTX *SSL_CTX_new(SSL_METHOD *method);
当SSL会话环境申请成功后,还要根据实际的需要设置CTX的属性,通常的设置是指定SSL握手阶段证书的验证方式和加载自己的证书。
制定证书验证方式的函数是: int SSL_CTX_set_verify(SSL_CTX *ctx,int mode,int(*verify_callback),int(X509_STORE_CTX *)); 为SSL会话环境加载CA证书的函数是: SSL_CTX_load_verify_location(SSL_CTX *ctx,const char *Cafile,const char *Capath); 为SSL会话加载用户证书的函数是: SSL_CTX_use_certificate_file(SSL_CTX *ctx, const char *file,int type); 为SSL会话加载用户私钥的函数是: SSL_CTX_use_PrivateKey_file(SSL_CTX *ctx,const char* file,int type); 在将证书和私钥加载到SSL会话环境之后,就可以调用下面的函数来验证私钥和证书是否相符 int SSL_CTX_check_private_key(SSL_CTX *ctx);
-
建立SSL套接字
SSL套接字是建立在普通的TCP套接字基础之上,在建立SSL套接字时可以使用下面的一些函数:
SSL *SSl_new(SSL_CTX *ctx); //申请一个SSL套接字 int SSL_set_fd(SSL *ssl,int fd);) //绑定读写套接字 int SSL_set_rfd(SSL *ssl,int fd); //绑定只读套接字 int SSL_set_wfd(SSL *ssl,int fd); //绑定只写套接字
-
完成SSL握手
在成功创建SSL套接字后,客户端应使用函数
SSL_connect( )
来完成握手过程:int SSL_connect(SSL *ssl);
而对服务器来讲,则应使用函数
SSL_ accept ( )
来完成握手过程:int SSL_accept(SSL *ssl);
握手过程完成之后,询问通信双方的证书信息是可选的
从SSL套接字中提取对方的证书信息,已被SSL验证过: X509 *SSL_get_peer_certificate(SSL *ssl); 得到证书所用者的名字: X509_NAME *X509_get_subject_name(X509 *a);
-
进行数据传输
当SSL握手完成之后,就可以进行安全的数据传输了,在数据传输阶段,需要使用
SSL_read( )
和SSL_write( )
来完成对套接字的读写操作:int SSL_read(SSL *ssl,void *buf,int num); int SSL_write(SSL *ssl,const void *buf,int num);
-
结束SSL通信
当客户端和服务器之间的数据通信完成之后,调用下面的函数来释放已经申请的SSL资源:
int SSL_shutdown(SSL *ssl); //关闭SSL套接字 void SSl_free(SSL *ssl); //释放SSL套接字 void SSL_CTX_free(SSL_CTX *ctx); //释放SSL会话环境
实验代码
server.cpp
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <winsock.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
using namespace std;
#define PORT 4433
extern "C"
{
#include <openssl/applink.c>
};
int main() {
cout << "SERVER" << endl;
OpenSSL_add_ssl_algorithms();
SSL_load_error_strings();
SSL_library_init();
//选择会话协议
const SSL_METHOD *meth;
meth = SSLv23_server_method();
//创建会话环境
SSL_CTX *ctx;
ctx = SSL_CTX_new(meth);
if ((ctx) == NULL)exit(-1);
//设置CTX的属性
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
SSL_CTX_load_verify_locations(ctx, "./certs/ca/ca.crt", NULL);
int ssl_server_cert = SSL_CTX_use_certificate_file(ctx, "./certs/server/server.crt", SSL_FILETYPE_PEM);
if (ssl_server_cert <= 0) {
ERR_print_errors_fp(stderr);
exit(-2);
}
int ssl_server_pri = SSL_CTX_use_PrivateKey_file(ctx, "./certs/server/server.key", SSL_FILETYPE_PEM);
if (ssl_server_pri <= 0)
{
ERR_print_errors_fp(stderr);
exit(-3);
}
int check = SSL_CTX_check_private_key(ctx);
if (!check) {
printf("Private key does not match the certificate public key\n");
exit(-4);
}
//开始TCP socket过程
WSAData wsa;
if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
{
cout << "WSAStartup error" << endl;
return 0;
}
cout << "Begin TCP socket..." << endl;
SOCKET listen_sd;
listen_sd = socket(AF_INET, SOCK_STREAM, 0);
if ((listen_sd) == INVALID_SOCKET) {
cout << "again" << endl;
int er = WSAGetLastError();
cout << er << endl;
exit(-2);
}
struct sockaddr_in sa_serv;
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(PORT);
int err;
err = bind(listen_sd, (struct sockaddr*)&sa_serv, sizeof(struct sockaddr_in));
if (err == -1) { perror("bind"); exit(-2); }
//接受TCP链接
err = listen(listen_sd, 10);
if (err == -1) { perror("listen"); exit(-2); }
cout << "Listening...." << endl;
cout << "Ready for Accept,Waitting..." << endl;
int client_len = sizeof(struct sockaddr_in);
struct sockaddr_in sa_cli;
int sd = accept(listen_sd, (struct sockaddr*) &sa_cli, &client_len);
if (sd == -1) { perror("accept"); exit(-2); }
cout << "Get the Client" << endl;
printf("Connection from %lx, port %x\n", sa_cli.sin_addr.s_addr, sa_cli.sin_port);
//TCP连接已建立,进行服务端的SSL过程
cout << "Begin server side SSL" << endl;
SSL* ssl;
ssl = SSL_new(ctx);
if (ssl == NULL)exit(-1);
SSL_set_fd(ssl, sd);
err = SSL_accept(ssl);
if (err == -1) {
cout << "Error:" << stderr << endl;
ERR_print_errors_fp(stderr);
exit(-3);
}
cout << "SSL_accept finished" << endl;
char buf[4096];
err = SSL_read(ssl, buf, sizeof(buf) - 1);
if (err == -1) { ERR_print_errors_fp(stderr); exit(-3); }
buf[err] = '\0';
printf("client:'%s'\n", buf);
printf("server:");
cout << buf << endl;
//关闭
int shut = SSL_shutdown(ssl);
SSL_free(ssl);
SSL_CTX_free(ctx);
return 0;
}
client.cpp
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <cstdlib>
#include <winsock.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/rand.h>
using namespace std;
#define PORT 4433
#define SERVER_ADDR "127.0.0.1"
extern "C"
{
#include <openssl/applink.c>
};
int main() {
cout << "CLIENT" << endl;
//初始化
OpenSSL_add_ssl_algorithms();
SSL_load_error_strings();
SSL_library_init();
//选择会话协议
const SSL_METHOD *meth;
meth = SSLv23_client_method();
//创建会话环境
SSL_CTX *ctx;
ctx = SSL_CTX_new(meth);
if ((ctx) == NULL) exit(-1);
//设置CTX的属性
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
SSL_CTX_load_verify_locations(ctx, "./certs/ca/ca.crt", NULL);
int ssl_user_cert = SSL_CTX_use_certificate_file(ctx, "./certs/client/client.crt", SSL_FILETYPE_PEM);
if (ssl_user_cert <= 0)
{
ERR_print_errors_fp(stderr);
exit(-2);
}
int ssl_user_pri = SSL_CTX_use_PrivateKey_file(ctx, "./certs/client/client.key", SSL_FILETYPE_PEM);
if (ssl_user_pri <= 0)
{
ERR_print_errors_fp(stderr);
exit(-3);
}
int check = SSL_CTX_check_private_key(ctx);
if (!check)
{
printf("Private key does not match the certificate public key\n");
exit(-4);
}
struct sockaddr_in sa;
memset(&sa, '\0', sizeof(sa));
sa.sin_family = AF_INET;
sa.sin_addr.s_addr = inet_addr(SERVER_ADDR);/* Server IP */
sa.sin_port = htons(PORT);/* Server Port number */
//初始化供进程调用的Winsock相关的dll
WSAData wsa;
if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
{
cout << "WSAStartup error" << endl;
return 0;
}
cout << "Begin TCP Socket..." << endl;
SOCKET sd;
sd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sd == INVALID_SOCKET) {
cout << "again" << endl;
int er = WSAGetLastError();
cout << er << endl;
exit(-2);
}
int err;
err = connect(sd, (struct sockaddr*)&sa, sizeof(struct sockaddr));
if (err == -1) { perror("connect"); exit(-2); }
cout << "Get the server" << endl;
//开始 SSL 握手过程
cout << "Begin SSL negotiation" << endl;
SSL* ssl = SSL_new(ctx);
if (ssl == NULL) exit(-1);
SSL_set_fd(ssl, sd);
err = SSL_connect(ssl);
if (err == -1) {
cout << "Error:" << stderr << endl;
ERR_print_errors_fp(stderr);
exit(-3);
}
cout << "Begin SSL data exchange" << endl;
cout << "client:" << "Hello world" << endl;
err = SSL_write(ssl, "Hello world", strlen("Hello World!"));
//关闭
int shut = SSL_shutdown(ssl);
SSL_free(ssl);
SSL_CTX_free(ctx);
system("pause");
return 0;
}
运行结果
心得体会
在同一个solution下建立两个project,先运行server再运行client。整体运行脉络很清楚,基本上可以简化到五步:初始化→创建会话→建立SSL握手→数据传输→结束通信。另外在windows下进行SSL连接之前,还要初始化供进程调用的Winsock相关的dll,否则connect会出错。
运行时要注意的就是在vs2017下,设server项目为启动项目,运行后对client项目进行调试启动新项目,这样就可以同时运行server和client进行SSL握手。
上一篇: 五国相王是哪五国?为什么不称天子
下一篇: 姑娘,你抽烟的样子一点也不酷