前几天组长让我实现一个使用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, '