2

我在 C 中找到了一个非常好的SSL/TLS 服务器-客户端示例,我想对其进行调整以与 BIO 库一起使用。我几乎成功了,除了运行服务器时出现的一个错误:

$ ./ssl-server 68671:error:140950D3:SSL routines:SSL3_READ_N:read bio not set:/SourceCache/OpenSSL098/OpenSSL098-35.1/src/ssl/s3_pkt.c:203:

gcc -o ssl-server SSL-Server.c -lssl -lcrypto -Wall用来编译服务器:

//SSL-Server.c
#include <errno.h>
#include <unistd.h>
#include <stdlib.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
#define PORT "2013"

SSL_CTX* InitServerCTX(void)
{
    SSL_METHOD *method;
    SSL_CTX *ctx;

    OpenSSL_add_all_algorithms();
    SSL_load_error_strings();
    method = SSLv3_server_method();
    ctx = SSL_CTX_new(method);

    if( ctx == NULL )
    {
        ERR_print_errors_fp(stderr);
        abort();
    }

    return ctx;
}

void LoadCertificates(SSL_CTX* ctx, char* CertFile, char* KeyFile)
{
    if( SSL_CTX_use_certificate_file(ctx, CertFile, SSL_FILETYPE_PEM) <= 0 )
    {
        ERR_print_errors_fp(stderr);
        abort();
    }

    if( SSL_CTX_use_PrivateKey_file(ctx, KeyFile, SSL_FILETYPE_PEM) <= 0 )
    {
        ERR_print_errors_fp(stderr);
        abort();
    }

    if( !SSL_CTX_check_private_key(ctx) )
    {
        fprintf(stderr, "Private key does not match the public certificate\n");
        abort();
    }
}

void ShowCerts(SSL* ssl)
{
    X509 *cert;
    char *line;

    cert = SSL_get_peer_certificate(ssl);
    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");
    }
}

void Servlet(SSL* ssl)
{
    char buf[1024];
    int sd, bytes;

    if( SSL_accept(ssl) == FAIL )
    {
        ERR_print_errors_fp(stderr);
    }
    else
    {
        ShowCerts(ssl);
        bytes = SSL_read(ssl, buf, sizeof(buf));

        if( bytes > 0 )
        {
            buf[bytes] = 0;
            printf("Client msg: \"%s\"\n", buf);
            SSL_write(ssl, "back message", strlen("back message"));
        }
        else
        {
            ERR_print_errors_fp(stderr);
        }
    }

    sd = SSL_get_fd(ssl);
    SSL_free(ssl);
    close(sd);
}

int main(int count, char *strings[])
{
    SSL_CTX *ctx;
    BIO *acc, *client;

    SSL_library_init();

    ctx = InitServerCTX();
    LoadCertificates(ctx, "mycert.pem", "mycert.pem");

    acc = BIO_new_accept(PORT);
    if(!acc)
    {
        printf("Error creating server socket");
    }
    while(1)
    {
        if(BIO_do_accept(acc) <= 0)
        {
            printf("Error binding server socket");
        }

        SSL *ssl;
        client = BIO_pop(acc);
        if(!(ssl = SSL_new(ctx)))
        {
            printf("Error creating SSL context");
        }
        SSL_set_bio(ssl, client, client);

            // Here should be created threads
        Servlet(ssl); 
    }

    SSL_CTX_free(ctx);
}

gcc -o ssl-client SSL-Client.c -lssl -lcrypto -Wall用来编译客户端:

//SSL-Client.c
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <resolv.h>
#include <netdb.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/bio.h>

#define FAIL -1
#define SERVER "localhost"
#define PORT "2013"

SSL_CTX* InitCTX(void)
{
    SSL_METHOD *method;
    SSL_CTX *ctx;

    OpenSSL_add_all_algorithms();
    SSL_load_error_strings();
    method = SSLv3_client_method();
    ctx = SSL_CTX_new(method);

    if( ctx == NULL )
    {
        ERR_print_errors_fp(stderr);
        abort();
    }

    return ctx;
}

void ShowCerts(SSL* ssl)
{
    X509 *cert;
    char *line;

    cert = SSL_get_peer_certificate(ssl);
    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");
    }
}

int main(int count, char *strings[])
{
    SSL_CTX *ctx;
    SSL *ssl;
    BIO *conn;
    char buf[1024];
    int bytes;

    SSL_library_init();
    ctx = InitCTX();

    conn = BIO_new_connect(SERVER ":" PORT);
    if(!conn)
    {
        printf("Error creating connection BIO");
    }
    if(BIO_do_connect(conn) <= 0)
    {
        printf("Error connecting to remote machine");
    }

    ssl = SSL_new(ctx);
    SSL_set_bio(ssl, conn, conn);

    if( SSL_connect(ssl) <= 0 )
    {
        printf("Error connecting SSL object");
    }
    else
    {
        printf("Connected!");
        ShowCerts(ssl);
        SSL_write(ssl, "ana are mere", strlen("ana are mere") );

        bytes = SSL_read(ssl, buf, sizeof(buf));
        printf("%s\n", buf);

        SSL_free(ssl);
    }

    SSL_CTX_free(ctx);

    return 0;
}

我正在使用openssl req -x509 -nodes -days 365 -newkey rsa:1024 -keyout mycert.pem -out mycert.pem命令生成证书。

一切正常,消息发送正常。请忽略缺少错误检查并原谅代码中的全部混乱,这只是为了学习目的。

谁能指出我出了什么问题?你能告诉我是否犯了重大错误,我正在努力学习吗?(例如例行呼叫顺序、重大安全问题等)

谢谢!

4

1 回答 1

3

根据手册页(底部的示例代码非常具有说明性),第一次调用BIO_do_accept()将设置接受 BIO 并且不执行任何其他操作。只有第二个和所有后续调用将是接受连接的实际调用。这很好地说明了为什么 OpenSSL 永远不会赢得“最直观的 API 设计”奖。

那么你的代码会发生什么?您只能BIO_do_accept()在循环内调用。第一次通过循环,它将设置 BIO 并立即返回。您的代码调用Servlet()不存在的连接,但SSL_accept()失败,返回您看到的错误。返回后Servlet(),您的代码将愉快地循环到BIO_do_accept()这次阻塞的第二次调用,等待第一次连接,一切都按预期工作。

要解决此问题,您需要BIO_do_accept()在循环之前调用一次,如下所示(使用您破坏的错误处理风格以保持一致性 - 您确实需要修复您的错误处理!):

[...]
acc = BIO_new_accept(PORT);
if(!acc)
{
    printf("Error creating server socket");
}
/* first call is to set up accept BIO */
if(BIO_do_accept(acc) <= 0)
{
    printf("Error calling BIO_do_accept() the first time");
}
while(1)
{
    if(BIO_do_accept(acc) <= 0)
    {
        printf("Error binding server socket");
    }
[...]
于 2013-01-29T21:46:48.093 回答