服务端:
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <errno.h>
#ifndef _WIN32
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
#else
#include <winsock2.h>
#include <windows.h>
#endif
#include "pthread.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 CERTF "certs/sslservercert.pem"
#define CERTF "servercert.pem"
//#define KEYF "certs/sslserverkey.pem"
#define KEYF "serverkey.pem"
//#define CAFILE "certs/cacert.pem"
#define CAFILE "demoCA/cacert.pem"
#define SOCKET_ERROR -1
#define SOCKET int
pthread_mutex_t mlock=PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t *lock_cs;
static long *lock_count;
#define CHK_NULL(x) if ((x)==NULL) { printf("null\n"); }
#define CHK_ERR(err,s) if ((err)==-1) { printf(" -1 \n"); }
#define CHK_SSL(err) if ((err)==-1) { printf(" -1 \n");}
#define CAFILE "demoCA/cacert.pem"
int verify_callback_server(int ok, X509_STORE_CTX *ctx)
{
printf("verify_callback_server \n");
return ok;
}
int SSL_CTX_use_PrivateKey_file_pass(SSL_CTX *ctx,char *filename,char *pass)
{
EVP_PKEY *pkey=NULL;
BIO *key=NULL;
key=BIO_new(BIO_s_file());
BIO_read_filename(key,filename);
pkey=PEM_read_bio_PrivateKey(key,NULL,NULL,pass);
if(pkey==NULL)
{
printf("PEM_read_bio_PrivateKey err");
return -1;
}
if (SSL_CTX_use_PrivateKey(ctx,pkey) <= 0)
{
printf("SSL_CTX_use_PrivateKey err\n");
return -1;
}
BIO_free(key);
return 1;
}
static int s_server_verify=SSL_VERIFY_NONE;
void * thread_main(void *arg)
{
SOCKET s;
SOCKET AcceptSocket;
//WORD wVersionRequested;
//WSADATA wsaData;
struct sockaddr_in service;
int err;
size_t client_len; SSL_CTX *ctx;
SSL *ssl;
X509 *client_cert;
char *str;
char buf[1024];
SSL_METHOD *meth;
ssl=(SSL *)arg;
s=SSL_get_fd(ssl);
err = SSL_accept (ssl);
if(err<0)
{
printf("ssl accerr\n");
return ;
}
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");
memset(buf,0,1024);
err = SSL_read (ssl, buf, sizeof(buf) - 1);
if(err<0)
{
printf("ssl read err\n");
//closesocket(s);
close(s);
return;
}
printf("get : %s\n",buf);
#if 1
buf[err] = '\0';
buf[err] = '\0';
err = SSL_write (ssl, "I hear you.", strlen("I hear you.")); CHK_SSL(err);
#endif
SSL_free (ssl);
//closesocket(s);
close(s);
}
pthread_t pthreads_thread_id(void)
{
pthread_t ret;
ret=pthread_self();
return(ret);
}
void pthreads_locking_callback(int mode, int type, char *file,
int line)
{
if (mode & CRYPTO_LOCK)
{
pthread_mutex_lock(&(lock_cs[type]));
lock_count[type]++;
}
else
{
pthread_mutex_unlock(&(lock_cs[type]));
}
}
int main ()
{
int err;
int i;
SOCKET s;
SOCKET AcceptSocket;
//int s,AcceptSocket;
//WORD wVersionRequested;
//WSADATA wsaData;
struct sockaddr_in service;
pthread_t pid;
size_t client_len;
SSL_CTX *ctx;
SSL *ssl;
X509 *client_cert;
char *str;
char buf[1024];
SSL_METHOD *meth;
SSL_load_error_strings();
SSLeay_add_ssl_algorithms();
meth = SSLv3_server_method();
ctx = SSL_CTX_new (meth);
if (!ctx)
{
ERR_print_errors_fp(stderr);
exit(2);
}
if ((!SSL_CTX_load_verify_locations(ctx,CAFILE,NULL)) ||
(!SSL_CTX_set_default_verify_paths(ctx)))
{
printf("err\n");
exit(1);
}
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_pass(ctx, KEYF, "123456") <= 0)
if (SSL_CTX_use_PrivateKey_file_pass(ctx, KEYF, "1428795366") <= 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);
}
s_server_verify=SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT|
SSL_VERIFY_CLIENT_ONCE;
SSL_CTX_set_verify(ctx,s_server_verify,verify_callback_server);
SSL_CTX_set_client_CA_list(ctx,SSL_load_client_CA_file(CAFILE));
//wVersionRequested = MAKEWORD( 2, 2 );
//err = WSAStartup( wVersionRequested, &wsaData );
//if ( err != 0 )
/*{
printf("err\n");
return -1;
}*/
s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(s<0) return -1;
service.sin_family = AF_INET;
service.sin_addr.s_addr = inet_addr("127.0.0.1");
service.sin_port = htons(1111);
//if (bind( s, (SOCKADDR*) &service, sizeof(service)) == SOCKET_ERROR)
if (bind( s, (struct sockaddr *) &service, sizeof(service)) == SOCKET_ERROR)
{
printf("bind() failed.\n");
// closesocket(s);
close(s);
return -1;
}
if (listen( s, 1 ) == SOCKET_ERROR)
printf("Error listening on socket.\n");
printf("recv .....\n");
lock_cs=OPENSSL_malloc(CRYPTO_num_locks() * sizeof(pthread_mutex_t));
lock_count=OPENSSL_malloc(CRYPTO_num_locks() * sizeof(long));
for (i=0; i<CRYPTO_num_locks(); i++)
{
lock_count[i]=0;
pthread_mutex_init(&(lock_cs[i]),NULL);
}
CRYPTO_set_id_callback((unsigned long (*)())pthreads_thread_id);
CRYPTO_set_locking_callback((void (*)())pthreads_locking_callback);
while(1)
{
struct timeval tv;
fd_set fdset;
tv.tv_sec = 1;
tv.tv_usec = 0;
FD_ZERO(&fdset);
FD_SET(s, &fdset);
select(s+1, &fdset, NULL, NULL, (struct timeval *)&tv);
if(FD_ISSET(s, &fdset))
{
AcceptSocket=accept(s, NULL,NULL);
ssl = SSL_new (ctx);
CHK_NULL(ssl);
err=SSL_set_fd (ssl, AcceptSocket);
if(err>0)
{
err=pthread_create(&pid,NULL,&thread_main,(void *)ssl);
pthread_detach(pid);
}
else
continue;
}
}
SSL_CTX_free (ctx);
return 0;
}
客户端:
#include <stdio.h>
#include <memory.h>
#include <errno.h>
#ifndef _WIN32
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
#else
#include <windows.h>
#endif
#include "pthread.h"
#include <openssl/crypto.h>
#include <openssl/x509.h>
#include <openssl/pem.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#define MAX_T 3
//#define CLIENTCERT "certs/sslclientcert.pem"
#define CLIENTCERT "clientcert.pem"
//#define CLIENTKEY "clientkey.pem"
#define CLIENTKEY "clientkey.pem"
//#define CAFILE "certs/cacert.pem"
#define CAFILE "certs/cacert.pem"
#define SOCKET int
#define SOCKET_ERROR -1
static pthread_mutex_t *lock_cs;
static long *lock_count;
pthread_t pthreads_thread_id(void)
{
pthread_t ret;
ret=pthread_self();
return(ret);
}
void pthreads_locking_callback(int mode, int type, char *file,int line)
{
if (mode & CRYPTO_LOCK){
pthread_mutex_lock(&(lock_cs[type]));
lock_count[type]++;
}
else {
pthread_mutex_unlock(&(lock_cs[type]));
}
}
int verify_callback(int ok, X509_STORE_CTX *ctx)
{
printf("verify_callback\n");
return ok;
}
int SSL_CTX_use_PrivateKey_file_pass(SSL_CTX *ctx,char *filename,char *pass)
{
EVP_PKEY *pkey=NULL;
BIO *key=NULL;
key=BIO_new(BIO_s_file());
BIO_read_filename(key,filename);
pkey=PEM_read_bio_PrivateKey(key,NULL,NULL,pass);
if(pkey==NULL) {
printf("PEM_read_bio_PrivateKey err");
return -1;
}
if (SSL_CTX_use_PrivateKey(ctx,pkey) <= 0){
printf("SSL_CTX_use_PrivateKey err\n");
return -1;
}
BIO_free(key);
return 1;
}
void*thread_main(void *arg)
{
int err,buflen,read;
int sd;
SSL_CTX *ctx=(SSL_CTX *)arg;
struct sockaddr_in dest_sin;
SOCKET sock;
/*PHOSTENT phe;
WORD wVersionRequested;
WSADATA wsaData;*/
SSL *ssl;
X509 *server_cert;
char *str;
char buf [1024];
SSL_METHOD *meth;
FILE *fp;
/* wVersionRequested = MAKEWORD( 2, 2 );
err = WSAStartup( wVersionRequested, &wsaData );
if ( err != 0 ){
printf("WSAStartup err\n");
return -1;
}*/
sock = socket(AF_INET, SOCK_STREAM, 0);
dest_sin.sin_family = AF_INET;
dest_sin.sin_addr.s_addr = inet_addr( "127.0.0.1" );
dest_sin.sin_port = htons( 1111 );
again:
//err=connect( sock,(PSOCKADDR) &dest_sin, sizeof( dest_sin));
err=connect( sock,(struct sockaddr *) &dest_sin, sizeof( dest_sin));
if(err<0){
//Sleep(1);
sleep(1);
goto again;
}
ssl = SSL_new (ctx);
if(ssl==NULL){
printf("ss new err\n");
return ;
}
SSL_set_fd(ssl,sock);
err = SSL_connect (ssl);
if(err<0) {
printf("SSL_connect err\n");
return;
}
printf ("SSL connection using %s\n", SSL_get_cipher (ssl));
server_cert = SSL_get_peer_certificate (ssl);
printf ("Server certificate:\n");
str = X509_NAME_oneline (X509_get_subject_name (server_cert),0,0);
printf ("\t subject: %s\n", str);
OPENSSL_free (str);
str = X509_NAME_oneline (X509_get_issuer_name (server_cert),0,0);
printf ("\t issuer: %s\n", str);
OPENSSL_free (str);
X509_free (server_cert);
err = SSL_write (ssl, "Hello World!", strlen("Hello World!"));
if(err<0) {
printf("ssl write err\n");
return ;
}
#if 1
//memset(buf,0,ONE_BUF_SIZE);
memset(buf,0,1024);
err = SSL_read (ssl, buf, sizeof(buf) - 1);
if(err<0){
printf("ssl read err\n");
return ;
}
buf[err] = '\0';
printf ("Got %d chars:'%s'\n", err, buf);
#endif
SSL_shutdown (ssl); /* send SSL/TLS close_notify */
SSL_free (ssl);
//closesocket(sock);
close(sock);
}
int main ()
{
int err,buflen,read;
int sd;
struct sockaddr_in dest_sin;
SOCKET sock;
/*PHOSTENT phe;
WORD wVersionRequested;
WSADATA wsaData;*/
SSL_CTX *ctx;
SSL *ssl;
X509 *server_cert;
char *str;
char buf [1024];
SSL_METHOD *meth;
int i;
pthread_t pid[MAX_T];
SSLeay_add_ssl_algorithms();
meth = SSLv3_client_method();
SSL_load_error_strings();
ctx = SSL_CTX_new (meth);
if(ctx==NULL){
printf("ssl ctx new eer\n");
return -1;
}
if (SSL_CTX_use_certificate_file(ctx, CLIENTCERT, SSL_FILETYPE_PEM) <= 0){
ERR_print_errors_fp(stderr);
exit(3);
}
//if (SSL_CTX_use_PrivateKey_file_pass(ctx, CLIENTKEY, "123456") <= 0){
if (SSL_CTX_use_PrivateKey_file_pass(ctx, CLIENTKEY, "1428795366") <= 0){
ERR_print_errors_fp(stderr);
exit(4);
}
lock_cs=OPENSSL_malloc(CRYPTO_num_locks() * sizeof(pthread_mutex_t));
lock_count=OPENSSL_malloc(CRYPTO_num_locks() * sizeof(long));
for (i=0; i<CRYPTO_num_locks(); i++)
{
lock_count[i]=0;
pthread_mutex_init(&(lock_cs[i]),NULL);
}
CRYPTO_set_id_callback((unsigned long (*)())pthreads_thread_id);
CRYPTO_set_locking_callback((void (*)())pthreads_locking_callback);
for(i=0;i<MAX_T;i++)
{
err=pthread_create(&(pid[i]),NULL,&thread_main,(void *)ctx);
if(err!=0)
{
printf("pthread_create err\n");
continue;
}
}
for (i=0; i<MAX_T; i++)
{
pthread_join(pid[i],NULL);
}
SSL_CTX_free (ctx);
printf("test ok\n");
return 0;
}
其中原实例是windows 下,在linux上面运行修改的地方有
#define SOCKET_ERROR -1
#define SOCKET int
去掉有
//WORD wVersionRequested;
//WSADATA wsaData;
//wVersionRequested = MAKEWORD( 2, 2 );
//err = WSAStartup( wVersionRequested, &wsaData );
//if ( err != 0 )
/*{
printf("err\n");
return -1;
}*/
if (bind( s, (SOCKADDR*) &service, sizeof(service)) == SOCKET_ERROR)改为if (bind( s, (struct sockaddr *) &service, sizeof(service)) == SOCKET_ERROR)
closesocket(s);改为close(s);
1.客户端程序的框架为:
/*生成一个ssl结构*/
meth = sslv23_client_method();
ctx = ssl_ctx_new (meth);
ssl = ssl_new(ctx);
/*下面是正常的socket过程*/
fd = socket();
connect();
/*把建立好的socket和ssl结构联系起来*/
ssl_set_fd(ssl,fd);
/*ssl的握手过程*/
ssl_connect(ssl);
/*接下来用ssl_write(), ssl_read()代替原有的write(),read()即可*/
ssl_write(ssl,"hello world",strlen("hello world!"));
2.服务端程序的框架为:
/*生成一个ssl结构*/
meth = sslv23_server_method();
ctx = ssl_ctx_new (meth);
ssl = ssl_new(ctx);
/*下面是正常的socket过程*/
fd = socket();
bind();
listen();
accept();
/*把建立好的socket和ssl结构联系起来*/
ssl_set_fd(ssl,fd);
/*ssl的握手过程*/
ssl_connect(ssl);
/*接下来用ssl_write(), ssl_read()代替原有的write(),read()即可*/
ssl_read (ssl, buf, sizeof(buf));
比较主要的函数有
1.int ssl_ctx_set_cipher_list(ssl_ctx *,const char *str);
根据ssl/tls规范,在clienthello中,客户端会提交一份自己能够支持的加密方法的列表,由服务端选择一种方法后在serverhello中通知服务端,从而完成加密算法的协商.
2.void ssl_ctx_set_verify(ssl_ctx *ctx,int mode,int (*callback)(int, x509_store_ctx *));
缺省mode是ssl_verify_none,如果想要验证对方的话,便要将此项变成ssl_verify_peer.ssl/tls中缺省只验证server,如果没有设置ssl_verify_peer的话,客户端连证书都不会发过来.
3.int ssl_ctx_load_verify_locations(ssl_ctx *ctx, const char *cafile,const char *capath);
要验证对方的话,当然装要有ca的证书了,此函数用来便是加载ca的证书文件的.
4.int ssl_ctx_use_certificate_file(ssl_ctx *ctx, const char *file, int type);
加载自己的证书文件.
5.int ssl_ctx_use_privatekey_file(ssl_ctx *ctx, const char *file, int type);
加载自己的私钥,以用于签名.
6.int ssl_ctx_check_private_key(ssl_ctx *ctx);
调用了以上两个函数后,自己检验一下证书与私钥是否一致
7.openssl_add_ssl_algorithms()或ssleay_add_ssl_algorithms()
其实都是调用int ssl_library_init(void)
进行一些必要的初始化工作,用openssl编写ssl/tls程序的话第一句便应是它.
8.void ssl_load_error_strings(void );
如果想打印出一些方便阅读的调试信息的话,便要在一开始调用此函数.
9.void err_print_errors_fp(file *fp);
如果调用了ssl_load_error_strings()后,便可以随时用err_print_errors_fp()来打印错误信息了.
10.x509 *ssl_get_peer_certificate(ssl *s);
握手完成后,便可以用此函数从ssl结构中提取出对方的证书(此时证书得到且已经验证过了)整理成x509结构.
11.x509_name *x509_get_subject_name(x509 *a);
得到证书所有者的名字,参数可用通过ssl_get_peer_certificate()得到的x509对象.
12.x509_name *x509_get_issuer_name(x509 *a)
得到证书签署者(往往是ca)的名字,参数可用通过ssl_get_peer_certificate()得到的x509对象.
13.char *x509_name_oneline(x509_name *a,char *buf,int size);
将以上两个函数得到的对象变成字符型,以便打印出来.
相关文件的生成
1、建立自己的CA
在OpenSSL的安装目录下的misc目录下,运行脚本sudo ./CA.sh -newCA。运行完后会生成一个demoCA的目录,里面存放了CA的证书和私钥。
2、生成客户端和服务器证书申请
openssl req -newkey rsa:1204 -out req1.pem -keyout sslclientkey.pem
openssl req -newkey rsa:1204 -out req2.pem -keyout sslserverkey.pem
3、签发客户端和服务器证书
openssl ca -in req1.pem -out sslclientcert.pem
openssl ca -in req2.pem -out sslservercert.pem
注意:
1.在填入信息的时候国家应该相同。
2.cacert.pem文件在demoCA文件夹下
http://getpocket.com/a/read/1449329039
http://getpocket.com/a/read/1449326704