因为在测试wss (WebSocket Secure)的时候,需要证书。因此参考网上的文章做了一个自己的server的证书。具体步骤如下,参考文章:
制作用于CA签名的证书
生成CA证书的私钥
$ openssl genrsa -des3 -passout pass:123456 -out ca.key 2048
Generating RSA private key, 2048 bit long modulus
................+++
..............................................................................................................+++
e is 65537 (0x10001)
-passout pass:123456
表示加密私钥文件时,传递密码是123456
。
genrsa
的详细说明见此文章。
生成一个证书请求文件:
$ openssl req -new -out ca.csr -key ca.key -passin pass:123456 -keyform PEM -subj "/C=CN/ST=SC/L=CD/O=CTP/OU=DEV/CN=0.0.0.0"
其中参数:
-new
表示新生成一个新的证书请求文件-out ca.csr
指定输出文件ca.csr
,此处输出文件即为证书请求文件-key ca.key
指定私钥文件ca.key
-passin pass:123456
是用户私钥的保护口令123456
-keyform PEM
表示编码格式为PEM
,可选DER
-subj
用于指定生成的证书请求的用户信息
注意:
CA证书的 subj 中的 CN 不要与后面签发的其他证书的 CN 内容相同,否则 OpenSSL 验证证书时无法通过
接下来生成CA 证书:
$ openssl x509 -req -days 3650 -in ca.csr -signkey ca.key -passin pass:123456 -CAcreateserial -out ca.crt
Signature ok
subject=/C=CN/ST=SC/L=CD/O=CTP/OU=DEV/CN=0.0.0.0
Getting Private key
参数说明:
-req
表示输入文件为证书请求文件-days 3650
表示证书有效期为3650
天,根证书的有效期一般为10年,普通证书有效期一般为1年-in ca.csr
输入文件ca.csr
可以是证书请求文件(-req
指定为证书请求文件),也可以是已签署过的证书-signkey ca.key
用于提供自签署时的私钥文件ca.key
-CAcreateserial
当使用该选项时,如果CA使用的序列号文件不存在将自动创建:该文件将包含序列号值"02"并且此次签名后证书文件序列号为1。
更多参数说明见此文章
更多:
上述步骤其实也可以浓缩成下面一步:$ openssl req -newkey rsa:2048 -nodes -keyout ca.key -x509 -days 3650 -out ca.crt -subj "/C=CN/ST=SC/L=CD/O=CTP/OU=DEV/CN=0.0.0.0" Generating a 2048 bit RSA private key ..............+++ ......................+++ writing new private key to 'ca.key' -----
参数说明:
-nodes
选项-nodes不是英文单词“nodes”,而是“no DES”。 当作为参数给出时,这意味着OpenSSL不会对 PKCS#12文件中的私钥做密码保护。
如果需要对密钥进行密码保护,可以将-nodes
用上面第一条命令的-passout pass:123456
代替。参考这篇文章。
用CA签名产生服务器证书
生成用于服务器证书的私钥
$ openssl genrsa -des3 -passout pass:123456 -out server.key 2048
Generating RSA private key, 2048 bit long modulus
......................+++
...............+++
e is 65537 (0x10001)
生成服务器使用的证书请求文件:
$ openssl req -new -key server.key -passin pass:123456 -out server.csr -subj "/C=CN/ST=SC/L=CD/O=CTP/OU=DEV/CN=127.0.0.1"
生成服务器使用的自签名SSL证书
$ openssl x509 -req -in server.csr -passin pass:123456 -out server.crt -signkey server.key -CA ca.crt -CAkey ca.key -days 365 -CAcreateserial
Signature ok
subject=/C=CN/ST=SC/L=CD/O=CTP/OU=DEV/CN=127.0.0.1
Getting Private key
Getting CA Private Key
参数说明:
-CA ca.crt
指定签署时所使用的CA证书ca.crt
-CAkey ca.key
设置CA签署时使用的私钥文件ca.key
。如果该选项没有指定,将假定CA私钥已经存在于CA自签名的证书文件中。
OpenSSL API 实现SSL连接代码
用于测试证书上述证书的代码如下:
ssl_common.h
//ssl_common.h
#include <stdio.h>
#include <openssl/crypto.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/rsa.h>
#include <openssl/x509.h>
inline
const char* SSL_error_string()
{
return ERR_error_string(ERR_get_error(), NULL);
}
inline
void ssl_init_lib()
{
SSL_library_init();
SSL_load_error_strings();
OpenSSL_add_all_algorithms();
}
inline
SSL* ssl_new(SSL_CTX* ctx, int socket)
{
SSL* ssl = SSL_new(ctx);
if(NULL == ssl)
printf("SSL_new return %s\n", SSL_error_string());
else
SSL_set_fd(ssl, socket);
return ssl;
}
inline
void ssl_free(SSL*& ssl)
{
SSL_shutdown(ssl);
SSL_free(ssl);
ssl = NULL;
}
inline
int ssl_set_verify(SSL_CTX* ctx, const char* root_certificate_path, const char* root_certificate_dir = NULL)
{
int r = 1;
if(root_certificate_path || root_certificate_dir)
{
if((r = SSL_CTX_load_verify_locations(ctx, root_certificate_path, root_certificate_dir)) <= 0)
{
printf("SSL_CTX_load_verify_locations return = %d %s\n", r, SSL_error_string());
return r;
}
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
}
else
{
SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL);
}
return r;
}
inline
int ssl_set_certificate_resource(SSL_CTX* ctx, const char* certificate_path, const char* private_key_path, const char* private_key_passwd)
{
int r;
if((r = SSL_CTX_use_certificate_file(ctx, certificate_path, SSL_FILETYPE_PEM)) <= 0)
{
printf("SSL_CTX_use_certificate_file return = %d %s\n", r, SSL_error_string());
return r;
}
SSL_CTX_set_default_passwd_cb_userdata(ctx, (void*)private_key_passwd);
if((r = SSL_CTX_use_PrivateKey_file(ctx, private_key_path, SSL_FILETYPE_PEM)) <= 0)
{
printf("SSL_CTX_use_PrivateKey_file return = %d %s\n", r, SSL_error_string());
return r;
}
if((r = SSL_CTX_check_private_key(ctx)) <= 0)
{
printf("SSL_CTX_check_private_key return = %d %s\n", r, SSL_error_string());
return r;
}
SSL_CTX_set_options(ctx, SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY);
return r;
}
server.cpp
//server.cpp
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/in.h>
#include "ssl_common.h"
const unsigned short port = 10001;
const char* const certificate_path = "./server.crt";
const char* const private_key_path = "./server.key";
const char* const private_key_passwd = "123456";
const char* const ca_path = "./ca.crt";
const char* const ret_str = "HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n";
int main(int argc, char* argv[])
{
ssl_init_lib();
const SSL_METHOD* method = TLSv1_2_method();
SSL_CTX* ctx = NULL;
SSL* ssl = NULL;
int r = -1;
int listen_socket = -1;
int client_socket = -1;
struct sockaddr_in server_addr;
struct sockaddr_in client_addr;
char buf[1024] = {0};
int client_len = 0;
do
{
ctx = SSL_CTX_new(method);
if(NULL == ctx)
{
printf("SSL_CTX_new return = NULL %s\n", SSL_error_string());
break;
}
ssl_set_verify(ctx, NULL, NULL);
ssl_set_certificate_resource(ctx, certificate_path, private_key_path, private_key_passwd);
//listen
listen_socket = socket(AF_INET, SOCK_STREAM, 0);
if(listen_socket < 0)
{
printf("socket error = %d\n", errno);
break;
}
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port);
if(bind(listen_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0)
{
printf("bind error = %d\n", errno);
break;
}
if(listen(listen_socket, 5) < 0)
{
printf("listen error = %d\n", errno);
break;
}
while(1)
{
//socket accept
client_len = sizeof(client_addr);
socklen_t len = client_len;
printf("start listen\n");
client_socket = accept(listen_socket, (struct sockaddr*)&client_addr, &len);
if(client_socket < 0)
{
printf("accept error = %d\n", errno);
break;
}
//SSL accept
ssl = ssl_new(ctx, client_socket);
if((r = SSL_accept(ssl)) <= 0)
{
printf("SSL_accept return %d, %s\n", r, SSL_error_string());
break;
}
//communication
r = SSL_read(ssl, buf, sizeof(buf));
if(r > 0)
{
printf("server recv text: %s\n", buf);
}
else
{
printf("SSL_read return %d, %s\n", r, SSL_error_string());
}
r = SSL_write(ssl, ret_str, strlen(ret_str));
if(r <= 0)
{
printf("SSL_write return %d, %s\n", r, SSL_error_string());
}
//shutdown
printf("close client socket\n");
ssl_free(ssl);
close(client_socket);
client_socket = 0;
}
}while(0);
printf("destroy resources\n");
if(ssl) { SSL_free(ssl); ssl = NULL; }
if(ctx) { SSL_CTX_free(ctx); ctx = NULL; }
if(listen_socket > 0) { close(listen_socket); listen_socket = 0; }
if(client_socket > 0) { close(client_socket); client_socket = 0; }
printf("server done\n");
return 0;
}
client.cpp
//client.cpp
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/in.h>
#include "ssl_common.h"
const char* const host_addr = "127.0.0.1";
const unsigned short port = 10001;
const char* const ca_path = "./ca.crt";
#define VIRIFY_SERVER_CA 1
int main(int argc, char* argv[])
{
ssl_init_lib();
const SSL_METHOD* method = SSLv23_method();
SSL_CTX* ctx = NULL;
SSL* ssl = NULL;
int r = -1;
int remote_socket = -1;
struct sockaddr_in remote_addr;
X509* x509_cert = NULL;
X509_NAME* x509_subject = NULL;
X509_NAME* x509_issuer = NULL;
char buf[1024] = {0};
char subject[1024] = {0};
char issuer[256] = {0};
int client_len = 0;
do
{
ctx = SSL_CTX_new(method);
if(NULL == ctx)
{
printf("SSL_CTX_new return = NULL %s\n", SSL_error_string());
break;
}
#if VIRIFY_SERVER_CA
ssl_set_verify(ctx, ca_path, NULL);
#endif
//socket connection
remote_socket = socket(AF_INET, SOCK_STREAM, 0);
if(remote_socket < 0)
{
printf("socket error = %d\n", errno);
break;
}
memset(&remote_addr, 0, sizeof(remote_addr));
remote_addr.sin_addr.s_addr = INADDR_ANY;
remote_addr.sin_family = AF_INET;
remote_addr.sin_port = htons(port);
if(connect(remote_socket, (struct sockaddr*)&remote_addr, sizeof(remote_addr)) < 0)
{
printf("connect error = %d\n", errno);
break;
}
{
char ret_str[256] = {0};
//SSL connection
ssl = ssl_new(ctx, remote_socket);
if((r = SSL_connect(ssl)) <= 0)
{
printf("SSL_connect return %d, %s\n", r, SSL_error_string());
break;
}
//check cerificate by application (not SSL handshake)
//not neccesary
#if VIRIFY_SERVER_CA
if(X509_V_OK != (r = SSL_get_verify_result(ssl)))
{
printf("SSL_get_verify_result return %d, %s\n", r, SSL_error_string());
break;
}
x509_cert = SSL_get_peer_certificate(ssl);
if(x509_cert == NULL)
{
printf("SSL_get_peer_certificate return NULL, %s\n", SSL_error_string());
break;
}
x509_subject = X509_get_subject_name(x509_cert);
if(x509_subject == NULL)
{
printf("X509_get_subject_name return NULL, %s\n", SSL_error_string());
break;
}
X509_NAME_oneline(x509_subject, subject, sizeof(subject)-1);
x509_issuer = X509_get_issuer_name(x509_cert);
if(x509_issuer == NULL)
{
printf("X509_get_issuer_name return NULL, %s\n", SSL_error_string());
break;
}
X509_NAME_oneline(x509_issuer, issuer, sizeof(issuer)-1);
X509_NAME_get_text_by_NID(x509_subject, NID_commonName, buf, sizeof(buf)-1);
printf("subject =%s \nissuer =%s\n commonName =%s\n",subject,issuer,buf);
#endif
//communication
strcpy(ret_str, "hello ssl");
SSL_write(ssl, ret_str, strlen(ret_str));
//shutdown
ssl_free(ssl);
close(remote_socket);
remote_socket = 0;
}
}while(0);
printf("destroy resources\n");
#if VIRIFY_SERVER_CA
if(x509_cert) { X509_free(x509_cert); x509_cert = NULL; }
#endif
if(ssl) { SSL_free(ssl); ssl = NULL; }
if(ctx) { SSL_CTX_free(ctx); ctx = NULL; }
if(remote_socket > 0) { close(remote_socket); remote_socket = 0; }
printf("done\n");
return 0;
}
CMakeLists.txt
cmake_minimum_required(VERSION 2.8)
set(project_name test)
project(${project_name})
SET(CMAKE_SKIP_BUILD_RPATH TRUE)
SET(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
SET(CMAKE_INSTALL_RPATH "./")
set(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_SKIP TRUE)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
if(WIN32)
set(SYS_LIBS ws2_32.lib CACHE INTERNAL "")
set(SSL_LIBS openssl32.lib libcrypto.lib CACHE INTERNAL "")
set(LIB_PARAM CACHE INTERNAL "")
else()
add_definitions(-std=c++11 -pipe -g -ggdb -fPIC)
include_directories(/usr/include)
set(SYS_LIBS pthread dl rt CACHE INTERNAL "")
set(SSL_LIBS ssl crypto CACHE INTERNAL "")
set(LIB_PARAM "-Wl," CACHE INTERNAL "")
endif()
add_executable(server server.cpp)
target_link_libraries(server
${SSL_LIBS}
${SYS_LIBS}
)
add_executable(client client.cpp)
target_link_libraries(client
${SSL_LIBS}
${SYS_LIBS}
)
测试通过。