1、gmssl组成
分为加密和通信两部分
2、加密
主要指的是sm2 sm3 sm4加密算法,以及相关的加密组件
3、通信
指的是gmtls
按照一个 GM/T 0024-2014规范实现的,采用双证书,签名证书+加密证书
4、生成证书
可使用地址https://github.com/jntass/TASSL/tree/master/Tassl_demo/mk_tls_cert 下的SM2certgen.sh生成双证书。
注意:生成请求时指定的摘要算法 在用请求生成证书时并不生效,需要重新指定,否则会使用默认的算法rsa-sha256
针对gmssl,签名算法一定要是sm2sign-with-sm3
针对tassl,只要加密用的是sm2即可,sm2sign-with-sm3不是必须
5、兼容性
指的是gmssl对openssl的兼容性
ssl/tls下,仅支持有限的套件:
ECDHE-SM2-WITH-SMS4-SM3
ECDHE-SM2-WITH-SMS4-SHA256
不是完全兼容的
采用老的openssl证书,要指定tls版本为1或1.2才可以,或者直接使用TSLv1_2_method,使用TSL_method不可以,号称的会自己检测版本并没有实现,貌似默认是使用1.1版本
6、双证书
gmssl对双证书和双密钥的设置
直接设置两个sm2证书和密钥就可以,没有新增接口,都是代码里自己适配:
keyusagedigitalSignature 类型的证书是签名证书,否则是加密证书,密钥呢,加密证书存在的时候是加密密钥,否则是签名密钥
这个其实是有漏洞的,必须先设置签名证书。。然后才是加密证书
tassl是有的,增加了一个设置加密密钥的接口SSL_use_enc_PrivateKey,设置证书的接口也是代码里适配的,证书类型需要keyusage(keyAgreementkeyEnciphermentdataEncipherment)
7、版本
以上总结仅针对GmSSL最新版(v2.3.1)和tassl当前最新版(2018-09-17)
8、最新版的openssl已经支持国密算法
仅仅支持国密的算法,通信还未支持
9、编程实现
server.c:
#include <stdio.h>
#include <string.h>
#include <openssl/evp.h>
#include <openssl/x509.h>
#include <openssl/ssl.h>
#include <openssl/pem.h>
#include <openssl/err.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
//#define CERTSERVER "/tmp/testopenssl/demoCA/cacert.pem"
//#define KEYSERVER "/tmp/testopenssl/demoCA/private/cakey.pem"
#define CERTSERVER "SS.pem"
#define KEYSERVER "SS.key.pem"
#define SM2_SERVER_ENC_CERT "SE.pem"
#define SM2_SERVER_ENC_KEY "SE.key.pem"
#define CHK_ERR(err, s) if((err) == -1) { perror(s); return -1; }else printf("%s success! ",s);
#define CHK_RV(rv, s) if((rv) != 1) { printf("%s error ", s); return -1; }else printf("%s success! ",s);
#define CHK_NULL(x, s) if((x) == NULL) { printf("%s error ", s); return -1; }else printf("%s success! ",s);
#define CHK_SSL(err, s) if((err) == -1) { ERR_print_errors_fp(stderr); return -1;}else printf("%s success! ",s);
int main()
{
int rv, err;
SSL_CTX *ctx = NULL;
SSL_METHOD *meth = NULL;
int listen_sd;
int accept_sd;
struct sockaddr_in socketAddrServer;
struct sockaddr_in socketAddrClient;
socklen_t socketAddrClientLen;
SSL *ssl = NULL;
char buf[4096];
rv = SSL_library_init();
CHK_RV(rv, "SSL_library_init");
meth = (SSL_METHOD *)GMTLS_server_method();
ctx = SSL_CTX_new(meth);
CHK_NULL(ctx, "SSL_CTX_new");
rv = SSL_CTX_use_certificate_file(ctx, CERTSERVER, SSL_FILETYPE_PEM);
CHK_RV(rv, "SSL_CTX_use_certicificate_file");
rv = SSL_CTX_use_PrivateKey_file(ctx, KEYSERVER, SSL_FILETYPE_PEM);
CHK_RV(rv, "SSL_CTX_use_PrivateKey_file");
rv = SSL_CTX_check_private_key(ctx);
CHK_RV(rv, "SSL_CTX_check_private_key");
rv = SSL_CTX_use_certificate_file(ctx, SM2_SERVER_ENC_CERT, SSL_FILETYPE_PEM);
CHK_RV(rv, "SSL_CTX_use_certicificate_file2");
rv = SSL_CTX_use_PrivateKey_file(ctx, SM2_SERVER_ENC_KEY, SSL_FILETYPE_PEM);
CHK_RV(rv, "SSL_CTX_use_PrivateKey_file2");
rv = SSL_CTX_check_private_key(ctx);
CHK_RV(rv, "SSL_CTX_check_private_key2");
SSL_CTX_set_security_level(ctx, 0);
listen_sd = socket(AF_INET, SOCK_STREAM, 0);
CHK_ERR(listen_sd, "socket");
memset(&socketAddrServer, 0, sizeof(socketAddrServer));
socketAddrServer.sin_family = AF_INET;
socketAddrServer.sin_port = htons(8443);
socketAddrServer.sin_addr.s_addr = INADDR_ANY;
err = bind(listen_sd, (struct sockaddr *)&socketAddrServer, sizeof(socketAddrServer));
CHK_ERR(err, "bind");
err = listen(listen_sd, 5);
CHK_ERR(err, "listen");
socketAddrClientLen = sizeof(socketAddrClient);
accept_sd = accept(listen_sd, (struct sockaddr *)&socketAddrClient, &socketAddrClientLen);
CHK_ERR(accept_sd, "accept");
close(listen_sd);
printf("Connect from %lx, port %x ", socketAddrClient.sin_addr.s_addr, socketAddrClient.sin_port);
ssl = SSL_new(ctx);
CHK_NULL(ssl, "SSL_new");
rv = SSL_set_fd(ssl, accept_sd);
CHK_RV(rv, "SSL_set_fd");
rv = SSL_accept(ssl);
CHK_RV(rv, "SSL_accpet");
/* Check for Client authentication error */
if (SSL_get_verify_result(ssl) != X509_V_OK) {
printf("SSL Client Authentication error ");
SSL_free(ssl);
close(accept_sd);
SSL_CTX_free(ctx);
exit(0);
}
/*Print out connection details*/
printf("SSL connection on socket %x,Version: %s, Cipher: %s ",
accept_sd,
SSL_get_version(ssl),
SSL_get_cipher(ssl));
rv = SSL_read(ssl, buf, sizeof(buf) - 1);
CHK_SSL(rv, "SSL_read");
buf[rv] = ' ';
printf("Got %d chars :%s ", rv, buf);
rv = SSL_write(ssl, "I accept your request", strlen("I accept your request"));
CHK_SSL(rv, "SSL_write");
close(accept_sd);
SSL_free(ssl);
SSL_CTX_free(ctx);
return 0;
}
client.c:
#include <stdio.h>
#include <string.h>
#include <openssl/evp.h>
#include <openssl/x509.h>
#include <openssl/ssl.h>
#include <openssl/pem.h>
#include <openssl/err.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <iostream>
#define SERVER_IP "127.0.0.1"
#define SERVER_PORT 8443
int main( int argc, char* argv[] ) {
int ret;
////////////
// 初始化 //
////////////
SSL_CTX* ctx;
SSL_METHOD *meth;
OpenSSL_add_ssl_algorithms();
SSL_load_error_strings();
//meth = (SSL_METHOD *)TLS_client_method();
meth = (SSL_METHOD *)GMTLS_client_method();
ctx = SSL_CTX_new (meth);
if (!ctx) {
ERR_print_errors_fp(stderr);
std::cout<<"SSL_CTX_new error."<<std::endl;
return -1;
}
//SSL_CTX_set_cipher_list(ctx, "ECDHE-RSA-AES256-GCM-SHA384");
// SSL_CTX_set_max_proto_version(ctx,TLS1_2_VERSION);
// SSL_CTX_set_min_proto_version(ctx,TLS1_2_VERSION);
///////////////////////
// 建立原始的TCP连接 //
///////////////////////
int client_socket;
struct sockaddr_in addr_server;
client_socket = socket (AF_INET, SOCK_STREAM, 0);
if( client_socket == -1 ) {
std::cout<<"socket error."<<std::endl;
return -1;
}
memset (&addr_server, 0, sizeof(addr_server));
addr_server.sin_family = AF_INET;
addr_server.sin_addr.s_addr = inet_addr(SERVER_IP);
addr_server.sin_port = htons (SERVER_PORT);
ret = connect(client_socket, (struct sockaddr*) &addr_server, sizeof(addr_server));
if( ret == -1 ) {
std::cout<<"connect error."<<std::endl;
return -1;
}
/////////////////////////////////////
// TCP连接已经建立,执行Client SSL //
/////////////////////////////////////
SSL* ssl;
X509* server_certificate;
char* str;
ssl = SSL_new (ctx);
if( ssl == NULL ) {
std::cout<<"SSL_new error."<<std::endl;
return -1;
}
SSL_set_fd (ssl, client_socket);
ret = SSL_connect (ssl);
if( ret == -1 ) {
std::cout<<"SSL_connect error."<<std::endl;
ERR_print_errors_fp(stderr);
return -1;
}
ERR_print_errors_fp(stderr);
// 接下来的获取密码和获取服务器端证书的两部是可选的,不会影响数据交换
// 获取cipher
std::cout<<"SSL connection using: "<<SSL_get_cipher(ssl)<<std::endl;
// 获取服务器端的证书
server_certificate = SSL_get_peer_certificate (ssl);
if( server_certificate != NULL ) {
std::cout<<"Server certificate:"<<std::endl;
str = X509_NAME_oneline (X509_get_subject_name (server_certificate),0,0);
if( str == NULL ) {
std::cout<<"X509_NAME_oneline error."<<std::endl;
} else {
std::cout<<"subject: "<<str<<std::endl;
OPENSSL_free (str);
}
str = X509_NAME_oneline (X509_get_issuer_name (server_certificate),0,0);
if( str == NULL ) {
std::cout<<"X509_NAME_oneline error."<<std::endl;
} else {
std::cout<<"issuer: "<<str<<std::endl;
OPENSSL_free (str);
}
X509_free (server_certificate);
} else {
std::cout<<"Server does not have certificate. we sould Esc!"<<std::endl;
return -1;
}
////////////////
// 数据交换 //
////////////////
char buf [4096];
ret = SSL_write (ssl, "Hello World!", strlen("Hello World!"));
if( ret == -1 ) {
std::cout<<"SSL_write error."<<std::endl;
return -1;
}
ret = SSL_read (ssl, buf, sizeof(buf) - 1);
if( ret == -1 ) {
std::cout<<"SSL_read error."<<std::endl;
return -1;
}
buf[ret] = ' ';
std::cout<<buf<<std::endl;
SSL_shutdown(ssl); /* send SSL/TLS close_notify */
/////////////
// Cleanup //
/////////////
close(client_socket);
SSL_free (ssl);
SSL_CTX_free (ctx);
return 0;
}
作者:高文茂
链接:https://www.jianshu.com/p/0dc9708e34de
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。