Linux下基于OpenSSL的SSL安全通信设计

24 篇文章 0 订阅

http://blog.163.com/xiaohu345@126/blog/static/6358736720107194658637/


本程序可以自由使用,请遵守GPL规范。

       OpenSSL中的SSL安全通信可以分为两类,两类基本上的操作相同,一类是建立SSL环境后使用BIO读写,另一类是直接在socket上建立SSL上下文环境。本文主要讨论在socket上建立SSL环境,以实现安全通信。首先需要生成一对客户机和服务器证书,这可以使用openssl的命令实现。使用赵春平前辈的OpenSSL编程一书中建立SSL测试环境的命令,可以建立一个模拟的CA,生成数字证书。如下:
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

       然后就可以使用OpenSSL的开源库实现SSL安全通信,本文设计了两种模式的通信,一种是没有建立SSL环境的TCP,另一种是建立了SSL环境的TCP,之后可以使用Linux下的嗅探软件wireshark嗅探出两种模式下的数据包,比较不同。程序设计参考了周立发老师的程序。

     下面首先是服务器端的程序:


    Profile: ssl tcp ip server
    Time   : 2010.07.31
    Edition: 1.0
********************************/
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <resolv.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <arpa/inet.h>

#include <openssl/bio.h>
#include <openssl/err.h>
#include <openssl/ssl.h>
#include "tcp_video.h"

#define MAXSIZE 1024 //每次最大数据传输量

#define PKEY_FILE "sslserverkey.pem"
#define CERT_FILE "sslservercert.pem"

int main()
{
    int sockfd,client_fd;
    socklen_t len;
 
    SSL_CTX *ctx;
   
    char serverbuf[MAXSIZE];

    ERR_load_BIO_strings();
    /* SSL 库初始化 */
    SSL_library_init();
    /* 载入所有 SSL 算法 */
    OpenSSL_add_all_algorithms();
    /* 载入所有 SSL 错误消息 */
    SSL_load_error_strings();
    /* 以 SSL V2 和 V3 标准兼容方式产生一个 SSL_CTX ,即 SSL Content Text */
    ctx = SSL_CTX_new(SSLv23_server_method());
    /* 也可以用 SSLv2_server_method() 或 SSLv3_server_method() 单独表示 V2 或 V3标准 */
    if (ctx == NULL)
    {
        ERR_print_errors_fp(stdout);
        exit(1);
    }
    /* 载入用户的数字证书, 此证书用来发送给客户端。 证书里包含有公钥 */
    if (!SSL_CTX_use_certificate_file(ctx, CERT_FILE, SSL_FILETYPE_PEM))
    {
        ERR_print_errors_fp(stdout);
        exit(1);
    }
    /* 载入用户私钥 */
    if (!SSL_CTX_use_PrivateKey_file(ctx, PKEY_FILE, SSL_FILETYPE_PEM) )
    {
        ERR_print_errors_fp(stdout);
        exit(1);
    }
    /* 检查用户私钥是否正确 */
    if (!SSL_CTX_check_private_key(ctx))
    {
        ERR_print_errors_fp(stdout);
        exit(1);
    }
   
    int chose=0;
   
    signal(SIGPIPE,SIG_IGN);
    tcpserver_init(&sockfd);

    while(1)
    {
        printf("Please Chose Channel No.:\n");
        printf("1.:SSL Protocol Channel\n");
        printf("2.:TCP Protocol Channel\n");
        scanf("%d",&chose);
        if(chose==1)
        {

            SSL *ssl;
            tcp_accept(sockfd,&client_fd);

            /* 基于 ctx 产生一个新的 SSL */
            ssl = SSL_new(ctx);
            /* 将连接用户的 socket 加入到 SSL */
            SSL_set_fd(ssl, client_fd);
            /* 建立 SSL 连接 */
            if (SSL_accept(ssl) == -1)
            {
                perror("accept");
                close(client_fd);
                break;
            }

            // 接收消息
            bzero(serverbuf, MAXSIZE);
            /* 接收客户端的消息 */
            len = SSL_read(ssl,serverbuf, MAXSIZE);
            if (len > 0)
                printf("接收消息成功:'%s',共%d个字节的数据\n",serverbuf, len);
            else
                printf("消息接收失败!错误代码是%d,错误信息是'%s'\n",errno, strerror(errno));

            /* 关闭 SSL 连接 */
            SSL_shutdown(ssl);
            /* 释放 SSL */
            SSL_free(ssl);
            /* 关闭 socket */
            close(client_fd);
        }
        else if(chose==2)
        {
            tcp_accept(sockfd,&client_fd);
            len=recv(client_fd,serverbuf, MAXSIZE,0);
            if (len > 0)
                printf("接收消息成功:'%s',共%d个字节的数据\n",serverbuf, len);
            else
                printf("消息接收失败!错误代码是%d,错误信息是'%s'\n",errno, strerror(errno));

            /* 关闭 socket */
            close(client_fd);
        }
        chose=0;
    }

    /* 关闭监听的 socket */
    close(sockfd);
    /* 释放 CTX */
    SSL_CTX_free(ctx);
    return 0;

}//main

再次是客户端编程



/*********************************
    Profile: ssl tcp ip client
    Time   : 2010.07.31
    Edition: 1.0
*********************************/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <resolv.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <arpa/inet.h>

#include <openssl/bio.h>
#include <openssl/err.h>
#include <openssl/ssl.h>
#include "tcp_video.h"

#define MAXSIZE 1024 //每次最大数据传输量

void ShowCerts(SSL * ssl)
{
    X509 *cert;
    char *line;

    cert = SSL_get_peer_certificate(ssl);
    if (cert != NULL)
    {
        printf("数字证书信息:\n");
        line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
        printf("证书: %s\n", line);
        free(line);
        line = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
        printf("颁发者: %s\n", line);
        free(line);
        X509_free(cert);
    }
    else
        printf("无证书信息!\n");
}

int main()
{
    char *hostname="127.0.0.1";
    int sockfd, len;
    char clientbuf[MAXSIZE];
    struct hostent *host;//gethostbyname函数的参数返回
    struct sockaddr_in serv_addr;
    SSL_CTX *ctx;
    SSL *ssl;
   
    /* SSL 库初始化 */
    SSL_library_init();
    OpenSSL_add_all_algorithms();
    SSL_load_error_strings();
    ctx = SSL_CTX_new(SSLv23_client_method());
    if (ctx == NULL)
    {
        ERR_print_errors_fp(stdout);
        exit(1);
    }

    int chose=0;
    printf("Please Chose Channel No.:\n");
    printf("1.:SSL Protocol Channel\n");
    printf("2.:TCP Protocol Channel\n");
    scanf("%d",&chose);

    serv_addr=tcpclient_init(&sockfd);
    if(chose==1)
    {
        tcp_connect(&sockfd,serv_addr);
        /* 基于 ctx 产生一个新的 SSL */
        ssl = SSL_new(ctx);
        SSL_set_fd(ssl, sockfd);
        /* 建立 SSL 连接 */
        if (SSL_connect(ssl) == -1)
            ERR_print_errors_fp(stderr);
        else
        {
            printf("Connected with %s encryption\n", SSL_get_cipher(ssl));
            ShowCerts(ssl);
        }
   
        /* 发消息给服务器 */
        bzero(clientbuf, MAXSIZE);
        strcpy(clientbuf, "id:am3517&pw:am3517\n");
        len = SSL_write(ssl, clientbuf, strlen(clientbuf));
        if (len < 0)
            printf("消息'%s'发送失败!错误代码是%d,错误信息是'%s'\n",clientbuf, errno, strerror(errno));
        else
            printf("消息'%s'发送成功,共发送了%d个字节!\n",clientbuf, len);

        /* 关闭连接 */
        SSL_shutdown(ssl);
        SSL_free(ssl);
    }
    else if(chose==2)
    {
        tcp_connect(&sockfd,serv_addr);
        /* 发消息给服务器 */
        bzero(clientbuf, MAXSIZE);
        strcpy(clientbuf, "id:am3517&pw:am3517\n");
        len = send(sockfd, clientbuf, strlen(clientbuf),0);
        if (len < 0)
            printf("消息'%s'发送失败!错误代码是%d,错误信息是'%s'\n",clientbuf, errno, strerror(errno));
        else
            printf("消息'%s'发送成功,共发送了%d个字节!\n",clientbuf, len);
    }

    close(sockfd);
    SSL_CTX_free(ctx);
    return 0;
}

       编译是需要OpenSSL库的支持,怎么安装OpenSSL在这里就不说了,网上有很多例子,另外在Ubuntu系统下可以直接使用新力得软件包安装。编辑时需要加 -lssl 选项,另外如果要调试,需要加 -g选项。
       另外,程序中使用的tcp连接的函数,可以使用下面的函数,本系统使用端口7838。

/*********************************
         服务器socket建立
*********************************/
void tcpserver_init(int *sockfd)
{
    socklen_t len;
    struct sockaddr_in my_addr;
    unsigned int myport, lisnum;

    myport = 7838;
    lisnum = 1;

    if ((*sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1)
    {
        perror("socket");
        exit(1);
    }
    else
        printf("socket created\n");

    bzero(&my_addr, sizeof(my_addr));
    my_addr.sin_family = PF_INET;
    my_addr.sin_port = htons(myport);
    my_addr.sin_addr.s_addr = INADDR_ANY;

    if(bind(*sockfd, (struct sockaddr *) &my_addr, sizeof(struct sockaddr)) == -1)
    {
        perror("bind");
        exit(1);
    }
    else
        printf("binded\n");

    if (listen(*sockfd, lisnum) == -1)
    {
        perror("listen");
        exit(1);
    }
    else
        printf("begin listen\n");
}

/*********************************
      响应客户端请求,建立连接
*********************************/
void tcp_accept(int sockfd,int *new_fd)
{
    struct sockaddr_in their_addr;
    socklen_t len;
    len = sizeof(struct sockaddr);
    if ((*new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &len)) == -1)
    {
        perror("accept");
        exit(errno);
    }
    else
        printf("server: got connection from %s, port %d, socket %d\n",inet_ntoa(their_addr.sin_addr), ntohs(their_addr.sin_port), *new_fd);
   
    int flags;
    flags = fcntl (sockfd, F_GETFL);  
    if (flags & O_NONBLOCK) /* if O_NONBLOCK is set */
    { 
        //fcntl (sockfd, F_SETFL, flags-O_NONBLOCK); /* unset it */
        fcntl(sockfd,F_SETFL,flags&(~O_NONBLOCK));
    }
}

/*********************************
          客户端socket建立
*********************************/
struct sockaddr_in tcpclient_init(int *sockfd)
{
    int len;
    struct sockaddr_in dest;

    char parainfo[3][20];
    printf("input server IP:\n");
    scanf("%s",parainfo[0]);
    printf("input server port:\n");
    scanf("%s",parainfo[1]);
   
    /* 创建一个 socket 用于 tcp 通信 */
    if((*sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
        perror("Socket");
        exit(errno);
    }
    printf("socket created\n");

    /* 初始化服务器端(对方)的地址和端口信息 */
    bzero(&dest, sizeof(dest));
    dest.sin_family = AF_INET;
    dest.sin_port = htons(atoi(parainfo[1]));
    if (inet_aton(parainfo[0], (struct in_addr *) &dest.sin_addr.s_addr) == 0)
    {
        perror(parainfo[0]);
        exit(errno);
    }
    printf("address created\n");
    return dest;
}

/*********************************
          与服务器建立连接
*********************************/
void tcp_connect(int *sockfd,struct sockaddr_in dest)
{
    /* 连接服务器 */
    if (connect(*sockfd, (struct sockaddr *) &dest, sizeof(dest)) != 0)
    {
        perror("Connect ");
        exit(errno);
    }
    printf("server connected\n");
   
    int flags;
    flags = fcntl (sockfd, F_GETFL);  
    if (flags & O_NONBLOCK) /* if O_NONBLOCK is set */
    { 
        //fcntl (sockfd, F_SETFL, flags-O_NONBLOCK); /* unset it */
        fcntl(sockfd,F_SETFL,flags&(~O_NONBLOCK));
    }
}

参考资料:赵春平。OpenSSL编程
                周立发。http://zhoulifa.bokee.com/



  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值