我在C中找到了一个非常好的SSL/TLS server-client example,我想把它改编成和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命令生成证书。
一切运行正常,消息发送正常。请忽略错误检查的缺乏,并原谅代码中的全部混乱,这只是为了学习目的。
有人能告诉我出什么问题了吗?你能分辨出我是否在犯重大错误,我正在努力学习吗?(例如,常规呼叫顺序,重大安全问题等)
谢谢!
发布于 2013-01-30 05:46:48
根据manual page (底部的示例代码非常具有说明性),对BIO_do_accept()的第一个调用将设置接受的BIO,其他什么也不做。只有第二个和所有后续调用才是接受连接的实际调用。这很好地说明了为什么OpenSSL永远不会赢得“最直观的应用程序接口设计”奖。
那么在你的代码中发生了什么呢?您只需在循环内调用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");
}
[...]https://stackoverflow.com/questions/14554514
复制相似问题