标签:openssl
1. OPENSSL简介
OpenSSL项目是一个协作开发一个健壮的,商业级的,全功能的,并且开放源代码工具包,它实现了安全套接字层(SSL v2/v3)和传输层安全(TLS v1)协议以及全强大的通用加密库。
2.使用SSL进行安全IO
使用SSL进行安全IO与原本的socketIO通信区别不大,只是增加了SSL部分内容.
下面说明SSL IO的基本过程:
客户端流程
① // SSL初始化
②//socket,connect,
③//ssl,建立SSL连接,SSL_connect(ssl)
④//SSL_write,SSL_read
⑤//关闭操作SSL_shutdown,SSL_free,close,SSL_CTX_free
服务端流程
① // SSL初始化
②//socket,bind,listen,accept
③//ssl,建立SSL连接,SSL_accept(ssl)
④//SSL_write,SSL_read
⑤//关闭操作SSL_shutdown,SSL_free,close,SSL_CTX_free
3.使用BIO进行安全IO
BIO是Openssl对IO类型的抽象封装,包括:内存、文件、日志、标准输入输出、socket(TCP/UDP)、加解密、摘要和ssl通道等。Openssl BIO通过回调函数为用户隐藏了底层实现细节。BIO的使用使得代码得到很大简化.
使用BIO进行安全IO的基本过程
①//SSL 初始化
②//加载可信认证书库(注意: 若不加载或加载失败,则证书无效,但仍可以继续连接(由客户端自己控制是否继续进行通信))
SSL_CTX_load_verify_locations(ctx, "TrustStore.pem", NULL)
③//建立连接
bio = BIO_new_ssl_connect(ctx);
/* Set the SSL_MODE_AUTO_RETRY flag */
BIO_get_ssl(bio, &ssl);
SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
/* Create and setup the connection */
BIO_set_conn_hostname(bio, "www.baidu.com:https");
④//BIO_write,BIO_read
⑤//关闭操作BIO_free_all,SSL_CTX_free
对比直接使用SSL进行IO与使用BIO进行通信,发现使用BIO更加简洁方便,建议更多的使用BIO.
4.操作SSL过程中的错误检测
OpenSSL 抛出了某种类型的错误。首先,需要得到错误代码本身; ERR_get_error 可以完成这项任务;
然后,需要将错误代码转换为错误字符串,它是一个指向由 SSL_load_error_strings 或 ERR_load_BIO_strings 加载到内存中的永久字符串的指针。 方法:ERR_reason_error_string,ERR_lib_error_string,ERR_func_error_string.
还可以将错误进行转储:ERR_print_errors_fp(FILE *);ERR_print_errors(BIO *);
代码如下:
a.client_SSL.cpp
int main(int argc, char * *argv) { int sockfd, len; struct sockaddr_in dest; char buffer[MAXBUF + 1]; SSL_CTX * ctx; SSL * ssl; /* SSL 库初始化*/ SSL_library_init(); /* 载入所有SSL 算法*/OpenSSL_add_all_algorithms(); /* 载入所有SSL 错误消息*/ SSL_load_error_strings(); /* 以SSL V2 和V3 标准兼容方式产生一个SSL_CTX ,即SSL Content Text */ ctx = SSL_CTX_new(SSLv23_client_method()); if (ctx == NULL) { ERR_print_errors_fp(stdout); exit(1); } /* 创建一个socket 用于tcp 通信*/ if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("Socket"); exit(errno); } printf("socket created\n"); /* 初始化服务器端(对方)的地址和端口信息*/ bzero(&dest, sizeof(dest)); dest.sin_family = AF_INET; //设置连接的端口 dest.sin_port = htons(12345); //设置连接的IP地址 char *addr = "127.0.0.1"; //115.239.210.27百度 //char *addr2 = "115.239.210.27"; if (inet_aton(addr, (struct in_addr *) &dest.sin_addr.s_addr) == 0) { perror(argv[0]); exit(errno); } printf("address created\n"); /* 连接服务器*/ if (connect(sockfd, (struct sockaddr *) &dest, sizeof(dest)) != 0) { perror("Connect "); exit(errno); } printf("server connected\n"); /* 基于ctx 产生一个新的SSL */ ssl = SSL_new(ctx); /* 将新连接的socket 加入到SSL */ SSL_set_fd(ssl, sockfd); //---------------------------------------------------------- // /* Load the trust store */ // if (!SSL_CTX_load_verify_locations(ctx, "TrustStore.pem", NULL)) { // fprintf(stderr, "Error loading trust store\n"); // ERR_print_errors_fp(stderr); // SSL_CTX_free(ctx); // return 0; // } //---------------------------------------------------------- /* 建立SSL 连接*/ if (SSL_connect(ssl) == -1) { ERR_print_errors_fp(stderr); } else { printf("Connected with %s encryption\n", SSL_get_cipher(ssl)); ShowCerts(ssl); } /* 接收对方发过来的消息,最多接收MAXBUF 个字节*/ bzero(buffer, MAXBUF + 1); /* 接收服务器来的消息*/ len = SSL_read(ssl, buffer, MAXBUF); if (len > 0) { printf("接收消息成功:'%s',共%d 个字节的数据\n", buffer, len); } else { printf("消息接收失败!错误代码是%d,错误信息是'%s'\n", errno, strerror(errno)); goto finish; } bzero(buffer, MAXBUF + 1); strcpy(buffer, "from client->server"); /* 发消息给服务器*/ len = SSL_write(ssl, buffer, strlen(buffer)); if (len < 0) { printf("消息'%s'发送失败!错误代码是%d,错误信息是'%s'\n", buffer, errno, strerror(errno)); } else { printf("消息'%s'发送成功,共发送了%d 个字节!\n", buffer, len); } finish: /* 关闭连接*/ SSL_shutdown(ssl); SSL_free(ssl); close(sockfd); SSL_CTX_free(ctx); return 0; }
int main(int argc, char * *argv) { int sockfd, new_fd; socklen_t len; struct sockaddr_in my_addr, their_addr; unsigned int myport, lisnum; char buf[MAXBUF + 1]; SSL_CTX * ctx; //指定监听端口 myport = 12345; //最大客户端连接数 lisnum = 10; /* SSL 库初始化*/ SSL_library_init(); /* 载入所有SSL 算法*/OpenSSL_add_all_algorithms(); /* 载入所有SSL 错误消息*/ SSL_load_error_strings(); /* 以SSL V2 和V3 标准兼容方式产生一个SSL_CTX ,即SSL Content Text */ ctx = SSL_CTX_new(SSLv23_server_method()); /* 也可以用SSLv2_server_method() 或SSLv3_server_method() 单独表示V2 或V3标准 */ if (ctx == NULL) { ERR_print_errors_fp(stdout); exit(1); } /* 载入用户的数字证书, 此证书用来发送给客户端。证书里包含有公钥*/ if (SSL_CTX_use_certificate_file(ctx, "/home/shuyan/workspace/Openssl_Server/files/cacert.pem", SSL_FILETYPE_PEM) <= 0) { ERR_print_errors_fp(stdout); exit(1); } /* 载入用户私钥*/ if (SSL_CTX_use_PrivateKey_file(ctx, "/home/shuyan/workspace/Openssl_Server/files/privkey.pem", SSL_FILETYPE_PEM) <= 0) { ERR_print_errors_fp(stdout); exit(1); } /* 检查用户私钥是否正确*/ if (!SSL_CTX_check_private_key(ctx)) { ERR_print_errors_fp(stdout); exit(1); } /* 开启一个socket 监听*/ if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) { perror("socket"); exit(1); } else { printf("socket created\n"); } bzero(&my_addr, sizeof(my_addr)); my_addr.sin_family = PF_INET; my_addr.sin_port = htons(myport); my_addr.sin_addr.s_addr = INADDR_ANY; if (bind(sockfd, (struct sockaddr *) &my_addr, sizeof(struct sockaddr)) == -1) { perror("bind"); exit(1); } else { printf("binded\n"); } if (listen(sockfd, lisnum) == -1) { perror("listen"); exit(1); } else { printf("begin listen\n"); } while (1) { SSL * ssl; len = sizeof(struct sockaddr); /* 等待客户端连上来*/ if ((new_fd = accept(sockfd, (struct sockaddr *) &their_addr, &len)) == -1) { perror("accept"); exit(errno); } else { printf("server: got connection from %s, port %d, socket %d\n", inet_ntoa(their_addr.sin_addr), ntohs(their_addr.sin_port), new_fd); } /* 基于ctx 产生一个新的SSL */ ssl = SSL_new(ctx); /* 将连接用户的socket 加入到SSL */ SSL_set_fd(ssl, new_fd); /* 建立SSL 连接*/ if (SSL_accept(ssl) == -1) { perror("accept"); close(new_fd); break; } /* 开始处理每个新连接上的数据收发*/ bzero(buf, MAXBUF + 1); strcpy(buf, "server->client"); /* 发消息给客户端*/ len = SSL_write(ssl, buf, strlen(buf)); if (len <= 0) { printf("消息'%s'发送失败!错误代码是%d,错误信息是'%s'\n", buf, errno, strerror(errno)); goto finish; } else { printf("消息'%s'发送成功,共发送了%d 个字节!\n", buf, len); } bzero(buf, MAXBUF + 1); /* 接收客户端的消息*/ len = SSL_read(ssl, buf, MAXBUF); if (len > 0) { printf("接收消息成功:'%s',共%d 个字节的数据\n", buf, len); } else { printf("消息接收失败!错误代码是%d,错误信息是'%s'\n", errno, strerror(errno)); ERR_reason_error_string(ERR_get_error()); } /* 处理每个新连接上的数据收发结束*/ finish: /* 关闭SSL 连接*/ SSL_shutdown(ssl); /* 释放SSL */ SSL_free(ssl); /* 关闭socket */ close(new_fd); } /* 关闭监听的socket */ close(sockfd); /* 释放CTX */ SSL_CTX_free(ctx); return 0; }
int main() { BIO * bio; SSL * ssl; SSL_CTX * ctx; int p; char * request = "GET / HTTP/1.1\x0D\x0AHost: www.baidu.com\x0D\x0A\x43onnection: Close\x0D\x0A\x0D\x0A"; char r[1024]; /* SSL 库初始化*/ SSL_library_init(); /* 载入所有SSL 算法*/ OpenSSL_add_all_algorithms(); /* 载入所有SSL 错误消息*/ SSL_load_error_strings(); /* Set up the library */ ERR_load_BIO_strings(); /* Set up the SSL context */ ctx = SSL_CTX_new(SSLv23_client_method()); /* Load the trust store */ if (!SSL_CTX_load_verify_locations(ctx, "TrustStore.pem", NULL)) { fprintf(stderr, "Error loading trust store\n"); ERR_print_errors_fp(stderr); SSL_CTX_free(ctx); return 0; } /* Setup the connection */ bio = BIO_new_ssl_connect(ctx); bio = BIO_new(BIO_s_socket()); /* Set the SSL_MODE_AUTO_RETRY flag */ BIO_get_ssl(bio, &ssl); SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY); /* Create and setup the connection */ BIO_set_conn_hostname(bio, "www.baidu.com:https"); if (BIO_do_connect(bio) <= 0) { fprintf(stderr, "Error attempting to connect\n"); ERR_print_errors_fp(stderr); BIO_free_all(bio); SSL_CTX_free(ctx); return 0; } /* Check the certificate */ if (SSL_get_verify_result(ssl) != X509_V_OK) { fprintf(stderr, "Certificate verification error: %i\n", SSL_get_verify_result(ssl)); BIO_free_all(bio); SSL_CTX_free(ctx); return 0; } ShowCerts(ssl); /* Send the request */ BIO_write(bio, request, strlen(request)); /* Read in the response */ for (;;) { p = BIO_read(bio, r, 1023); if (p <= 0) break; r[p] = 0; printf("%s", r); } /* Close the connection and free the context */ BIO_free_all(bio); SSL_CTX_free(ctx); return 0; }
标签:openssl
原文地址:http://blog.csdn.net/leokelly001/article/details/44306301