zoukankan      html  css  js  c++  java
  • 使用c语言实现在linux下的openssl客户端和服务器端编程

    使用c语言实现在linux下的openssl客户端和服务器端编程

    摘自:https://www.cnblogs.com/etangyushan/p/3679457.html

      前几天组长让我实现一个使用openssl的c语言编写的客户端和java编写的服务器实现字符流的通信,给了段代码。在自己的ubuntu上跑服务器和客户端收发信息都没有问题,但是就是和java的通信不了。后来发现组长给的客户端代码有问题,于是网上找到了比较正确的客户端和服务器代码,自己做了稍微的改动。有一点要说一下,我的c客户端使用的证书格式是.pem的,而java那边使用kittool生成的证书格式是.cer的所以需要进行cer到pem格式的转换才可以使用。

    我使用的是这个网址下进行证书格式转换:https://www.sslshopper.com/ssl-converter.html

    操作是:

    1.选择要转换的文件。

    2.选择文件的格式,里面没有cer格式选项,可以使用der就可以。 

    3.选择要转换成为什么格式我是选择standard pem。生成的是。crt文件,这个在ubuntu下面导入到我的客户端中即可。

    以下是我编程的具体的操作步骤,大部分是从网上整理的,小部分的做了改动,加了注释。

    安装openssl安装包

    在以下网站下载openssl:

    http://www.openssl.org/source/openssl-1.0.0a.tar.gz

    tar -zxvf openssl-1.0.0a.tar.gz
    mv openssl-1.0.1a openssl
    cd openssl

     如果需要zlib压缩模块的话,还需要先安装zlib

    ./config --prefix=/usr/local/ssl shared zlib-dynamic enable-camellia

     不需要就直接用:

    ./config --prefix=/usr/local/ssl shared no-zlib

     更多详细帮助请运行

    ./config –help
    ./config –t
    make depend
    make
    make test
    sudo make install

     设置环境变量:在/etc/profile的PATH中增加如下内容

    PATH=/usr/local/ssl/bin:/sbin/:$PATH:/usr/sbin
    export PATH
    cat /etc/ssl/openssl.cnf
    //查看路径
    which openssl
    //查看版本
    openssl version
    openssl安装完毕

    如果计算机联网的话可以使用如下命令安装比较简便

    Ubuntu系统下安装openssl

    sudo apt-get install openssl
    //安装openssl-devel
    //由于ubuntu下无法安装openssl-devel 所以使用libssl-dev代替openssl-devel
    sudo apt-get install libssl-dev

    CentOS系统下安装openssl

    复制代码
    //解压openssl安装包
    [root@localhost opt]# tar xvzf openssl-1.0.0d.tar.gz
    //进入解压后的目录
    [root@localhost opt]# cd openssl-1.0.0d
    //修改openssl配置文件
    [root@localhost openssl-1.0.0d]# ./configure --prefix=/usr/local/openssl
    //编译代码
    [root@localhost openssl-1.0.0d]# make
    //安装
    [root@localhost openssl-1.0.0d]# make install
    //安装curses.h头文件的库
    sudo apt-get install libncurses5-dev
    //所需软件安装完毕:openssl、openssl-devel、libncurses5-dev 三个软件
    复制代码

    生成工作目录产生CA凭证

    ca.crt 为自签名证书;
    server.crt,server.key 为服务器端的证书和私钥文件;
    proxy.crt,proxy.key 为代理服务器端的证书和私钥文件;
    client.crt,client.key 为客户端的证书和私钥文件。

    复制代码
    //把openssl安装目录下的misc拷贝到用户目录下
    cd
    mkdir sslca
    cd sslca
    sudo cp /usr/local/ssl/ssl/misc -r ./
    sudo cp /usr/local/ssl/ssl/openssl.cnf ./misc
    Cd misc
    CA.sh –newca
    //产生一个demoCA文件夹
    cd demoCA
    //复制安装目录下面的openssl.cnf文件到demoCA下
    sudo cp /usr/local/ssl/ssl/openssl.cnf ./
    //产生CA自签名证书
    openssl genrsa -out ./private/ca.key -rand ./private/.rnd -des 2048
    openssl req -new -x509 -days 3650 -key ./private/ca.key -out ./private/ca.crt -config openssl.cnf
    openssl x509 -in ./private/ca.crt -noout -text
    //产生server的证书过程
    openssl genrsa -out ./private/server.key 1024
    openssl req -new -key ./private/server.key -out ./newcerts/server.csr -config openssl.cnf
    //这一步如果产生错误,请看后面的解决方法
    openssl ca -in ./newcerts/server.csr -cert ./private/ca.crt -keyfile ./private/ca.key -config openssl.cnf -policy policy_anything -out ./certs/server.crt
    openssl x509 -in ./certs/server.crt -noout -text
    //产生proxy的证书过程
    openssl genrsa -out ./private/proxy.key 1024
    //这步要是产生错误,请看后面的解决方法
    openssl req -new -key ./private/proxy.key -out ./newcerts/proxy.csr -config openssl.cnf
    openssl ca -in ./newcerts/proxy.csr -cert ./private/ca.crt -keyfile./private/ca.key -config openssl.cnf -policy policy_anything -out ./certs/proxy.crt
    openssl x509 -in ./certs/proxy.crt -noout -text
    //产生client的证书过程
    openssl genrsa -out ./private/client.key 1024
    openssl req -new -key ./private/client.key -out ./newcerts/client.csr -config openssl.cnf
    openssl ca -in ./newcerts/client.csr -cert ./private/ca.crt -keyfile ./private/ca.key -config openssl.cnf -policy policy_anything -out ./certs/client.crt
    openssl x509 -in ./certs/client.crt -noout -text
    复制代码

    产生一般错误的解决方法

    复制代码
    //出现:I am unable to access the ./demoCA/newcerts directory
           ./demoCA/newcerts:Nosuch file or directory
      解决:修改openssl.cnf
      在42行:dir = ./demoCA修改为 dir = ./
    //出现:failed to update database
             TXT_DB error number 2
      解决:修改index.txt.attr
      将unique_subject = yes修改为 unique_subject = no
    复制代码

    #客户端的代码

    vim client.c

    复制代码
    //client 
    #include <openssl/rand.h>
    #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>
    #include <errno.h>
    #include <curses.h>
    #define PORT 7979
    #define SERVER "127.0.0.1"
    #define CACERT "./private/ca.crt"
    /* 如果需要与不是本机的服务器通信,在这个地方的证书导入对应服务器的证书,我的是和java通信,所以就导入了java端的服务器证书
    注意,java端的证书需要进行格式转换,具体操作方法请参见开头的说明。*/
    #define MYCERTF "./certs/proxy.crt"
    #define MYKEYF "./private/proxy.key"
    #define MSGLENGTH 1024
    int
    main ()
    {
    struct sockaddr_in sin;
    int seed_int[100];
    SSL *ssl;
    SSL_METHOD *meth;
    SSL_CTX *ctx;
    int i;
     
    /* 初始化OpenSSL */
    SSL_library_init();
    /*加载算法库 */
    OpenSSL_add_ssl_algorithms ();
    /*加载错误处理信息 */
    SSL_load_error_strings ();
    /* 选择会话协议 */
    meth = (SSL_METHOD *) TLSv1_client_method ();
    /* 创建会话环境 */
    ctx = SSL_CTX_new (meth);
    if (NULL == ctx)
    exit (1);
    /* 制定证书验证方式 */
    SSL_CTX_set_verify (ctx, SSL_VERIFY_PEER, NULL);
     
    /* 为SSL会话加载CA证书*/
    SSL_CTX_load_verify_locations (ctx, CACERT, NULL);
    
    /* 为SSL会话加载用户证书 */
    if (0 == SSL_CTX_use_certificate_file (ctx, MYCERTF, SSL_FILETYPE_PEM))
    {
    ERR_print_errors_fp (stderr);
    exit (1);
    }
    /* 为SSL会话加载用户私钥 */
    if (0 == SSL_CTX_use_PrivateKey_file (ctx, MYKEYF, SSL_FILETYPE_PEM))
    {
    ERR_print_errors_fp (stderr);
    exit (1);
    }
    /* 验证私钥和证书是否相符 */
    if (!SSL_CTX_check_private_key (ctx))
    {
    printf ("Private key does not match the certificate public key
    ");
    exit (1);
    }
    /* 设置随机数 */
    srand ((unsigned) time (NULL));
    for (i = 0; i < 100; i++)
    seed_int[i] = rand ();
    RAND_seed (seed_int, sizeof (seed_int));
    /* 指定加密器类型 */
    SSL_CTX_set_cipher_list (ctx, "RC4-MD5");
    SSL_CTX_set_mode (ctx, SSL_MODE_AUTO_RETRY);
    int sock;
    printf ("Begin tcp socket...
    ");
    /* 创建socket描述符 */
    sock = socket (AF_INET, SOCK_STREAM, 0);
    if (sock == -1)
    {
    printf ("SOCKET error. 
    ");
    }
    memset (&sin, '', sizeof (sin));
     
    /* 准备通信地址和端口号 */
    sin.sin_family = AF_INET;
    sin.sin_addr.s_addr = inet_addr (SERVER); /* Server IP */
    sin.sin_port = htons (PORT); /* Server Port number */
    int icnn = connect (sock, (struct sockaddr *) &sin, sizeof (sin));
    if (icnn == -1)
    {
    printf ("can not connect to server,%s
    ", strerror (errno));
    exit (1);
    }
    
     
    /* 创建一个SSL套接字*/
    ssl = SSL_new (ctx);
    if (NULL == ssl)
    exit (1);
    
    /* 申请一个ssl套接字 */
    if (0 >= SSL_set_fd (ssl, sock))
    {
    printf ("Attach to Line fail!
    ");
    exit (1);
    }
    //建立SSL连接
    int k = SSL_connect (ssl);
    if (0 == k)
    {
    printf ("%d
    ", k);
    printf ("SSL connect fail!
    ");
    exit (1);
    }
    printf ("connect to server
    ");
    char sendmsg[MSGLENGTH] = "";
    char revmsg[MSGLENGTH] = "";
    //接收服务器消息
    int err = SSL_read (ssl, revmsg, sizeof (revmsg));
    revmsg[err] = '';
    printf ("%s
    ", revmsg);
    while (1)
    {
    printf ("please input the data to send:
    ");
    scanf ("%s", sendmsg);
    //向服务器发送消息
    SSL_write (ssl, sendmsg, strlen (sendmsg));
    printf ("send message ' %s ' success
    ", sendmsg);
    }
    //关闭连接
    SSL_shutdown (ssl);
    SSL_free (ssl);
    SSL_CTX_free (ctx);
    close (sock);
    getch ();
    return 0;
    }
    复制代码

    #服务端的代码

    vim server.c

    复制代码
    //server 
    #include <stdio.h>
    #include <openssl/x509.h>
    #include <openssl/ssl.h>
    #include <openssl/err.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>
    #include <curses.h>
    #define MSGLENGTH 1024
    #define PORT 7979
    #define CACERT "./private/ca.crt"
    #define SVRCERTF "./certs/server.crt"
    #define SVRKEYF "./private/server.key"
    #define ADDRESS “127.0.0.1”
    int main ()
    {
    int sock;
    SSL_METHOD *meth;
    SSL_CTX *ctx;
    SSL *ssl;
    //SSL初库始化
    SSL_library_init();
    //载入所有SSL算法
    OpenSSL_add_ssl_algorithms ();
    //载入所有错误信息
    SSL_load_error_strings ();
    meth = (SSL_METHOD *) TLSv1_server_method ();
    ctx = SSL_CTX_new (meth);
    if (NULL == ctx)
    exit (1);
    SSL_CTX_set_verify (ctx, SSL_VERIFY_PEER, NULL);
    SSL_CTX_load_verify_locations (ctx, CACERT, NULL);
    //加载证书和私钥
    if (0 == SSL_CTX_use_certificate_file (ctx, SVRCERTF, SSL_FILETYPE_PEM))
    {
    ERR_print_errors_fp (stderr);
    exit (1);
    }
    if (0 == SSL_CTX_use_PrivateKey_file (ctx, SVRKEYF, SSL_FILETYPE_PEM))
    {
    ERR_print_errors_fp (stderr);
    exit (1);
    }
    if (!SSL_CTX_check_private_key (ctx))
    {
    printf ("Private key does not match the certificate public key
    ");
    exit (1);
    }
    SSL_CTX_set_cipher_list (ctx, "RC4-MD5");
    SSL_CTX_set_mode (ctx, SSL_MODE_AUTO_RETRY);
    printf ("Begin tcp socket...
    ");
    sock = socket (AF_INET, SOCK_STREAM, 0);
    if (sock == -1)
    {
    printf ("SOCKET error! 
    ");
    return 0;
    }
    //准备通信地址和端口号
    struct sockaddr_in addr;
    memset (&addr, '', sizeof (addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons (PORT); /* Server Port number */
    //addr.sin_addr.s_addr = INADDR_ANY;
    addr.sin_addr.s_addr = inet_addr(ADDRESS);
    //绑定地址和端口号
    int nResult = bind (sock, (struct sockaddr *) &addr, sizeof (addr));
    if (nResult == -1)
    {
    printf ("bind socket error
    ");
    return 0;
    }
    printf ("server start successfully,port:%d
    waiting for connections
    ",
    PORT);
    struct sockaddr_in sa_cli;
    //设置最大连接数
    int err = listen (sock, 5);
    if (-1 == err)
    exit (1);
    int client_len = sizeof (sa_cli);
    //等待客户端连接
    int ss = accept (sock, (struct sockaddr *) &sa_cli, &client_len);
    if (ss == -1)
    {
    exit (1);
    }
    close (sock);
    printf ("Connection from %d, port %d
    ", sa_cli.sin_addr.s_addr,
    sa_cli.sin_port);
    ssl = SSL_new (ctx);
    if (NULL == ssl)
    exit (1);
    if (0 == SSL_set_fd (ssl, ss))
    {
    printf ("Attach to Line fail!
    ");
    exit (1);
    }
    int k = SSL_accept (ssl);
    if (0 == k)
    {
    printf ("%d/n", k);
    printf ("SSL connect fail!
    ");
    exit (1);
    }
    X509 *client_cert;
    client_cert = SSL_get_peer_certificate (ssl);
    printf ("find a customer to try to connect
    ");
    if (client_cert != NULL)
    {
    printf ("Client certificate:
    ");
    char *str =
    X509_NAME_oneline (X509_get_subject_name (client_cert), 0, 0);
    if (NULL == str)
    {
    printf ("auth error!
    ");
    exit (1);
    }
    printf ("subject: %s
    ", str);
    str = X509_NAME_oneline (X509_get_issuer_name (client_cert), 0, 0);
    if (NULL == str)
    {
    printf ("certificate name is null
    ");
    exit (1);
    }
    printf ("issuer: %s
    ", str);
    printf ("connect successfully
    ");
    X509_free (client_cert);
    OPENSSL_free (str);
    }
    else
    {
    printf ("can not find the customer's certificate
    ");
    exit (1);
    }
    char buf[MSGLENGTH];
    SSL_write (ssl, "Server is connect to you!
    ",
    strlen ("Server is connect to you!
    "));
    printf ("Listen to the client: 
    ");
    while (1)
    {
    err = SSL_read (ssl, buf, sizeof (buf));
    buf[err] = '';
    printf ("%s
    ", buf);
    }
    SSL_shutdown (ssl);
    SSL_free (ssl);
    SSL_CTX_free (ctx);
    getch ();
    return 0;
    }
    复制代码

    #makefile的代码:

    vim makefile

    复制代码
    all:client.c server.c
           gcc –o clientclient.c –Wall –lssl –ldl -lcurses
           gcc –o serverserver.c –Wall –lssl –ldl –lcurses
           #如果是自定义安装路径的,可以使用下面的
    #gcc -Wall -o clientclient.c -I/usr/openssl-1.0.0c/include/usr/openssl-1.0.0c/libssl.a /usr/openssl-1.0.0c/libcrypto.a –ldl
    #gcc -Wall -o serverserver.c -I/usr/openssl-1.0.0c/include/usr/openssl-1.0.0c/libssl.a /usr/openssl-1.0.0c/libcrypto.a -ldl
    clean::
    rm -f client server
    复制代码
  • 相关阅读:
    ASP.NET MVC 5 学习教程:使用 SQL Server LocalDB
    ASP.NET MVC 5 学习教程:生成的代码详解
    ASP.NET MVC 5 学习教程:通过控制器访问模型的数据
    ASP.NET MVC 5 学习教程:创建连接字符串
    ASP.NET MVC 5 学习教程:添加模型
    ASP.NET MVC 5 学习教程:控制器传递数据给视图
    ASP.NET MVC 5 学习教程:修改视图和布局页
    ASP.NET MVC 5 学习教程:添加视图
    ASP.NET MVC 5 学习教程:添加控制器
    ASP.NET MVC 5 学习教程:快速入门
  • 原文地址:https://www.cnblogs.com/LiuYanYGZ/p/10438861.html
Copyright © 2011-2022 走看看