锐英源软件
第一信赖

精通

英语

开源

擅长

开发

培训

胸怀四海 

第一信赖

当前位置:锐英源 / 开源技术 / openssl开源 / 使用OpenSSL的内存BIO
服务方向
人工智能数据处理
人工智能培训
kaldi数据准备
小语种语音识别
语音识别标注
语音识别系统
语音识别转文字
kaldi开发技术服务
软件开发
运动控制卡上位机
机械加工软件
软件开发培训
Java 安卓移动开发
VC++
C#软件
汇编和破解
驱动开发
联系方式
固话:0371-63888850
手机:138-0381-0136
Q Q:396806883
微信:ryysoft

使用OpenSSL的内存BIO


For a couple of projects I've been using SSL/TLS to secure data transport, but everytime when I start to use the openSSL library, it's tough to find the correct documentation. The library is poorly documented but when you have some experience with it I'm sure it all makes sense. When you don't have the experience it's hard to find the correct information and see the bigger picture.

I'm far from an expect on SSL/TLS or the openSSL library. Though after having it used for a couple of projects I'm getting more familiar and got a basic understanding. I'll try to explain how I use it. Please contact me at info@this_domain if you see any errors/mistakes.

对于一些项目,我一直在使用SSL / TLS来保护数据传输,但每当我开始使用openSSL库时,很难找到正确的文档。库的文档很少,但是当你有一些经验时,我确信这一切都是有道理的。当您没有经验时,很难找到正确的信息并看到更大的视野。

我对SSL / TLS或openSSL库的期望远非如此。虽然在将它用于几个项目后,我变得越来越熟悉并得到了基本的了解。我会试着解释一下我是如何使用它的。如果您发现任何错误/错误,请通过info@this_domain与我联系。

How TLS works

The Transport Layer Security protocol is used to secure your data that you e.g. want to receive and send to a client over a network. For example the Twitter API is only accessible over HTTPS. This means that the HTTP protocol is encrypted using TLS. TLS is based on asymmetric and symetric keys.

  • asymetric keys use a public and private key to encrypt and decrypt data. When data is encrypted using a private key only users with the public key can decrypt the data. When data is encrypted with the public key, only users with the private key can decrypt the data. Encrypting or decrypting data with asymetric keys takes more time then symetric keys. (RSA)

  • when using symmetric keys one can encrypt and decrypt data with the same key. symmetric keys are often used to encrypt large amount of data because it's easier to compute then asymetric keys. (DES, 3DES, AES, RC4, RC2)

TLS uses asymetric keys to exchange a symmetric key. Once the symmetric key is available between sender and receiver they are used to encrypt/decrypt the large partion of application data.

传输层安全协议来保护你的数据,比如,你要接收和发送到在网络上客户端的数据。例如,Twitter API只能通过HTTPS访问。这意味着使用TLS加密HTTP协议。TLS基于非对称 和对称密钥。

  • 非对称密钥使用公钥和私钥来加密和解密数据。当使用私钥加密数据时,只有具有公钥的用户才能解密数据。使用公钥加密数据时,只有拥有私钥的用户才能解密数据。使用不对称密钥加密或解密数据比symetric密钥花费更多时间。(RSA)

  • 当使用对称密钥时,可以使用相同的密钥加密和解密数据。对称密钥通常用于加密大量数据,因为它更容易计算非对称密钥。(DES,3DES,AES,RC4,RC2)

TLS使用不对称密钥来交换对称密钥。一旦发送器和接收器之间的对称密钥可用,它们就用于加密/解密大部分应用程序数据。

TLS FLOW

TLS is a protocol which means that the flow of data is predefined. In most cases the data flows as shown in the snippet below. Important to know is the handshake, which kicks off the communication between a sender and receiver (e.g. browser and webserver). The CertificateRequest is an important aspect of TLS. A Certificate is used to check if the keys are created by an authority which is trusted. Also a certificate contains the public key. Also note that the handshake hops 4 times between client and server.TLS是一种协议,意味着数据流是预定义的。在大多数情况下,数据流如下面的代码段所示。重要的是要知道握手,它启动发送者和接收者(例如浏览器和网络服务器)之间的通信。这CertificateRequest是TLS的一个重要方面。证书用于检查密钥是否由受信任的机构创建。证书也包含公钥。另请注意,握手在客户端和服务器之间跳转4次。

客户端											服务器
------ ------

(1)
ClientHello --------> |
ServerHello |
证书* |
ServerKeyExchange * |
CertificateRequest * |
(2)|
<-------- ServerHelloDone |
证书* |
ClientKeyExchange | --- HANDSHAKE
CertificateVerify * |
[ChangeCipherSpec] |
(3)|
完成--------> |
[ChangeCipherSpec] |
(4)|
<--------完成|

应用数据<------->应用数据

BIOs

This part will describe how I've used openSSL in a experimental application to secure data. In the code which will follow below I'm similating a client and server in one application. The client starts the handshake by sending a ClientHello to the server.

OpenSSL uses the a concept of BIOs which is an input/output abstraction allowing us to e.g. use data from a file, memory or network in a similar way. Because I want to manage
my own memory I'm using a memory bio. These BIOs always tend to confuse me because of the BIO_read(), BIO_write(), SSL_read(), SSL_write() functions and the BIO objects you use with them. To make it a bit easier and less confusing with all these read(), write() functions I name my BIOs not "read" and "write" BIO, but input bio and output bio. Somehow this makes more sense to me. For example, I use an input BIO to store data that comes from the network and then I use SSL_read() to retrieve the unencrypted data.

这部分将描述我如何在实验性应用程序中使用openSSL来保护数据。在下面的代码中,我在一个应用程序中模拟客户端和服务器。客户端通过向服务器发送ClientHello来启动握手。

OpenSSL使用BIO的概念,它是一种输入/输出抽象,允许我们以类似的方式使用来自文件,内存或网络的数据。因为我想管理
自己的内存,所以我正在使用内存BIO。由于BIO_read(), BIO_write(), SSL_read(), SSL_write()函数和你使用的BIO对象,这些BIO 总是会让我感到困惑。为了使它更容易一点,并与所有这些容易混淆read(), write() 的功能,我的命名我的BIO不带“读”和“写” BIO,而是input bio和output bio。不知怎的,这对我来说更有意义。例如,我使用输入BIO来存储来自网络的数据,然后SSL_read()用于检索未加密的数据。

  • SSL_read() read unencrypted data which is stored in the input BIO.
  • SSL_write() write unencrypted data into the output BIO.
  • BIO_write() write encrypted data into the input BIO.
  • BIO_read() read encrypted data from the output BIO.
  • SSL_read()读取存储在输入BIO中的未加密数据。
  • SSL_write()将未加密的数据写入输出BIO。
  • BIO_write()将加密数据写入输入BIO。
  • BIO_read()从输出BIO读取加密数据。

So when do you use what function?那你什么时候使用什么功能?

  • Use BIO_write() to store encrypted data you receive from e.g. a tcp/udp socket. Once you've written to an input BIO, you use SSL_read() to get the unencrypted data, but only after the handshake is ready.
  • Use BIO_read() to check if there is any data in the output BIO. The output BIO will be filled by openSSL when it's handling the handshake or when you call SSL_write(). When there is data in your output BIO, use BIO_read to get the data and send it to e.g. a client. Use BIO_ctrl_pending() to check how many bytes there are stored in the output bio. See krx_ssl_handle_traffic() in the code listing at the bottom of this post.
  • 使用BIO_write()存储如TCP / UDP套接字接收加密的数据。一旦您写入输入BIO,您就可以使用SSL_read()获取未加密的数据,但只有在握手准备好之后。
  • 使用BIO_read()以检查是否有输出BIO的任何数据。当处理握手或打电话时,输出BIO将由openSSL填充SSL_write()。当输出BIO中有数据时,使用BIO_read获取数据并将其发送到例如客户端。使用BIO_ctrl_pending()检查输出bio中存储了多少字节。请参阅krx_ssl_handle_traffic()本文底部的代码清单。

Using OpenSSL

Before you can use anything of the OpenSSL library you need to initialize it:初始化

SSL_library_init();
SSL_load_error_strings();
ERR_load_BIO_strings();
OpenSSL_add_all_algorithms();

When you're ready you can shutdown the library by using:准备好后,您可以使用以下命令关闭库

ERR_remove_state(0);
ENGINE_cleanup();
CONF_modules_unload(1);
ERR_free_strings();
EVP_cleanup();
sk_SSL_COMP_free(SSL_COMP_get_compression_methods());
CRYPTO_cleanup_all_ex_data();

To use openSSL you create a SSL_CTX object which is a context that keeps track of shared information like the certificate to use, private key to use etc.. See the krx_ssl_ctx_init() function in the souce code at the bottom of this document.要使用openSSL,您需要创建一个SSL_CTX对象,该对象是跟踪共享信息的上下文,例如要使用的证书,要使用的私钥等。请参阅krx_ssl_ctx_init()本文档底部的源代码中的函数。

Once you have a SSL_CTX object you can create a SSL object for a particulair connection. You can create many SSLobjects for each connection you want to handle and share one SSL_CTX object.

When you're using openSSL as a server you need to make sure that the SSL object knows that it should act as a server which can be done by calling SSL_set_accept_state(ssl). When SSL is used as a server it will wait for a ClientHellomessage and makes sure that a ServerHello, CertificateRequest etc.. is send back to the client. When you want your SSL object to act like a client you need to call SSL_set_connect_state(ssl) and call start filling your output BIO by calling SSL_do_handshake().一旦有了SSL_CTX对象,就可SSL以为特定连接创建一个对象。您可以为要处理的每个连接创建许多SSL对象并共享一个SSL_CTX对象。

当您使用openSSL作为服务器时,您需要确保SSL对象知道它应该充当可以通过调用完成的服务器 SSL_set_accept_state(ssl)。当SSL用作服务器时,它将等待 ClientHello消息并确保将ServerHello,CertificateRequest等发送回客户端。当您希望SSL对象像客户端一样工作时,您需要通过调用来调用SSL_set_connect_state(ssl)并调用start来填充输出BIO SSL_do_handshake()。

As long as the handshake is not yet ready, you have to make sure that the function SSL_do_handshake() is called everytime the input or output BIOs change. For example, when the server starts, and a clients connects, you call BIO_write() and fill the input BIO with data you got on your socket, then call SSL_do_handshake(ssl) as long as the handshake isn't ready yet. You can check this by using SSL_is_init_finished(ssl). After the handshake has been completed you can start filling the BIOs by calling SSL_read and SSL_write with your application data that you want to secure.只要握手尚未就绪,您必须确保 每次输入或输出BIO发生变化时都会调用该SSL_do_handshake()函数。例如,当服务器启动并且客户端连接时,您调用BIO_write()并使用您在套接字上获得的数据填充输入BIO,然后只要握手尚未就绪就调用SSL_do_handshake(ssl)。您可以使用SSL_is_init_finished(ssl)以查看此内容。握手完成后,您可以通过使用您想要保护的应用程序数据调用SSL_read和SSL_write来开始填充BIO。

Update: tracking state更新:跟踪状态

Some people have told me you don't need to track state of SSL. Though in some situations it's good to know when the handshake (initialisation) has finished. OpenSSL provides a function SSL_is_init_finished() to check this. As long as SSL_is_init_finished() returns false you should handle the handshake, which is a 4 step procedure as described above (see the flow example). When SSL_is_init_finished() returns false you need to call SSL_do_handshake(). OpenSSL keeps state internally and calling SSL_do_handshake() should be enough to allow openSSL to perform the handshake. But, it's important that you check the return value of the call to SSL_do_handshake() because it tells you want functions you need to call next, especially when using memory bios. As described in the manual: "if the underlying BIO is non-blocking, SSL_do_handshake() will also return when the underlying BIO could not satisfy the needs of SSL_do_handshake() to continue the handshake. In this case a call to SSL_get_error() with the return value of SSL_do_handshake() will yield SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE." So A very verbose way of handling this could be something like:

有些人告诉我你不需要跟踪SSL的状态。虽然在某些情况下,最好知道握手(初始化)何时结束。OpenSSL提供了SSL_is_init_finished()检查此功能的功能。只要SSL_is_init_finished()返回false,就应该处理握手,这是一个如上所述的4步过程(参见流程示例)。当SSL_is_init_finished()返回false需要调用 SSL_do_handshake() 。OpenSSL在内部保持状态,并且调用SSL_do_handshake()应该足以允许openSSL执行握手。但是,检查调用的返回值非常重要,SSL_do_handshake()因为它告诉您需要接下来调用的函数,尤其是在使用内存BIOS时。如手册中所述:“如果底层BIO是非阻塞的,当底层BIO无法满足SSL_do_handshake()继续握手的需求时,SSL_do_handshake()也会返回。在这种情况下,调用SSL_get_error()并返回值为SSL_do_handshake( )将产生SSL_ERROR_WANT_READ或SSL_ERROR_WANT_WRITE。“ 所以处理这个的非常冗长的方式可能是这样的:

if (!SSL_is_init_finished(ssl)) {

/* NOT INITIALISED */

r = SSL_do_handshake(ssl);
if (r < 0) {

r = SSL_get_error(ssl, r);
if (SSL_ERROR_WANT_READ == r) {
pending = BIO_ctrl_pending(out_bio);
if (pending > 0) {
read = BIO_read(out_bio, buffer, POLY_SSL_BUFFER_SIZE);
if (read > 0) {
listener->onSslBufferEncryptedData(buffer, read);
}
}
}
}
}
else {

/* SSL IS INITIALISED */

pending = BIO_ctrl_pending(out_bio);
if (pending > 0) {
read = BIO_read(out_bio, buffer, POLY_SSL_BUFFER_SIZE);
if (read > 0) {
listener->onSslBufferEncryptedData(buffer, read);
}
}

pending = BIO_ctrl_pending(in_bio);
if (pending > 0) {
while((read = SSL_read(ssl, buffer, POLY_SSL_BUFFER_SIZE)) > 0) {
listener->onSslBufferDecryptedData(buffer, read);
}
}
}
}

If you implement your own socket functions you need to make sure that the SSL handshake has been finished before your start enrypting your application data. A function to check if you need to read more data from your socket is:如果您实现自己的套接字函数,则需要确保在开始加密应用程序数据之前已完成SSL握手。检查是否需要从套接字读取更多数据的函数是:

int SslBuffer::wantsMoreData() {

int r = 0;

if (SSL_is_init_finished(ssl)) {
return -1;
}

r = SSL_do_handshake(ssl);
if (r < 0) {
r = SSL_get_error(ssl, r);
if (SSL_ERROR_WANT_READ == r) {
return 0;
}
}

return -1;
}

Example code

The following code listing shows how you can use OpenSSL with Memory BIOs.以下代码清单显示了如何将OpenSSL与Memory BIO一起使用。

/* 

Create server/client self-signed certificate/key (self signed, DONT ADD PASSWORD)

openssl req -x509 -newkey rsa:2048 -days 3650 -nodes -keyout client-key.pem -out client-cert.pem
openssl req -x509 -newkey rsa:2048 -days 3650 -nodes -keyout server-key.pem -out server-cert.pem

*/

#include <stdio.h>
#include <stdlib.h>

#include <openssl/err.h>
#include <openssl/dh.h>
#include <openssl/ssl.h>
#include <openssl/conf.h>
#include <openssl/engine.h>

/* SSL debug */
#define SSL_WHERE_INFO(ssl, w, flag, msg) { \
if(w & flag) { \
printf("+ %s: ", name); \
printf("%20.20s", msg); \
printf(" - %30.30s ", SSL_state_string_long(ssl)); \
printf(" - %5.10s ", SSL_state_string(ssl)); \
printf("\n"); \
} \
}

typedef void(*info_callback)();

typedef struct {
SSL_CTX* ctx; /* main ssl context */
SSL* ssl; /* the SSL* which represents a "connection" */
BIO* in_bio; /* we use memory read bios */
BIO* out_bio; /* we use memory write bios */
char name[512];
} krx;

void krx_begin(); /* initialize SSL */
void krx_end(); /* shutdown SSL */
int krx_ssl_ctx_init(krx* k, const char* keyname); /* initialize the SSL_CTX */
int krx_ssl_init(krx* k, int isserver, info_callback cb); /* init the SSL* (the "connection"). we use the `isserver` to tell SSL that it should either use the server or client protocol */
int krx_ssl_shutdown(krx* k); /* cleanup SSL allocated mem */
int krx_ssl_verify_peer(int ok, X509_STORE_CTX* ctx); /* we set the SSL_VERIFY_PEER option on the SSL_CTX, so that the server will request the client certificate. We can use the certificate to get/verify the fingerprint */
int krx_ssl_handle_traffic(krx* from, krx* to);

/* some debug info */
void krx_ssl_server_info_callback(const SSL* ssl, int where, int ret); /* purely for debug purposes; logs server info. */
void krx_ssl_client_info_callback(const SSL* ssl, int where, int ret); /* client info callback */
void krx_ssl_info_callback(const SSL* ssl, int where, int ret, const char* name); /* generic info callback */

int main() {

/* startup SSL */
krx_begin();

/* create client/server objects */
krx server;
krx client;

/* init server. */
if(krx_ssl_ctx_init(&server, "server") < 0) {
exit(EXIT_FAILURE);
}
if(krx_ssl_init(&server, 1, krx_ssl_server_info_callback) < 0) {
exit(EXIT_FAILURE);
}

printf("+ Initialized server.\n");

/* init client. */
if(krx_ssl_ctx_init(&client, "client") < 0) {
exit(EXIT_FAILURE);
}
if(krx_ssl_init(&client, 0, krx_ssl_client_info_callback) < 0) {
exit(EXIT_FAILURE);
}

printf("+ Initialized client.\n");

/* kickoff handshake; initiated by client (e.g. browser) */
SSL_do_handshake(client.ssl);
krx_ssl_handle_traffic(&client, &server);
krx_ssl_handle_traffic(&server, &client);
krx_ssl_handle_traffic(&client, &server);
krx_ssl_handle_traffic(&server, &client);

/* encrypt some data and send it to the client */
char buf[521] = { 0 } ;
sprintf(buf, "%s", "Hello world");
SSL_write(server.ssl, buf, sizeof(buf));
krx_ssl_handle_traffic(&server, &client);

krx_ssl_shutdown(&server);
krx_ssl_shutdown(&client);

krx_end();
return EXIT_SUCCESS;
}

void krx_begin() {
SSL_library_init();
SSL_load_error_strings();
ERR_load_BIO_strings();
OpenSSL_add_all_algorithms();
}

void krx_end() {
ERR_remove_state(0);
ENGINE_cleanup();
CONF_modules_unload(1);
ERR_free_strings();
EVP_cleanup();
sk_SSL_COMP_free(SSL_COMP_get_compression_methods());
CRYPTO_cleanup_all_ex_data();
}

int krx_ssl_ctx_init(krx* k, const char* keyname) {

int r = 0;

/* create a new context using DTLS */
k->ctx = SSL_CTX_new(DTLSv1_method());
if(!k->ctx) {
printf("Error: cannot create SSL_CTX.\n");
ERR_print_errors_fp(stderr);
return -1;
}

/* set our supported ciphers */
r = SSL_CTX_set_cipher_list(k->ctx, "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH");
if(r != 1) {
printf("Error: cannot set the cipher list.\n");
ERR_print_errors_fp(stderr);
return -2;
}

/* the client doesn't have to send it's certificate */
SSL_CTX_set_verify(k->ctx, SSL_VERIFY_PEER, krx_ssl_verify_peer);

/* enable srtp */
r = SSL_CTX_set_tlsext_use_srtp(k->ctx, "SRTP_AES128_CM_SHA1_80");
if(r != 0) {
printf("Error: cannot setup srtp.\n");
ERR_print_errors_fp(stderr);
return -3;
}

/* load key and certificate */
char certfile[1024];
char keyfile[1024];
sprintf(certfile, "./%s-cert.pem", keyname);
sprintf(keyfile, "./%s-key.pem", keyname);

/* certificate file; contains also the public key */
r = SSL_CTX_use_certificate_file(k->ctx, certfile, SSL_FILETYPE_PEM);
if(r != 1) {
printf("Error: cannot load certificate file.\n");
ERR_print_errors_fp(stderr);
return -4;
}

/* load private key */
r = SSL_CTX_use_PrivateKey_file(k->ctx, keyfile, SSL_FILETYPE_PEM);
if(r != 1) {
printf("Error: cannot load private key file.\n");
ERR_print_errors_fp(stderr);
return -5;
}

/* check if the private key is valid */
r = SSL_CTX_check_private_key(k->ctx);
if(r != 1) {
printf("Error: checking the private key failed. \n");
ERR_print_errors_fp(stderr);
return -6;
}

sprintf(k->name, "+ %s", keyname);

return 0;
}

int krx_ssl_verify_peer(int ok, X509_STORE_CTX* ctx) {
return 1;
}

/* this sets up the SSL* */
int krx_ssl_init(krx* k, int isserver, info_callback cb) {

/* create SSL* */
k->ssl = SSL_new(k->ctx);
if(!k->ssl) {
printf("Error: cannot create new SSL*.\n");
return -1;
}

/* info callback */
SSL_set_info_callback(k->ssl, cb);

/* bios */
k->in_bio = BIO_new(BIO_s_mem());
if(k->in_bio == NULL) {
printf("Error: cannot allocate read bio.\n");
return -2;
}

BIO_set_mem_eof_return(k->in_bio, -1); /* see: https://www.openssl.org/docs/crypto/BIO_s_mem.html */

k->out_bio = BIO_new(BIO_s_mem());
if(k->out_bio == NULL) {
printf("Error: cannot allocate write bio.\n");
return -3;
}

BIO_set_mem_eof_return(k->out_bio, -1); /* see: https://www.openssl.org/docs/crypto/BIO_s_mem.html */

SSL_set_bio(k->ssl, k->in_bio, k->out_bio);

/* either use the server or client part of the protocol */
if(isserver == 1) {
SSL_set_accept_state(k->ssl);
}
else {
SSL_set_connect_state(k->ssl);
}

return 0;
}

void krx_ssl_server_info_callback(const SSL* ssl, int where, int ret) {
krx_ssl_info_callback(ssl, where, ret, "server");
}
void krx_ssl_client_info_callback(const SSL* ssl, int where, int ret) {
krx_ssl_info_callback(ssl, where, ret, "client");
}

void krx_ssl_info_callback(const SSL* ssl, int where, int ret, const char* name) {

if(ret == 0) {
printf("-- krx_ssl_info_callback: error occured.\n");
return;
}

SSL_WHERE_INFO(ssl, where, SSL_CB_LOOP, "LOOP");
SSL_WHERE_INFO(ssl, where, SSL_CB_HANDSHAKE_START, "HANDSHAKE START");
SSL_WHERE_INFO(ssl, where, SSL_CB_HANDSHAKE_DONE, "HANDSHAKE DONE");
}

int krx_ssl_handle_traffic(krx* from, krx* to) {

// Did SSL write something into the out buffer
char outbuf[4096];
int written = 0;
int read = 0;
int pending = BIO_ctrl_pending(from->out_bio);

if(pending > 0) {
read = BIO_read(from->out_bio, outbuf, sizeof(outbuf));
}
printf("%s Pending %d, and read: %d\n", from->name, pending, read);

if(read > 0) {
written = BIO_write(to->in_bio, outbuf, read);
}

if(written > 0) {
if(!SSL_is_init_finished(to->ssl)) {
SSL_do_handshake(to->ssl);
}
else {
read = SSL_read(to->ssl, outbuf, sizeof(outbuf));
printf("%s read: %s\n", to->name, outbuf);
}
}

return 0;
}

int krx_ssl_shutdown(krx* k) {
if(!k) {
return -1;
}

if(k->ctx) {
SSL_CTX_free(k->ctx);
k->ctx = NULL;
}

if(k->ssl) {
SSL_free(k->ssl);
k->ssl = NULL;
}

return 0;
} /* we use memory read bios */ BIO* out_bio; /* we use memory write bios */ char name[512]; } krx;   void krx_begin(); /* initialize SSL */ void krx_end(); /* shutdown SSL */ int krx_ssl_ctx_init(krx* k, const char* keyname); /* initialize the SSL_CTX */ int krx_ssl_init(krx* k, int isserver, info_callback cb); /* init the SSL* (the "connection"). we use the `isserver` to tell SSL that it should either use the server or client protocol */ int krx_ssl_shutdown(krx* k); /* cleanup SSL allocated mem */ int krx_ssl_verify_peer(int ok, X509_STORE_CTX* ctx); /* we set the SSL_VERIFY_PEER option on the SSL_CTX, so that the server will request the client certificate. We can use the certificate to get/verify the fingerprint */ int krx_ssl_handle_traffic(krx* from, krx* to);   /* some debug info */ void krx_ssl_server_info_callback(const SSL* ssl, int where, int ret); /* purely for debug purposes; logs server info. */ void krx_ssl_client_info_callback(const SSL* ssl, int where, int ret); /* client info callback */ void krx_ssl_info_callback(const SSL* ssl, int where, int ret, const char* name); /* generic info callback */   int main() {   /* startup SSL */ krx_begin();   /* create client/server objects */ krx server; krx client;   /* init server. */ if(krx_ssl_ctx_init(&server, "server") < 0) { exit(EXIT_FAILURE); } if(krx_ssl_init(&server, 1, krx_ssl_server_info_callback) < 0) { exit(EXIT_FAILURE); }   printf("+ Initialized server.\n");   /* init client. */ if(krx_ssl_ctx_init(&client, "client") < 0) { exit(EXIT_FAILURE); } if(krx_ssl_init(&client, 0, krx_ssl_client_info_callback) < 0) { exit(EXIT_FAILURE); }   printf("+ Initialized client.\n");   /* kickoff handshake; initiated by client (e.g. browser) */ SSL_do_handshake(client.ssl); krx_ssl_handle_traffic(&client, &server); krx_ssl_handle_traffic(&server, &client); krx_ssl_handle_traffic(&client, &server); krx_ssl_handle_traffic(&server, &client);   /* encrypt some data and send it to the client */ char buf[521] = { 0 } ; sprintf(buf, "%s", "Hello world"); SSL_write(server.ssl, buf, sizeof(buf)); krx_ssl_handle_traffic(&server, &client);   krx_ssl_shutdown(&server); krx_ssl_shutdown(&client);   krx_end(); return EXIT_SUCCESS; }   void krx_begin() { SSL_library_init(); SSL_load_error_strings(); ERR_load_BIO_strings(); OpenSSL_add_all_algorithms(); }   void krx_end() { ERR_remove_state(0); ENGINE_cleanup(); CONF_modules_unload(1); ERR_free_strings(); EVP_cleanup(); sk_SSL_COMP_free(SSL_COMP_get_compression_methods()); CRYPTO_cleanup_all_ex_data(); }   int krx_ssl_ctx_init(krx* k, const char* keyname) {   int r = 0;   /* create a new context using DTLS */ k->ctx = SSL_CTX_new(DTLSv1_method()); if(!k->ctx) { printf("Error: cannot create SSL_CTX.\n"); ERR_print_errors_fp(stderr); return -1; }   /* set our supported ciphers */ r = SSL_CTX_set_cipher_list(k->ctx, "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH"); if(r != 1) { printf("Error: cannot set the cipher list.\n"); ERR_print_errors_fp(stderr); return -2; }   /* the client doesn't have to send it's certificate */ SSL_CTX_set_verify(k->ctx, SSL_VERIFY_PEER, krx_ssl_verify_peer);   /* enable srtp */ r = SSL_CTX_set_tlsext_use_srtp(k->ctx, "SRTP_AES128_CM_SHA1_80"); if(r != 0) { printf("Error: cannot setup srtp.\n"); ERR_print_errors_fp(stderr); return -3; }   /* load key and certificate */ char certfile[1024]; char keyfile[1024]; sprintf(certfile, "./%s-cert.pem", keyname); sprintf(keyfile, "./%s-key.pem", keyname);   /* certificate file; contains also the public key */ r = SSL_CTX_use_certificate_file(k->ctx, certfile, SSL_FILETYPE_PEM); if(r != 1) { printf("Error: cannot load certificate file.\n"); ERR_print_errors_fp(stderr); return -4; }   /* load private key */ r = SSL_CTX_use_PrivateKey_file(k->ctx, keyfile, SSL_FILETYPE_PEM); if(r != 1) { printf("Error: cannot load private key file.\n"); ERR_print_errors_fp(stderr); return -5; }   /* check if the private key is valid */ r = SSL_CTX_check_private_key(k->ctx); if(r != 1) { printf("Error: checking the private key failed. \n"); ERR_print_errors_fp(stderr); return -6; }   sprintf(k->name, "+ %s", keyname);   return 0; }   int krx_ssl_verify_peer(int ok, X509_STORE_CTX* ctx) { return 1; }   /* this sets up the SSL* */ int krx_ssl_init(krx* k, int isserver, info_callback cb) {   /* create SSL* */ k->ssl = SSL_new(k->ctx); if(!k->ssl) { printf("Error: cannot create new SSL*.\n"); return -1; }   /* info callback */ SSL_set_info_callback(k->ssl, cb);   /* bios */ k->in_bio = BIO_new(BIO_s_mem()); if(k->in_bio == NULL) { printf("Error: cannot allocate read bio.\n"); return -2; }   BIO_set_mem_eof_return(k->in_bio, -1); /* see: https://www.openssl.org/docs/crypto/BIO_s_mem.html */   k->out_bio = BIO_new(BIO_s_mem()); if(k->out_bio == NULL) { printf("Error: cannot allocate write bio.\n"); return -3; }   BIO_set_mem_eof_return(k->out_bio, -1); /* see: https://www.openssl.org/docs/crypto/BIO_s_mem.html */   SSL_set_bio(k->ssl, k->in_bio, k->out_bio);   /* either use the server or client part of the protocol */ if(isserver == 1) { SSL_set_accept_state(k->ssl); } else { SSL_set_connect_state(k->ssl); }   return 0; }   void krx_ssl_server_info_callback(const SSL* ssl, int where, int ret) { krx_ssl_info_callback(ssl, where, ret, "server"); } void krx_ssl_client_info_callback(const SSL* ssl, int where, int ret) { krx_ssl_info_callback(ssl, where, ret, "client"); }   void krx_ssl_info_callback(const SSL* ssl, int where, int ret, const char* name) {   if(ret == 0) { printf("-- krx_ssl_info_callback: error occured.\n"); return; }   SSL_WHERE_INFO(ssl, where, SSL_CB_LOOP, "LOOP"); SSL_WHERE_INFO(ssl, where, SSL_CB_HANDSHAKE_START, "HANDSHAKE START"); SSL_WHERE_INFO(ssl, where, SSL_CB_HANDSHAKE_DONE, "HANDSHAKE DONE"); }   int krx_ssl_handle_traffic(krx* from, krx* to) {   // Did SSL write something into the out buffer char outbuf[4096]; int written = 0; int read = 0; int pending = BIO_ctrl_pending(from->out_bio);   if(pending > 0) { read = BIO_read(from->out_bio, outbuf, sizeof(outbuf)); } printf("%s Pending %d, and read: %d\n", from->name, pending, read);   if(read > 0) { written = BIO_write(to->in_bio, outbuf, read); }   if(written > 0) { if(!SSL_is_init_finished(to->ssl)) { SSL_do_handshake(to->ssl); } else { read = SSL_read(to->ssl, outbuf, sizeof(outbuf)); printf("%s read: %s\n", to->name, outbuf); } }   return 0; }   int krx_ssl_shutdown(krx* k) { if(!k) { return -1; }   if(k->ctx) { SSL_CTX_free(k->ctx); k->ctx = NULL; }   if(k->ssl) { SSL_free(k->ssl); k->ssl = NULL; }   return 0; }
友情链接
版权所有 Copyright(c)2004-2021 锐英源软件
公司注册号:410105000449586 豫ICP备08007559号 最佳分辨率 1024*768
地址:郑州大学北校区院(文化路97号院)内