我在我的现有代码上使用OpenSSL集成来实现SSL安全性。我的客户端代码是用C++编写的,运行在Windows操作系统上,而后端代码、服务器代码则运行在AIX上。
证书是在AIX上按照以下命令创建的,客户端(即Windows)正在使用创建的客户端证书和密钥:
//创建CA颁发机构的命令
1.openssl genrsa-des3-out密钥/RootCA_test.key 2048
1.openssl req-new-x509天360密钥密钥/RootCA_test.key-out证书/RootCA_test.crt
- //创建服务器证书的命令*
1.openssl genrsa-out密钥/server_test.key 2048
1.openssl req-new-key Key/server_est.key-out CSR/server_est.csr
1.openssl ca-day 360-in csr/服务器_est.csr-out证书/server_test.crt-key文件密钥/RootCA_test.key-cert证书/RootCA_test.crt
- //创建客户端证书的命令*
1.openssl genrsa-out密钥/CLIENT_Test.Key 2048
1.openssl请求-新密钥密钥/CLIENT_TEST.KEY-OUT CSR/CLIENT_TEST.csr
1.openssl ca-day 360-in csr/CLIENT_TEST.csr-OUT证书/CLIENT_Test.crt-KEYFILE密钥/RootCA_Test.key-cert证书/RootCA_est.crt
集成后的代码如下:
客户端代码-在Windows上
# include "stdafx.h"
#include <string>
#include <stdio.h>
#include <openssl/ssl.h> // SSL and SSL_CTX for SSL connections
#include <openssl/err.h> // Error reporting
SOCKET s;
SSL_CTX *ctx;
SSL *ssl;
int OpenConnection(const char *hostname, int port)
{ int sd;
struct hostent *host;
struct sockaddr_in addr;
if ( (host = gethostbyname(hostname)) == NULL )
{
perror(hostname);
abort();
}
sd = socket(AF_INET, SOCK_STREAM,IPPROTO_TCP);
memset(&addr,0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = *(long*)(host->h_addr);
if ( connect(sd, (struct sockaddr*)&addr, sizeof(addr)) != 0 )
{
close(sd);
perror(hostname);
abort();
}
return sd;
}
BOOL InitOpenSSL()//OPENSSL
{
//set default locations for trusted CA certificates
CString sslCrtFilePath = "C:\\Program Files\\cv\\certificates\\client_test.crt";
CString sslKeyFilePath = "C:\\Program Files\\cv\\certificates\\client_test.key";
CString sslRootFilePath = "C:\\Program Files\\cv\\certificates\\RootCA_test.crt";
int server;
char buf[1024];
int bytes;
char hostname[]="20.17.127.235";
int portnum = 4005;
SSL_library_init();
SSL_METHOD *method;
OpenSSL_add_all_algorithms(); /* Load cryptos, et.al. */
SSL_load_error_strings(); /* Bring in and register error messages */
method = SSLv23_client_method();//SSLv23_method(); /* Create new client-method instance */
ctx = SSL_CTX_new(method); /* Create new context */
if ( ctx == NULL )
{
abort();
}
if (!(LoadCertificates(sslRootFilePath.GetBuffer(sslRootFilePath.GetLength()),sslCrtFilePath.GetBuffer(sslCrtFilePath.GetLength()), sslKeyFilePath.GetBuffer(sslKeyFilePath.GetLength()))))
{
s = INVALID_SOCKET;
cout << "Failed to load certificate file and key file.";
return false;
}
ctx = SSL_CTX_new(method); /* Create new context */
ssl = SSL_new(ctx); /* create new SSL connection state */
if ( ctx == NULL )
{
ERR_print_errors_fp(stderr);
s = INVALID_SOCKET;
cout << "Error in creating SSL_CTX object.";
return FALSE;
}
server = OpenConnection(hostname, portnum);
SSL_set_fd(ssl, server); /* attach the socket descriptor */
int n = SSL_connect(ssl);
if ( n != 1 ) /* perform the connection */
{
int rc = SSL_get_error(ssl,n);
cout << "Error in SSL_connect";
exit(1);
}
else
{ char *msg = "Hello! I am Client.Who are you ?\n";
cout << "Connected with " << SSL_get_cipher(ssl) << " encryption" ;
SSL_write(ssl, msg, strlen(msg)); /* encrypt & send message */
bytes = SSL_read(ssl, buf, sizeof(buf)); /* get reply & decrypt */
buf[bytes] = 0;
cout << buf;
SSL_free(ssl); /* release connection state */
}
close(server); /* close socket */
SSL_CTX_free(ctx); /* release context */
return 0;
}
bool LoadCertificates(char* sslRootFilePath,char* CertFile, char* KeyFile)//OPENSSL
{
if(!SSL_CTX_load_verify_locations(ctx,sslRootFilePath , NULL))
{
/* Handle error here */
s = INVALID_SOCKET;
cout << "Failed to load root CA certificate.";
return FALSE;
}
/*Set cipher list*/
if (SSL_CTX_set_cipher_list(ctx,CIPHER_LIST) <= 0)
{
cout << "Error setting the cipher list.";
}
if ( SSL_CTX_use_certificate_file(ctx, CertFile, SSL_FILETYPE_PEM ) <= 0 )
{
cout << "Failed in setting the local certificate from CertFile.";
return false;
}
/* set the private key from KeyFile (may be the same as CertFile) */
if ( SSL_CTX_use_PrivateKey_file (ctx, KeyFile , SSL_FILETYPE_PEM) != 1 ) //SSL_FILETYPE_PEM
{
cout << "Failed in setting the private key from KeyFile.";
return false;
}
/* verify private key */
if ( !SSL_CTX_check_private_key(ctx) )
{
cout << ("Failed in verifying private key.", MB_TOPMOST | MB_SETFOREGROUND|MB_OK);
return false;
}
}
int main()
{
BOOL st = InitOpenSSL();
return 0;
}
AIX上的服务器代码
Ssl_ctxctx;sslssl;
string root,key,cert;
int OpenListener(int port)
{
cout << "OpenListener() Start" << endl ;
int sd;
struct sockaddr_in addr;
int portno = 4005;
sd = socket(AF_INET, SOCK_STREAM, 0);
memset(&addr,0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(portno);
addr.sin_addr.s_addr = INADDR_ANY;
if ( bind(sd, (struct sockaddr*)&addr, sizeof(addr)) != 0 )
{
cout << "can't bind port" << endl;
abort();
}
if ( listen(sd, 10) != 0 )
{
cout << "Can't configure listening port" << endl;
abort();
}
cout << "Port : " << addr.sin_port << " Address : " << addr.sin_addr.s_addr << endl;
cout << "OpenListener() End" << endl ;
return sd;
}
void Servlet(SSL* ssl)
{
cout << "Servlet() Start" << endl;
char buf[1024];
char reply[1024];
int sd, bytes;
if ( SSL_accept(ssl) != 1 ) /* do SSL-protocol accept */
ERR_print_errors_fp(stderr);
else
{
cout << "SSL_accept() executed" << endl;
ShowCerts(); /* get any certificates */
bytes = SSL_read(ssl, buf, sizeof(buf)); /* get request */
if ( bytes > 0 )
{
buf[bytes] = 0;
cout << "Client msg: " << buf << endl;
SSL_write(ssl, reply, strlen(reply)); /* send reply */
}
else
{
cout << " Bytes returned by SSL_read() is 0" << endl;
ERR_print_errors_fp(stderr);
}
}
sd = SSL_get_fd(ssl); /* get socket connection */
SSL_free(ssl); /* release SSL state */
close(sd); /* close connection */
cout << "Servlet() End" << endl;
}
void ShowCerts()
{
cout << "ShowCerts() Start " << endl;
X509 *cert;
char *line;
cout << "SSL_get_peer_certificate Initiated " << endl;
cert = SSL_get_peer_certificate(ssl); /* Get certificates (if available) */
if ( cert != NULL )
{
cout << "Server certificates: " << endl;
line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
cout << "Subject: " << line << endl;
free(line);
line = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
cout << "Issuer: " << line << endl;
free(line);
X509_free(cert);
}
else
cout << "SSL_get_peer_certificate() returns NULL " << endl;
cout << "ShowCerts() End " << endl;
}
bool InitOpenSSL()
{
cout << "InitOpenSSL() Starts" << endl;
SSL_library_init();
SSL_METHOD *method;
OpenSSL_add_all_algorithms(); /* Load cryptos, et.al. */
SSL_load_error_strings(); /* Bring in and register error messages */
method = const_cast <SSL_METHOD *> (SSLv23_server_method()); /* Create new server-method instance */
ctx = SSL_CTX_new(method); /* Create new context */
if ( ctx == NULL )
{
ERR_print_errors_fp(stderr);
cout << "Error in creating SSL_CTX object." << endl;
return false;
}
//set default locations for trusted CA certificates
cout << "Server Certificate Used : " << cert << endl;
cout << "Server Key Used : " << key << endl;
if (!(LoadCertificates(const_cast<char *> (cert.c_str()), const_cast <char*> (key.c_str()))) )
{
cout << "Failed to load certificate file and key file." << endl;
return false;
}
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
SSL_CTX_set_verify_depth(ctx, 4);
if(!SSL_CTX_load_verify_locations(ctx,root.c_str() , NULL) )
cout << "SSL_CTX_load_verify_locations failed." << endl;
else
cout << "SSL_CTX_load_verify_locations passed." << endl;
int server = OpenListener(4005); /* create server socket */
cout << "OpenListener() returns : " << server << endl;
while (1)
{
cout << "In While(1) Loop " << endl;
struct sockaddr_in addr;
socklen_t len = sizeof(addr);
cout << "::accept() called, get ready!" << endl ;
int client = ::accept(server, (struct sockaddr*)&addr, &len); /* accept connection as usual */
cout << "::accept() returns " << client << endl;
//printf("Connection: %s:%d\n",inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
cout << "Connection " << inet_ntoa(addr.sin_addr) << ntohs(addr.sin_port) << endl;
ssl = SSL_new(ctx); /* get new SSL state with context */
SSL_set_fd(ssl, client); /* set connection socket to SSL state */
Servlet(ssl); /* service connection */
}
close(server); /* close server socket */
SSL_CTX_free(ctx); /* release context */
cout << "InitOpenSSL() Ends" << endl;
}
bool LoadCertificates(char* CertFile, char* KeyFile)
{
cout << "LoadCertificates() Start" << endl;
bool rc = true;
/* set the local certificate from CertFile */
if ( SSL_CTX_use_certificate_file(ctx, CertFile, SSL_FILETYPE_PEM) <= 0 )
{
cout << "Failed to get the certificate file" << endl;
rc = false;
}
/* set the private key from KeyFile (may be the same as CertFile) */
if ( SSL_CTX_use_PrivateKey_file(ctx, KeyFile, SSL_FILETYPE_PEM) <= 0 )
{
cout << "Failed to get the private key file" << endl;
rc = false;
}
/* verify private key */
if ( !SSL_CTX_check_private_key(ctx) )
{
cout << "Private key does not match the public certificate" << endl;
rc = false;
}
cout << "LoadCertificates() End" << endl;
return rc;
}
int main()
{
cout << "Root path:" << endl;
cin >> root;
cout << "Key path:" << endl;
cin >> key;
cout << "Cert path:" << endl;
cin >> cert;
InitOpenSSL();
return 0;
}
在AIX上运行服务器,然后在Windows上运行客户端,输出如下所示:
Root path:
/tmp/Certificates/RootCA_test.crt
Key path:
/tmp/Keys/server_test.key
Cert path:
/tmp/Certificates/server_test.crt
InitOpenSSL() Starts
Server Certificate Used : /tmp/Certificates/server_test.crt
Server Key Used : /tmp/Keys/server_test.key
LoadCertificates() Start
LoadCertificates() End
SSL_CTX_load_verify_locations passed for /tmp/Certificates/RootCA_test.crt
OpenListener() Start
Port : 4005 Address : 0
OpenListener() End
OpenListener() returns : 3
In While(1) Loop
::accept() called, get ready!
::accept() returns 4
Connection xx.xx.xx.xxxxx
Servlet() Start
**1152921504606846944:error:140890C7:SSL routines:SSL3_GET_CLIENT_CERTIFICATE:peer did not return a certificate:s3_srvr.c:3281:**
Servlet() End
In While(1) Loop
::accept() called, get ready!
请帮助我解决此错误:1152921504606846944:错误:140890C7:ssl例程:SSL3_GET_CLIENT_CERTIFICATE:Peer未返回证书:s3_srvr.c:3281:
我尝试了各种链接,也用谷歌搜索了一下,但似乎没有一个答案对此有效!
我创建证书和密钥的方式可以吗?
在上面给出的客户端/服务器代码片段中,是否需要添加或删除任何代码?因为有很多不同的方法来做同样的事情!
一个有趣的结论是,当我在AIX服务器上运行OpenSSL命令时,它似乎建立了一个连接:
my_server_07: openssl s_server -cert /tmp/Certificates/server_test.crt -key /tmp/Keys/server_test.key -port 4005
Using default temp DH parameters
Using default temp ECDH parameters
ACCEPT
-----BEGIN SSL SESSION PARAMETERS-----
MFUCAQECAgMDBALAMAQABDDNSf27vf0Jg6GLr+Z7DP/DasT0+dCDRprYoQ4kMMIk
bwtptVYQUgIpkLk/SRDhdhqhBgIEVkB9J6IEAgIBLKQGBAQAAAAB
-----END SSL SESSION PARAMETERS-----
Shared ciphers:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-DSS-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA256:DHE-RSA-AES256-SHA:DHE-DSS-AES256-SHA:DHE-RSA-CAMELLIA256-SHA:DHE-DSS-CAMELLIA256-SHA:ECDH-RSA-AES256-GCM-SHA384:ECDH-ECDSA-AES256-GCM-SHA384:ECDH-RSA-AES256-SHA384:ECDH-ECDSA-AES256-SHA384:ECDH-RSA-AES256-SHA:ECDH-ECDSA-AES256-SHA:AES256-GCM-SHA384:AES256-SHA256:AES256-SHA:CAMELLIA256-SHA:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:DHE-DSS-AES128-GCM-SHA256:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES128-SHA256:DHE-DSS-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA:DHE-RSA-SEED-SHA:DHE-DSS-SEED-SHA:DHE-RSA-CAMELLIA128-SHA:DHE-DSS-CAMELLIA128-SHA:ECDH-RSA-AES128-GCM-SHA256:ECDH-ECDSA-AES128-GCM-SHA256:ECDH-RSA-AES128-SHA256:ECDH-ECDSA-AES128-SHA256:ECDH-RSA-AES128-SHA:ECDH-ECDSA-AES128-SHA:AES128-GCM-SHA256:AES128-SHA256:AES128-SHA:SEED-SHA:CAMELLIA128-SHA:IDEA-CBC-SHA:ECDHE-RSA-RC4-SHA:ECDHE-ECDSA-RC4-SHA:ECDH-RSA-RC4-SHA:ECDH-ECDSA-RC4-SHA:RC4-SHA:RC4-MD5:ECDHE-RSA-DES-CBC3-SHA:ECDHE-ECDSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:EDH-DSS-DES-CBC3-SHA:ECDH-RSA-DES-CBC3-SHA:ECDH-ECDSA-DES-CBC3-SHA:DES-CBC3-SHA:EDH-RSA-DES-CBC-SHA:EDH-DSS-DES-CBC-SHA:DES-CBC-SHA
CIPHER is ECDHE-RSA-AES256-GCM-SHA384
Secure Renegotiation IS supported
Hello! I am Client.Who are you ?
I am Server AIX :)
- 这工作得很好,这给我的印象是我的客户端代码和证书都是正常的,并且工作得很好,因为我能够在服务器上接收客户端消息,在客户端上接收服务器消息。不是吗?*
但是,当我在AIX上运行我的服务器端代码和客户端openssl命令时,如下所示:C:\WINDOWS\SYSTEM32>openssl s_client-Connect xx.xx.xxx.xxx:4005
输出如下客户端(服务端显示相同错误:1152921504606846944:错误:140890C7:ssl例程:SSL3_GET_CLIENT_CERTIFICATE:Peer未返回证书:s3_srvr.c:3281:)
Loading 'screen' into random state - done
CONNECTED(000000D0)
depth=1 /C=IN/ST=XX/L=AAA/O=BBB/OU=CCC/CN=root_crt/emailAddress=ans@gmail.com
verify error:num=19:self signed certificate in certificate chain
verify return:0
1520:error:14094410:SSL routines:SSL3_READ_BYTES:sslv3 alert handshake failure:.\ssl\s3_pkt.c:1146:SSL alert number 40
1520:error:140790E5:SSL routines:SSL23_WRITE:ssl handshake failure:.\ssl\s23_lib.c:178:**
- 证书链中的Error:Verify Error:Num=19:自签名证书似乎指向某种类型的证书签名过程。我查看了各种论坛和opessl.org,但似乎没有任何地方对此给出了适当的有效解决方案。*
- 请以您认为可以导致解决这些问题的任何方式帮助我。提前感谢...!!*
1条答案
按热度按时间fwzugrvs1#
我认为问题的一部分可能是在您的
LoadCertificates
函数中缺少对SSL_CTX_load_verify_locations()
的调用。请求客户端经由M1N2O1P消息发送其证书的一部分在该请求中包括服务器信任的CA的列表(即,它将用于验证任何客户端提供的证书)。服务器可能信任多个不同的CA,而给定的客户端可能有多个不同的证书可供选择。因此,
CertificateRequest
消息包含CA的列表,然后客户端将选择它的哪些客户端证书与那些CA匹配。因此,要使用用于验证客户端证书的CA列表配置OpenSSL,您可以使用
SSL_CTX_load_verify_locations()
函数,并将其指向串联证书的PEM文件和/或受信任证书的目录(使用opensslc_rehash
实用程序进行散列)。否则,您的服务器可能正在发送CertificateRequest
消息,但带有空的CA列表,因此客户端不能/不能选择要发送的客户端证书。希望这能有所帮助!