我将尝试使用openssl进行相互身份验证的TLS。
但是,如下面的输出结果所示,客户端可以接收并输出服务器证书,但是服务器尚未收到客户端证书。
我的工作细节如下。
(1)生成服务器密钥和证书。
$ openssl genrsa -des3 -out server.key 2048
$ openssl req-新-key server.key -out server.csr
$ cp server.key server.key.origin
$ openssl rsa -in server.key.origin -out server.key
$ openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt
(2)生成客户端密钥和证书。 $ openssl genrsa -des3 -out client.key 2048
$ openssl req-新-key client.key -out client.csr
$ cp client.key client.key.origin
$ openssl rsa -in client.key.origin -out client.key
$ openssl x509 -req -days 365 -in client.csr -signkey client.key -out client.crt
// Server.c
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <memory.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <openssl/rsa.h>
#include <openssl/crypto.h>
#include <openssl/x509.h>
#include <openssl/pem.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#define HOME "./"
#define CERTF HOME "server.crt"
#define KEYF HOME "server.key"
#define CHK_NULL(x) if((x) == NULL) exit(1);
#define CHK_ERR(err, s) if((err) == -1) { perror(s); exit(1); }
#define CHK_SSL(err) if((err) == -1) { ERR_print_errors_fp(stderr); exit(2); }
int main(void) {
int err;
int listen_sd;
int sd;
struct sockaddr_in sa_serv;
struct sockaddr_in sa_cli;
size_t client_len;
SSL_CTX *ctx;
SSL *ssl;
X509 *client_cert;
char *str;
char buf[4096];
SSL_METHOD *meth;
SSL_load_error_strings();
SSLeay_add_ssl_algorithms();
meth = TLSv1_2_server_method();
ctx = SSL_CTX_new(meth);
if(!ctx) {
ERR_print_errors_fp(stderr);
exit(2);
}
if(SSL_CTX_use_certificate_file(ctx, CERTF, SSL_FILETYPE_PEM) <= 0) {
ERR_print_errors_fp(stderr);
exit(3);
}
if(SSL_CTX_use_PrivateKey_file(ctx, KEYF, SSL_FILETYPE_PEM) <= 0) {
ERR_print_errors_fp(stderr);
exit(4);
}
if(!SSL_CTX_check_private_key(ctx)) {
fprintf(stderr, "Private key does not match the certificate public keyn");
exit(5);
}
listen_sd = socket(AF_INET, SOCK_STREAM, 0);
CHK_ERR(listen_sd, "socket");
memset(&sa_serv, 0x00, sizeof(sa_serv));
sa_serv.sin_family = AF_INET;
sa_serv.sin_addr.s_addr = INADDR_ANY;
sa_serv.sin_port = htons(1111);
err = bind(listen_sd, (struct sockaddr*)&sa_serv, sizeof(sa_serv));
CHK_ERR(err, "bind");
err = listen(listen_sd, 5);
CHK_ERR(err, "listen");
client_len = sizeof(sa_cli);
sd = accept(listen_sd, (struct sockaddr*)&sa_cli, &client_len);
CHK_ERR(sd, "accept");
close(listen_sd);
ssl = SSL_new(ctx);
CHK_NULL(ssl);
SSL_set_fd(ssl, sd);
// to request client's certificate
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
err = SSL_accept(ssl);
CHK_SSL(err);
printf("SSL connection using %s \n", SSL_get_cipher(ssl));
client_cert = SSL_get_peer_certificate(ssl);
if(client_cert != NULL) {
printf("Client certificate: \n");
str = X509_NAME_oneline(X509_get_subject_name(client_cert), 0, 0);
CHK_NULL(str);
printf("\t subject: %s\n", str);
OPENSSL_free(str);
str = X509_NAME_oneline(X509_get_issuer_name(client_cert), 0, 0);
CHK_NULL(str);
printf("\t issuer: %s\n", str);
OPENSSL_free(str);
X509_free(client_cert);
} else {
printf("Client does not have certificate. \n");
}
err = SSL_read(ssl, buf, sizeof(buf)-1);
CHK_SSL(err);
buf[err] = 0x00;
printf("Got %d chars: %s \n", err, buf);
err = SSL_write(ssl, "I hear you/", strlen("I hear you."));
CHK_SSL(err);
close(sd);
SSL_free(ssl);
SSL_CTX_free(ctx);
return(0);
}
// client.c
#include <stdio.h>
#include <memory.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <openssl/crypto.h>
#include <openssl/x509.h>
#include <openssl/pem.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#define CHK_NULL(x) if((x) == NULL) exit(1);
#define CHK_ERR(err, s) if((err) == -1) { perror(s); exit(1); }
#define CHK_SSL(err) if((err) == -1) { ERR_print_errors_fp(stderr); exit(2); }
int main(void) {
int err;
int sd;
struct sockaddr_in sa;
SSL_CTX *ctx;
SSL *ssl;
X509 *server_cert;
char *str;
char buf[4096];
SSL_METHOD *meth;
SSL_load_error_strings();
SSLeay_add_ssl_algorithms();
meth = TLSv1_2_client_method();
ctx = SSL_CTX_new(meth);
CHK_NULL(ctx);
if(SSL_CTX_use_certificate_file(ctx, "./client.crt", SSL_FILETYPE_PEM) <= 0) {
ERR_print_errors_fp(stderr);
exit(3);
}
if(SSL_CTX_use_PrivateKey_file(ctx, "./client.key", SSL_FILETYPE_PEM) <= 0) {
ERR_print_errors_fp(stderr);
exit(4);
}
if(!SSL_CTX_check_private_key(ctx)) {
fprintf(stderr, "Private key does not match the certificate public keyn");
exit(5);
}
CHK_SSL(err);
sd = socket(AF_INET, SOCK_STREAM, 0);
CHK_ERR(sd, "socket");
memset(&sa, 0x00, sizeof(sa));
sa.sin_family = AF_INET;
sa.sin_addr.s_addr = inet_addr("127.0.0.1");
sa.sin_port = htons(1111);
err = connect(sd, (struct sockaddr*)&sa, sizeof(sa));
CHK_ERR(err, "connect");
ssl = SSL_new(ctx);
CHK_NULL(ssl);
SSL_set_fd(ssl, sd);
err = SSL_connect(ssl);
CHK_NULL(err);
printf("SSL connection using %s \n", SSL_get_cipher(ssl));
server_cert = SSL_get_peer_certificate(ssl);
CHK_NULL(server_cert);
printf("Server certificate: \n");
str = X509_NAME_oneline(X509_get_subject_name(server_cert), 0, 0);
CHK_NULL(str);
printf("\t subject: %s \n", str);
OPENSSL_free(str);
str = X509_NAME_oneline(X509_get_issuer_name(server_cert), 0, 0);
CHK_NULL(str);
printf("\t issuer: %s \n", str);
OPENSSL_free(str);
X509_free(server_cert);
err = SSL_write(ssl, "Hello World!", strlen("Hello World!"));
CHK_SSL(err);
err = SSL_read(ssl, buf, sizeof(buf)-1);
CHK_SSL(err);
buf[err] = 0x0;
printf("Got %d chars: %s \n", err, buf);
SSL_shutdown(ssl);
close(sd);
SSL_free(ssl);
SSL_CTX_free(ctx);
return 0;
}
下面是输出结果。
(1)服务器
$ ./服务器
使用ECDHE-RSA-AES256-GCM-SHA384的SSL连接
客户端没有证书。
有12个字符:世界您好!
(2)客户
$ ./client
使用ECDHE-RSA-AES256-GCM-SHA384的SSL连接
服务器证书:
主题:/ C = IN / ST = WB / L =加尔各答/O=TEST-INFO-CLIENTA/OU=IT/CN=clienta.com/emailAddress=aaa@aaa.com
发布者:/ C = IN / ST = WB / L =加尔各答/O=TEST-INFO-CLIENTA/OU=IT/CN=clienta.com/emailAddress=aaa@aaa.com
有11个字符:我听到了你/
============================================= ========
我不知道为什么服务器的输出显示“客户端没有证书”。
尽管我在server.c中添加了“ SSL_CTX_set_verify(ctx,SSL_VERIFY_PEER,NULL)”,但服务器未收到客户端的证书。
如果有人知道这一点,请回复。
答案 0 :(得分:1)
如何避免证书验证失败错误?
如果要创建“ rootCA”,然后颁发服务器证书 和客户端证书使用相同的“ rootCA”,则需要确保 通过以下方式将“ rootCA”证书文件作为CA添加到OpenSSL: API SSL_CTX_load_verify_locations。即使仅使用自签名证书,我们仍然需要使用API SSL_CTX_load_verify_locations添加它们。此操作必须在SSL_new之前完成。
是否可以将证书的验证与客户端分开?现在的代码是SSL_accept(ssl);似乎证书验证已在函数中完成。换句话说,我想要的是服务器收到客户端证书后,它首先打印出证书信息,然后对其进行验证。
对收到的证书的验证进行一些控制 从客户端,您可以设置证书验证回调。您 将在此回调中获取从客户端收到的证书, 您出于调试目的进行打印,然后继续 验证。请参考API SSL_CTX_set_verify。尽管您似乎在设置它,但您传递的是NULL以进行回调。如果您将有效的回调函数作为参数传递,则您将在回调中收到证书。