文档章节

OpenSSL安全套接字编程

满小茂
 满小茂
发布于 2016/03/26 22:52
字数 1694
阅读 166
收藏 7

首先在系统中安装openssl1.0.2g版本的库

下载地址 https://www.openssl.org/source/

 

编译程序用下列命令:
gcc -Wall ssl-client.c -o client -lssl -lcrypto
gcc -Wall ssl-server.c -o server -lssl -lcrypto

用下面这两个命令产生上述cacert.pem和privkey.pem文件:
openssl genrsa -out privkey.pem 2048
openssl req -new -x509 -key privkey.pem -out cacert.pem -days 1095

运行程序用如下命令:
./server 7838 cacert.pem privkey.pem
./client 127.0.0.1 7838

服务器端

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <openssl/ssl.h>
#include <openssl/err.h>

#define MAXBUF 1024

int main(int argc, char **argv)
{
    int sockfd, new_fd;
    socklen_t len;
    struct sockaddr_in my_addr, their_addr;
    unsigned int myport, lisnum;
    char buf[MAXBUF + 1];
    SSL_CTX *ctx;

    if (argv[1])
        myport = atoi(argv[1]);
    else
        myport = 7838;


    lisnum = 2;

    /* 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, argv[2], SSL_FILETYPE_PEM) <= 0)
    {
        ERR_print_errors_fp(stdout);
        exit(1);
    }
    /* 载入用户私钥 */
    if (SSL_CTX_use_PrivateKey_file(ctx, argv[3], SSL_FILETYPE_PEM) <= 0)
    {
        ERR_print_errors_fp(stdout);
        exit(1);
    }
    /* 检查用户私钥是否正确 */
    if (!SSL_CTX_check_private_key(ctx))
    {
        ERR_print_errors_fp(stdout);
        exit(1);
    }

    /* 开启一个 socket 监听 */
    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");

    while (1)
    {
        SSL *ssl;
        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);

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

        /* 开始处理每个新连接上的数据收发 */
        bzero(buf, MAXBUF + 1);
        strcpy(buf, "server->client");
        /* 发消息给客户端 */
        len = SSL_write(ssl, buf, strlen(buf));

        if (len <= 0)
        {
            printf
            ("消息'%s'发送失败!错误代码是%d,错误信息是'%s'\n",
             buf, errno, strerror(errno));
            goto finish;
        }
        else
            printf("消息'%s'发送成功,共发送了%d个字节!\n",
                   buf, len);

        bzero(buf, MAXBUF + 1);
        /* 接收客户端的消息 */
        len = SSL_read(ssl, buf, MAXBUF);
        if (len > 0)
            printf("接收消息成功:'%s',共%d个字节的数据\n",
                   buf, len);
        else
            printf
            ("消息接收失败!错误代码是%d,错误信息是'%s'\n",
             errno, strerror(errno));
        /* 处理每个新连接上的数据收发结束 */
finish:
        /* 关闭 SSL 连接 */
        SSL_shutdown(ssl);
        /* 释放 SSL */
        SSL_free(ssl);
        /* 关闭 socket */
        close(new_fd);
    }

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

 

客户端

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <resolv.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <openssl/ssl.h>
#include <openssl/err.h>

#define MAXBUF 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(int argc, char **argv)
{
    int sockfd, len;
    struct sockaddr_in dest;
    char buffer[MAXBUF + 1];
    SSL_CTX *ctx;
    SSL *ssl;

    if (argc != 3)
    {
        printf
        ("参数格式错误!正确用法如下:\n\t\t%s IP地址 端口\n\t比如:\t%s 127.0.0.1 80\n此程序用来从某个 IP 地址的服务器某个端口接收最多 MAXBUF 个字节的消息",
         argv[0], argv[0]);
        exit(0);
    }

    /* SSL 库初始化,参看 ssl-server.c 代码 */
    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);
    }

    /* 创建一个 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(argv[2]));
    if (inet_aton(argv[1], (struct in_addr *) &dest.sin_addr.s_addr) == 0)
    {
        perror(argv[1]);
        exit(errno);
    }
    printf("address created\n");

    /* 连接服务器 */
    if (connect(sockfd, (struct sockaddr *) &dest, sizeof(dest)) != 0)
    {
        perror("Connect ");
        exit(errno);
    }
    printf("server connected\n");

    /* 基于 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);
    }

    /* 接收对方发过来的消息,最多接收 MAXBUF 个字节 */
    bzero(buffer, MAXBUF + 1);
    /* 接收服务器来的消息 */
    len = SSL_read(ssl, buffer, MAXBUF);
    if (len > 0)
        printf("接收消息成功:'%s',共%d个字节的数据\n",
               buffer, len);
    else
    {
        printf
        ("消息接收失败!错误代码是%d,错误信息是'%s'\n",
         errno, strerror(errno));
        goto finish;
    }
    bzero(buffer, MAXBUF + 1);
    strcpy(buffer, "from client->server");
    /* 发消息给服务器 */
    len = SSL_write(ssl, buffer, strlen(buffer));
    if (len < 0)
        printf
        ("消息'%s'发送失败!错误代码是%d,错误信息是'%s'\n",
         buffer, errno, strerror(errno));
    else
        printf("消息'%s'发送成功,共发送了%d个字节!\n",
               buffer, len);

finish:
    /* 关闭连接 */
    SSL_shutdown(ssl);
    SSL_free(ssl);
    close(sockfd);
    SSL_CTX_free(ctx);
    return 0;
}

 

SSL建立连接过程图

 

数字证书生成

    生成RSA密钥:

    openssl genrsa - des3 -out privkey.pem 2048

   这个命令会生成一个2048位的密钥,同时有一个des3方法加密的密码,如果你不想要每次都输入密码,可以改成:
    openssl genrsa -out privkey.pem 2048

  建议用2048位密钥,少于此可能会不安全或很快将不安全。

    生成一个证书请求:

   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

nginx安装数字证书

  • openssl下载: http://www.openssl.org/source/ 版本:1.0.1f

  注意:OPENSSL不需要安装,只需要解压出来就行。不然在编译时会提示类似以下的错误:
make[1]: Entering directory `/root/nginx'
cd /server/openssl \
&& make clean \
&& ./config --prefix=/server/openssl/openssl no-shared no-threads \
&& make \
&& make install
make[2]: Entering directory `/server/openssl'
make[2]: *** No rule to make target `clean'. Stop.
make[2]: Leaving directory `/server/openssl'
make[1]: *** [/server/openssl/openssl/include/openssl/ssl.h] Error 2
make[1]: Leaving directory `/root/nginx'
make: *** [build] Error 2

  • 安装Nginx的时候,把openssl路径指定到解压出来的路径:

./configure --with-http_stub_status_module --with-http_ssl_module --with-openssl=/root/openssl --with-http_gzip_static_module --with-http_stub_status_module
之后是一个漫长的等待时间

 

  • Nginx配置中,需要修改的地方:

   server {
        listen 443;
        server_name localhost;
        ssl on;
        ssl_certificate /usr/local/nginx/conf/cacert.pem;
        ssl_certificate_key /usr/local/nginx/conf/privkey.pem;
        server_name 192.168.10.70
        ssl_session_timeout 5m;
    }
 在iptables中打开ssl使用到的443端口,重启iptables.
 测试。访问时,发现有提示需要证书,应该成功了。

 

© 著作权归作者所有

满小茂
粉丝 79
博文 121
码字总数 136849
作品 0
成都
程序员
私信 提问
OpenSSL 1.0.2 发布

OpenSSL 在 1月22日时发布了 1.0.2 版本,这是一个主要的更新版本,包含众多的改进内容,详情请看 CHANGES。 下载地址:openssl-1.0.2.tar.gz 同时你现在可以通过 github 来访问 OpenSSL 源码...

oschina
2015/02/18
6.6K
0
OpenSSL 更新以修复远程执行漏洞

OpenSSL是一个强大的安全套接字层密码库,Apache使用它加密HTTPS,OpenSSH使用它加密SSH,但是,你不应该只将其作为一个库来使用,它还是一个多用途的、跨平台的密码工具。 OpenSSL的服务器端...

红薯
2010/11/17
655
1
数字证书,数字签名,https,ssl等安全概念的解释

讲这些概念之前,先讲讲非对称加密,与对称加密算法不同,非对称加密算法需要两个密钥:公开密钥(publickey)和私有密钥(privatekey)。公开密钥与私有密钥是一对,如果用公开密钥对数据进...

chaney
2014/05/28
1K
0
加密程序库 - OpenSSL

OpenSSL包含一个命令行工具用来完成OpenSSL库中的所有功能,更好的是,它可能已经安装到你的系统中了。 OpenSSL是一个强大的安全套接字层密码库,Apache使用它加密HTTPS,OpenSSH使用它加密S...

匿名
2009/01/09
86.7K
16
linux/Windows系统如何安装PHP-openssl扩展

现在很多的smtp服务器是需要ssl方式加密的,而支持ssl需要php加载openssl扩展。所以本文我们将和大家一起分享如何在linux/Windows系统下安装PHP的openssl扩展。 关于openssl OpenSSL是一个安...

丶八戒丶
2017/09/27
120
0

没有更多内容

加载失败,请刷新页面

加载更多

Spring Boot + Mybatis-Plus 集成与使用(二)

前言: 本章节介绍MyBatis-Puls的CRUD使用。在开始之前,先简单讲解下上章节关于Spring Boot是如何自动配置MyBatis-Plus。 一、自动配置 当Spring Boot应用从主方法main()启动后,首先加载S...

伴学编程
59分钟前
7
0
用最通俗的方法讲spring [一] ──── AOP

@[TOC](用最通俗的方法讲spring [一] ──── AOP) 写这个系列的目的(可以跳过不看) 自己写这个系列的目的,是因为自己是个比较笨的人,我曾一度怀疑自己的智商不适合干编程这个行业.因为在我...

小贼贼子
今天
6
0
Flutter系列之在 macOS 上安装和配置 Flutter 开发环境

本文为Flutter开发环境在macOS下安装全过程: 一、系统配置要求 想要安装并运行 Flutter,你的开发环境需要最低满足以下要求: 操作系统:macOS(64位) 磁盘空间:700 MB(不包含 IDE 或其余...

過愙
今天
4
0
OSChina 周六乱弹 —— 早上儿子问我他是怎么来的

Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 @凉小生 :#今日歌曲推荐# 少点戾气,愿你和这个世界温柔以待。中岛美嘉的单曲《僕が死のうと思ったのは (曾经我也想过一了百了)》 《僕が死の...

小小编辑
今天
2.5K
16
Excption与Error包结构,OOM 你遇到过哪些情况,SOF 你遇到过哪些情况

Throwable 是 Java 中所有错误与异常的超类,Throwable 包含两个子类,Error 与 Exception 。用于指示发生了异常情况。 Java 抛出的 Throwable 可以分成三种类型。 被检查异常(checked Exc...

Garphy
今天
42
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部