Openssl应用编程框架:
1.客户端程序的框架为:
- meth = SSLv23_client_method();
- ctx = SSL_CTX_new (meth);
- ssl = SSL_new(ctx);
- fd = socket();
- connect();
- SSL_set_fd(ssl,fd);
- SSL_connect(ssl);
- SSL_write(ssl,"Hello world",strlen("Hello World!"));
2.服务端程序的框架为:
- meth = SSLv23_server_method();
- ctx = SSL_CTX_new (meth);
- ssl = SSL_new(ctx);
- fd = socket();
- bind();
- listen();
- accept();
- SSL_set_fd(ssl,fd);
- SSL_connect(ssl);
- SSL_read (ssl, buf, sizeof(buf));
对程序来说,openssl将整个握手过程用一对函数体现,即客户端的SSL_connect和服务端的SSL_accept。而后的应用层数据交换则用SSL_read和 SSL_write来完成。
SSL协议一般要用到证书,可以用openssl工具生成:
1、生成RSA密钥的方法
openssl genrsa -des3 -out privkey.pem 2048
这个命令会生成一个2048位的密钥,同时有一个des3方法加密的密码,如果你不想要每次都输入密码,可以改成:
openssl genrsa -out privkey.pem 2048
建议用2048位密钥,少于此可能会不安全或很快将不安全。
2、生成一个证书请求
openssl req -new -key privkey.pem -out cert.csr
这个命令将会生成一个证书请求,当然,用到了前面生成的密钥privkey.pem文件
这里将生成一个新的文件cert.csr,即一个证书请求文件,你可以拿着这个文件去数字证书颁发机构(即CA)申请一个数字证书。CA会给你一个新的文件cacert.pem,那才是你的数字证书。
如果是自己做测试,那么证书的申请机构和颁发机构都是自己。就可以用下面这个命令来生成证书:
openssl req -new -x509 -key privkey.pem -out cacert.pem -days 1095
这个命令将用上面生成的密钥privkey.pem生成一个数字证书cacert.pem
3、使用数字证书和密钥
有了privkey.pem和cacert.pem文件后就可以在自己的程序中使用了。
服务端程序代码:
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <errno.h>
#include <winsock2.h>
#include <openssl/rsa.h> /* SSLeay stuff */
#include <openssl/crypto.h>
#include <openssl/x509.h>
#include <openssl/pem.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
/* define HOME to be dir for key and cert files... */
#define HOME "./"
/* Make these what you want for cert & key files */
#define CERTF HOME "cacert.pem"
#define KEYF HOME "privkey.pem"
#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); }
void socket_init_tcpip()
{
#ifdef _WIN32
WORD wVersionRequested;
WSADATA wsaData;
wVersionRequested = MAKEWORD(2, 2);
if (WSAStartup(wVersionRequested, &wsaData ) != 0)
{
return;
}
/* 检查版本号 */
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)
{
return;
}
#else
#endif
}
void main ()
{
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 preliminaries. We keep the certificate and key with the context. */
SSL_load_error_strings();
SSLeay_add_ssl_algorithms();
meth = (SSL_METHOD *)SSLv23_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 key\n");
exit(5);
}
/* ----------------------------------------------- */
/* Prepare TCP socket for receiving connections */
socket_init_tcpip();
listen_sd = socket (AF_INET, SOCK_STREAM, 0);
CHK_ERR(listen_sd, "socket");
memset (&sa_serv, '\0', sizeof(sa_serv));
sa_serv.sin_family = AF_INET;
sa_serv.sin_addr.s_addr = INADDR_ANY;
sa_serv.sin_port = htons (1111); /* Server Port number */
err = bind(listen_sd, (struct sockaddr*) &sa_serv,
sizeof (sa_serv)); CHK_ERR(err, "bind");
/* Receive a TCP connection. */
err = listen (listen_sd, 5); CHK_ERR(err, "listen");
client_len = sizeof(sa_cli);
sd = accept (listen_sd, (struct sockaddr*) &sa_cli, (int*)&client_len);
CHK_ERR(sd, "accept");
closesocket (listen_sd);
printf ("Connection from %lx, port %x\n",
sa_cli.sin_addr.s_addr, sa_cli.sin_port);
/* ----------------------------------------------- */
/* TCP connection is ready. Do server side SSL. */
ssl = SSL_new (ctx); CHK_NULL(ssl);
SSL_set_fd (ssl, sd);
err = SSL_accept (ssl); CHK_SSL(err);
/* Get the cipher - opt */
printf ("SSL connection using %s\n", SSL_get_cipher (ssl));
/* Get client's certificate (note: beware of dynamic allocation) - opt */
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);
/* We could do all sorts of certificate verification stuff here before
deallocating the certificate. */
X509_free (client_cert);
} else
printf ("Client does not have certificate.\n");
/* DATA EXCHANGE - Receive message and send reply. */
while(true)
{
err = SSL_read (ssl, buf, sizeof(buf) - 1);
CHK_SSL(err);
buf[err] = '\0';
printf ("Got %d chars:'%s'\n", err, buf);
err = SSL_write (ssl, "I hear you.", strlen("I hear you.")); CHK_SSL(err);
}
/* Clean up. */
closesocket(sd);
SSL_free (ssl);
SSL_CTX_free (ctx);
}
客户端程序代码:
#include <stdio.h>
#include <memory.h>
#include <errno.h>
#include <WinSock2.h>
#include <openssl/crypto.h>
#include <openssl/x509.h>
#include <openssl/pem.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
/* define HOME to be dir for key and cert files... */
#define HOME "./"
/* Make these what you want for cert & key files */
#define CERTF HOME "cacert_client.pem"
#define KEYF HOME "privkey.pem"
#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); }
void socket_init_tcpip()
{
#ifdef _WIN32
WORD wVersionRequested;
WSADATA wsaData;
wVersionRequested = MAKEWORD(2, 2);
if (WSAStartup(wVersionRequested, &wsaData ) != 0)
{
return;
}
/* 检查版本号 */
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)
{
return;
}
#else
#endif
}
void main ()
{
int err=0;
int sd;
struct sockaddr_in sa;
SSL_CTX* ctx;
SSL* ssl;
X509* server_cert;
char* str;
char buf [4096];
SSL_METHOD *meth;
SSLeay_add_ssl_algorithms();
meth = (SSL_METHOD *)SSLv23_client_method();
SSL_load_error_strings();
ctx = SSL_CTX_new (meth); CHK_NULL(ctx);
CHK_SSL(err);
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 key\n");
exit(5);
}
/* ----------------------------------------------- */
/* Create a socket and connect to server using normal socket calls. */
socket_init_tcpip();
sd = socket (AF_INET, SOCK_STREAM, 0); CHK_ERR(sd, "socket");
memset (&sa, '\0', sizeof(sa));
sa.sin_family = AF_INET;
sa.sin_addr.s_addr = inet_addr ("192.168.2.141"); /* Server IP */
sa.sin_port = htons (1111); /* Server Port number */
err = connect(sd, (struct sockaddr*) &sa,
sizeof(sa)); CHK_ERR(err, "connect");
/* ----------------------------------------------- */
/* Now we have TCP conncetion. Start SSL negotiation. */
ssl = SSL_new (ctx);
CHK_NULL(ssl);
SSL_set_fd (ssl, sd);
err = SSL_connect (ssl);
CHK_SSL(err);
/* Following two steps are optional and not required for
data exchange to be successful. */
/* Get the cipher - opt */
printf ("SSL connection using %s\n", SSL_get_cipher (ssl));
/* Get server's certificate (note: beware of dynamic allocation) - opt */
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);
/* We could do all sorts of certificate verification stuff here before
deallocating the certificate. */
X509_free (server_cert);
/* --------------------------------------------------- */
/* DATA EXCHANGE - Send a message and receive a reply. */
while (1)
{
printf("please input the words you want to send to server:\n");
char sendbuf[512]={0};
scanf("%s",sendbuf);
err = SSL_write (ssl, sendbuf, strlen(sendbuf));
CHK_SSL(err);
err = SSL_read (ssl, buf, sizeof(buf) - 1);
CHK_SSL(err);
buf[err] = '\0';
printf ("Got %d chars:'%s'\n", err, buf);
}
SSL_shutdown (ssl); /* send SSL/TLS close_notify */
/* Clean up. */
closesocket (sd);
SSL_free (ssl);
SSL_CTX_free (ctx);
}