事前准备
相关:
pem、key:私钥文件,对数据进行加密解密
csr:证书签名请求文件,将其提交给证书颁发机构(ca、CA)对证书签名
crt:由证书颁发机构(ca、CA)签名后的证书或者自签名证书,该证书包含证书持有人的信息、持有人的公钥以及签署者的签名等信息
关于证书相关格式转换本文不做介绍
终端输入命令:vim /usr/local/openssl/ssl/openssl.cnf,根据自己具体的安装路径查找openssl的默认配置文件即可,部分内容如下图显示:
openssl默认配置部分截图
dir:默认的ssl工作目录,可以修改默认目录,这里是安装完默认的
certs:存放已经签发的证书
newcerts:存放CA新生成的证书
private:存放私钥
crl:存放已经吊销的证书
index.txt:已签发证书的文本数据库文件
serial:序列号存储文件,序列号为16进制数存储供证书签发使用序列号做参考
.rand:私有随机文件,
生成随机数命令:openssl rand -out xxx/.rand 1024
1024表示随机数长度
在生成证书的临时目录里创建默认配置目录文件命令,一键梭哈:mkdir -p ./demoCA/certs; mkdir -p ./demoCA/crl; mkdir ./democA/newcerts; mkdir -p ./demoCA/private; touch ./demoCA/index.txt; touch ./demoCA/serial; echo 01 > ./demoCA/serial;
创建自己生成证书的临时文件夹:mkdir ~/Desktop/openssl_ssl
进入文件夹:cd ~/Desktop/openssl_ssl,然后将之前提到过得【openssl.cnf】文件复制到此,开始制作证书用于之后的socket编程里
1. 制作ca根证书
1.1 生成ca根证书私钥
openssl genrsa -aes256 -out ca.key 2048
genrsa:使用RSA算法生成私钥
-aes256:使用256位密钥的AES算法对私钥进行加密
-out:输出文件路径
2048:私钥长度
输入完命令会让你输入两遍私钥文件的密码
1.2 生成ca根证书请求文件
openssl req -new -key ca.key -out ca.csr
req:执行证书签发命令
-new:新的证书签发请求
-key:指定私钥文件的路径
-out:csr文件的输出路径
输入完命令会让你输入ca根证书私钥的密码并填写一些信息,如下所示:
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
Country Name (2 letter code) [AU]:cn 【国家代码两个字母可为空,ca、server、client要一致】
State or Province Name (full name) [Some-State]:【省份,ca、server、client要一致】
Locality Name (eg, city) []:【城市】
Organization Name (eg, company) [Internet Widgits Pty Ltd]:【公司名,ca、server、client要一致】
Organizational Unit Name (eg, section) []:【组织名】
Common Name (e.g. server FQDN or YOUR name) []:【不可为空,全限定域名或名字】
Email Address []:【邮箱】Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:【输入密码】
An optional company name []:【可选的公司名】
上面输入的ca请求信息在后续申请使用该ca证书签名的请求证书要保证一致。
1.3自己作为ca机构签发根证书(自签发证书)
openssl x509 -req -days 365 -signkey ca.key -in ca.csr -out ca.crt
x509:用于自签名证书,生成x509格式的证书
-req:请求签名
-days:证书有效期
-signkey:证书签发的私钥
-in:证书请求文件,有效的文件路径
-out:ca签名后的证书输出路径
2. 制作server端证书
用刚才的ca证书为使用端签发证书
2.1 生成server端私钥
openssl genrsa -aes256 -out server.key 2048
连续输入两次密码
2.2 生成server端证书请求文件
openssl req -new -key server.key -out server.csr
同样的会让你输入刚才私钥的密码,并填写和ca根证书请求文件一样的信息,两者要保持一样
2.3 使用ca根证书为server端签发证书
openssl ca -in server.csr -out server.crt -cert ca.crt -keyfile ca.key -config openssl.cnf
-in:输入证书签名请求文件
-out:签名后的证书输出路径
-cert:ca根证书
-keyfile:ca根证书私钥文件
-config:配置文件
3 制作client端证书
套路和server端基本是一样的,下面就直接上命令了
3.1 生成client端私钥
openssl genrsa -aes256 -out client.key 2048
3.2 生成client端证书请求文件
openssl req -new -key client.key-out client.csr
3.3 使用ca根证书为client端签发证书
openssl ca -in client.csr -out client.crt -cert ca.crt -keyfile ca.key -config openssl.cnf
文件变化:1. index.txt本地数据库文本文件多了两条信息、2. serial序列号文件的序列号递增了、3. newcerts文件夹多了两个证书、4. 其它...
到这里我们就可以结束了,我们已经得到了ca.crt、server.key、server.crt、client.key、client.crt,我们就可以开始编程了。
双向认证要保证server、client端证书签名的ca要一样或者期限没有过期否则会认证失败
【下一篇:openssl 自签名证书 - socket编程应用(三)】
4. 导出证书
4.1 导出server端证书
导出时会让你输入server端私钥的密码,即server-key.pem的密码,验证通过后再输入两次证书导出的密码openssl pkcs12 -export -clcerts -name server-p12 -inkey server.key -in server.crt -out server-p12.keystore
4.2 导出client端证书
导出同server操作是一样的,先输入client-key.pem的密码,在输入两次证书导出的密码openssl pkcs12 -export -clcerts -name client-p12 -inkey client.key -in client.crt -out client-p12.keystore
4.3 导出受信任的证书
keytool -importcert -trustcacerts -alias localhost -file ca-crt.cer -keystore ca-trust.keystore
终端显示:
输入密钥库口令:
再次输入新口令:
所有者: EMAILADDRESS=【邮箱地址】, CN=127.0.0.1, OU=【组织名】, O=【公司】, L=【市】, ST=【省份】, C=cn【国家代码】
发布者: EMAILADDRESS=【邮箱地址】, CN=127.0.0.1, OU=【组织名】, O=【公司名】, L=【市】, ST=【省份】, C=cn【国家代码】
序列号: 4272b0ce733*************316f20cfe5f6a182
生效时间: Mon Jan 28 11:12:17 CST 2019, 失效时间: Tue Jan 28 11:12:17 CST 2020
证书指纹:
SHA1: C1:B6:C6:B3:95:8F:3C:12:B8:C4:AB:5E:8B:0D:03:6B:6D:B2:1E:D3
SHA256: 31:BF:B7:EA:F3:72:E9:28:95:55:41:76:F0:7B:24:19:2B:FF:C7:C4:AA:AE:63:EF:6C:98:69:5A:49:37:56:3F
签名算法名称: SHA256withRSA
主体公共密钥算法: 2048 位 RSA 密钥
版本: 1
是否信任此证书? [否]: y【终端输入y/n】
证书已添加到密钥库中
现在就可以使用ca证书,server端证书和私钥,client端证书和私钥开始socket编程了...
作者:码掺和_夏尼
链接:https://www.jianshu.com/p/37ded4da1095
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
用xcode创建应用工程
1. 确认头文件搜索目录
1.根据openssl编译设置目录,比如说/usr/local/openssl/include
2.我使用homebrew安装的openssl,目录一般为/usr/local/opt/openssl/include
2.工程设置头文件搜索
左边点击project -> 中间target -> 右边Build Settings选项卡 -> Header Search Paths,双击添加路径
头文件搜索设置
3. 静态库设置
- 将编译出来的【libcrypto.a】【libssl.a】复制工程目录然后添加进来
- 我是homebrew安装的,复制【libcrypto.a】【libssl.a】到工程目录然后添加进来,“什么?我去哪里找?!”,一般是在/usr/local/opt/openssl/lib/目录下
4. 上代码
#include <stdio.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <string.h>
#include <unistd.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#define SSL_ROOT_DIR "your path here..."
#define SSL_CA_PATH SSL_ROOT_DIR "ca.crt"
#define SSL_SERVER_CER_PATH SSL_ROOT_DIR "server.crt"
#define SSL_SERVER_KEY_PATH SSL_ROOT_DIR "server.key"
#define SSL_CLIENT_CER_PATH SSL_ROOT_DIR "client.crt"
#define SSL_CLIENT_KEY_PATH SSL_ROOT_DIR "client.key"
#ifndef SSL_VERIFY
#define SSL_VERIFY 1
#endif
#if SSL_VERIFY
#define SSL_VERIFY_MODE SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT
#else
#define SSL_VERIFY_MODE SSL_VERIFY_NONE
#endif
int g_ssl_connect_flag = 0;
int g_ssl_server_close = 0;
int g_ssl_client_close = 0;
void ssl_show_certs(SSL * ssl, const char *label)
{
X509 *cert;
char *line;
cert = SSL_get_peer_certificate(ssl);
// SSL_get_verify_result(), really verify
printf(">>> %s\n", label);
if(SSL_get_verify_result(ssl) == X509_V_OK){
printf("ssl verify ok.\n");
}
if(NULL != cert) {
line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
printf("certificate: %s\n", line);
free(line);
line = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
printf("ca: %s\n", line);
free(line);
X509_free(cert);
} else{
printf("no certificate info.\n");
}
}
void ssl_socket_server(void){
SSL_CTX *ctx = NULL;
int sock = 0;
ctx = SSL_CTX_new(SSLv23_server_method());
if(NULL == ctx){
fprintf(stderr, "server create ssl context fail.\n");
ERR_print_errors_fp(stderr);
return;
}
do{
SSL_CTX_set_verify(ctx, SSL_VERIFY_MODE, NULL);
if(0 >= SSL_CTX_load_verify_locations(ctx, SSL_CA_PATH, NULL)){
ERR_print_errors_fp(stderr);
fprintf(stderr, "server load ca file fail.\n");
ERR_print_errors_fp(stderr);
break;
}
if(0 >= SSL_CTX_use_certificate_chain_file(ctx, SSL_SERVER_CER_PATH)){
fprintf(stderr, "server load cer file fail.\n");
ERR_print_errors_fp(stderr);
break;
}
/// set private key password if needed
SSL_CTX_set_default_passwd_cb_userdata(ctx, "123456");
if(0 >= SSL_CTX_use_PrivateKey_file(ctx, SSL_SERVER_KEY_PATH, SSL_FILETYPE_PEM)){
fprintf(stderr, "server load private key file fail.\n");
ERR_print_errors_fp(stderr);
break;
}
if(0 >= SSL_CTX_check_private_key(ctx)){
fprintf(stderr, "check server private key fail.\n");
ERR_print_errors_fp(stderr);
break;
}
sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
struct sockaddr_in s_addr;
memset(&s_addr, 0, sizeof(s_addr));
s_addr.sin_family = AF_INET;
s_addr.sin_port = htons(10086);
s_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
if(0 != bind(sock, (const struct sockaddr*)&s_addr, sizeof(s_addr))){
fprintf(stderr, "server bind fail.\n");
break;
}
if(0 != listen(sock, 1)){
fprintf(stderr, "server lisent fail.\n");
break;
}
struct sockaddr_in s_addr_out;
int s_add_len = 0;
int new_sock = 0;
memset(&s_addr_out, 0x00, sizeof(s_addr_out));
g_ssl_connect_flag = 1;
SSL *ssl = NULL;
printf("server waiting...\n");
char msg[256] = {0};
while(1){
s_add_len = sizeof(s_addr_out);
new_sock = accept(sock, (struct sockaddr*)&s_addr_out, (socklen_t*)&s_add_len);
if(0 >= new_sock){
continue;
}
ssl = SSL_new(ctx);
SSL_set_fd(ssl, new_sock);
if(1 != SSL_accept(ssl)){
fprintf(stderr, "server accept ssl socket fail.\n");
ERR_print_errors_fp(stderr);
SSL_free(ssl);
close(new_sock);
continue;
}
#if SSL_VERIFY
ssl_show_certs(ssl, "serever side:");
#endif
memset(msg, 0x00, 256);
int sl = sprintf(msg, "hello client<%d> from <%s:%d>\n", new_sock, inet_ntoa(s_addr_out.sin_addr), ntohs(s_addr_out.sin_port));
int wl = SSL_write(ssl, msg, sl);
printf("server send %d bytes data.\n", wl);
memset(msg, 0x00, 256);
int rl = SSL_read(ssl, msg, 256);
printf("server recv %d bytes data:\ncontent: %s\n", rl, msg);
g_ssl_client_close = 1;
while(1 != g_ssl_server_close){
usleep(200000);
}
SSL_shutdown(ssl);
SSL_free(ssl);
close(new_sock);
printf("server close...\n");
break;
}
}while(0);
shutdown(sock, 2);
close(sock);
SSL_CTX_free(ctx);
}
void* ssl_client_thread_(void *param){
int sock = 0;
SSL *ssl = NULL;
SSL_CTX *ctx = SSL_CTX_new(SSLv23_client_method());
if(NULL == ctx){
fprintf(stderr, "client create ssl context fail.\n");
ERR_print_errors_fp(stderr);
return NULL;
}
do{
do{
usleep(200000);
}while(1 != g_ssl_connect_flag);
usleep(1000000);
SSL_CTX_set_verify(ctx, SSL_VERIFY_MODE, NULL);
if(0 >= SSL_CTX_load_verify_locations(ctx, SSL_CA_PATH, NULL)){
fprintf(stderr, "client load ca file fail.\n");
ERR_print_errors_fp(stderr);
break;
}
if(0 >= SSL_CTX_use_certificate_chain_file(ctx, SSL_CLIENT_CER_PATH)){
fprintf(stderr, "client load cer file fail.\n");
ERR_print_errors_fp(stderr);
break;
}
/// set private key password if needed
SSL_CTX_set_default_passwd_cb_userdata(ctx, "123456");
if(0 >= SSL_CTX_use_PrivateKey_file(ctx, SSL_CLIENT_KEY_PATH, SSL_FILETYPE_PEM)){
fprintf(stderr, "client load private key file fail.\n");
ERR_print_errors_fp(stderr);
break;
}
if(0 >= SSL_CTX_check_private_key(ctx)){
fprintf(stderr, "check client private key fail.\n");
ERR_print_errors_fp(stderr);
break;
}
sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
struct sockaddr_in s_addr;
memset(&s_addr, 0x00, sizeof(s_addr));
s_addr.sin_family = AF_INET;
s_addr.sin_port = htons(10086);
s_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
if(0 != connect(sock, (const struct sockaddr *)&s_addr, sizeof(s_addr))){
printf("client connect server fail.\n");
break;
}
ssl = SSL_new(ctx);
SSL_set_fd(ssl, sock);
if(1 != SSL_connect(ssl)){
fprintf(stderr, "client ssl connect fail.\n");
ERR_print_errors_fp(stderr);
break;
}
#if SSL_VERIFY
ssl_show_certs(ssl, "client side:");
#endif
char msg[256] = {0};
int rl = SSL_read(ssl, msg, 256);
printf("client recv %d bytes data:\ncontent: %s\n", rl, msg);
memset(msg, 0x00, 256);
const char *m = "hello ssl server!!";
int sl = (int)strlen(m);
int wl = SSL_write(ssl, m, sl);
printf("client send %d bytes data.\n", wl);
while(1 != g_ssl_client_close){
usleep(200000);
}
}while(0);
if(NULL != ssl){
SSL_shutdown(ssl);
SSL_free(ssl);
}
close(sock);
SSL_CTX_free(ctx);
g_ssl_server_close = 1;
printf("client close...\n");
return NULL;
}
void ssl_socket_client(void){
pthread_t pt;
if(0 > pthread_create(&pt, NULL, ssl_client_thread_, NULL)){
printf("client create socket thread fail.\n");
return;
}
}
void ssl_socket_test(void){
printf("ca path: %s\nser-req: %s\nser-key: %s\nclt-req: %s\nclt-key: %s\n", SSL_CA_PATH, SSL_SERVER_CER_PATH, SSL_SERVER_KEY_PATH, SSL_CLIENT_CER_PATH, SSL_CLIENT_KEY_PATH );
SSL_library_init();
OpenSSL_add_all_algorithms();
SSL_load_error_strings();
ssl_socket_client();
ssl_socket_server();
}
int main(int argc, const char * argv[]) {
// insert code here...
printf("Hello, World!\n");
ssl_socket_test();
printf("press any key to exit...\n");
getchar();
return 0;
}
控制台主要信息输出,如下:
Hello, World!
...
server waiting...
...
server send 39 bytes data.
...
client recv 39 bytes data:
content: hello client<5> from <127.0.0.1:53269>client send 18 bytes data.
server recv 18 bytes data:
content: hello ssl server!!
client close...
server close...
press any key to exit...
作者:码掺和_夏尼
链接:https://www.jianshu.com/p/bba9c47a0515
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。