这个DEMO是JAVA通过JNI的方式调用C/C++对国密库的封装,然后访问LINUX C语言编写的国密服务端
- 需要准备的工具
1. 江南天安国密库:https://github.com/jntass/TASSL (以天安国密库为例)
2. 下载并安装ActivePerl。 Download & Install Perl - ActiveState
3. 下载并安装nasm汇编解释器 http://www.nasm.us/
4. 安装visual studio2019 企业版或者专业版,(建议使用高版本 >= 2017)
注:编译Linux下的国密库就简单很多,只要有gcc/make工具就可以。
- 环境变量配置
- 编译国密库
- 安装完VS2019之后,找到VS2019安装文件下面的CLI
如果需要编译64位的需要选x64_native
记得一定要用管理员身份打开
- 打开后并切换到下载国密库的路径
输入:
perl Configure VC-WIN64A no-asm --prefix=”E:\gmssl-test”
或者perl Configure VC-WIN32 --prefix=”E:\gmssl-test”
出现“OpenSSL has been successfully configured”字样后
输入:nmake (如果先前有编译失败先执行nmake clean)
(比较漫长编译过程。。。。。。。。。。。。。编译过程中可能会遇到一些奇怪的小错误,百度基本都有解决方案)
输入:nmake install
编译的结果
- JAVA JNI 调用 windows 动态库
- 先建立java工程,然后编写java native方法
2. 打开cmd,然后切换到工程目录下
执行命令:
javah -classpath D:\java-ws\gmtlstest-ws\CopGmssl\src -jni com.cop.gmssl.CopGmsslClient
头文件中的函数就是我们要C/C++实现的接口
3. 在visual studio 2019 专业/企业版中新建C++解决方案
4. Cpp实现
com_cop_gmssl_CopGmsslClient.h
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_cop_gmssl_CopGmsslClient */
#ifndef _Included_com_cop_gmssl_CopGmsslClient
#define _Included_com_cop_gmssl_CopGmsslClient
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_cop_gmssl_CopGmsslClient
* Method: Connect
* Signature: (Ljava/lang/String;)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_cop_gmssl_CopGmsslClient_Connect
(JNIEnv *, jobject, jstring);
/*
* Class: com_cop_gmssl_CopGmsslClient
* Method: Recv
* Signature: (Ljava/lang/String;)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_cop_gmssl_CopGmsslClient_Recv
(JNIEnv *, jobject, jstring);
/*
* Class: com_cop_gmssl_CopGmsslClient
* Method: Send
* Signature: (Ljava/lang/String;Ljava/lang/String;)V
*/
JNIEXPORT void JNICALL Java_com_cop_gmssl_CopGmsslClient_Send
(JNIEnv *, jobject, jstring, jstring);
/*
* Class: com_cop_gmssl_CopGmsslClient
* Method: Close
* Signature: (Ljava/lang/String;)V
*/
JNIEXPORT void JNICALL Java_com_cop_gmssl_CopGmsslClient_Close
(JNIEnv *, jobject, jstring);
#ifdef __cplusplus
}
#endif
#endif
com_cop_gmssl_CopGmsslClient.cpp
#include "com_cop_gmssl_CopGmsslClient.h"
#include "gmssljni.h"
#include <jni.h>
#include <iostream>
#include <string>
#include <sstream>
template <class Type> Type stringToNum(std::string& str)
{
std::istringstream iss(str);
Type num;
iss >> num;
return num;
}
std::string jstring2string(JNIEnv* env, jstring jstr)
{
const char* tmpStr = env->GetStringUTFChars(jstr, nullptr);
std::string ret(tmpStr);
env->ReleaseStringUTFChars(jstr, tmpStr);
return ret;
}
jstring string2jstring(JNIEnv* env, std::string str)
{
return env->NewStringUTF(str.c_str());
}
jstring chararray2jstring(JNIEnv* env, char * str)
{
return env->NewStringUTF(str);
}
JNIEXPORT jstring JNICALL Java_com_cop_gmssl_CopGmsslClient_Connect(JNIEnv* env, jobject jobj, jstring jstr)
{
const char* phost = env->GetStringUTFChars(jstr,nullptr);
printf("host:%s\n",phost);
CopGmSslClient *cli = new CopGmSslClient();
int ret = cli->OpenTcpSslClient(phost);
if (ret < 0){
printf("Connect error\n");
return NULL;
}
uint64_t uaddr = (uint64_t)(cli);
// printf("uaddr:%I64x\n", uaddr);
std::string saddr(std::to_string(uaddr));
jstring retval = string2jstring(env,saddr);
return retval;
}
JNIEXPORT jstring JNICALL Java_com_cop_gmssl_CopGmsslClient_Recv(JNIEnv* env, jobject jobj, jstring jstr)
{
std::string saddr = jstring2string(env,jstr);
uint64_t uaddr = stringToNum<uint64_t>(saddr);
CopGmSslClient* cli = (CopGmSslClient*)uaddr;
char buff[10240];
cli->Recv(buff,sizeof(buff));
return chararray2jstring(env,buff);
}
JNIEXPORT void JNICALL Java_com_cop_gmssl_CopGmsslClient_Send(JNIEnv* env, jobject jobj, jstring jbuff,jstring jaddr)
{
std::string saddr = jstring2string(env, jaddr);
// printf("handle:%I64x\n", stringToNum<uint64_t>(saddr));
uint64_t uaddr = stringToNum<uint64_t>(saddr);
CopGmSslClient* cli = (CopGmSslClient*)uaddr;
std::string buff = jstring2string(env, jbuff);
size_t slen = strlen(buff.c_str());
cli->Send(buff.c_str(), slen);
return;
}
JNIEXPORT void JNICALL Java_com_cop_gmssl_CopGmsslClient_Close(JNIEnv* env, jobject jobj, jstring jstr)
{
std::string saddr = jstring2string(env, jstr);
uint64_t uaddr = stringToNum<uint64_t>(saddr);
CopGmSslClient* cli = (CopGmSslClient*)uaddr;
cli->CloseTcpSslClient();
delete cli;
return;
}
gmssljni.h
#ifndef _GMSSL_JNI_H_
#define _GMSSL_JNI_H_
extern "C" {
#include <windows.h>
#include <openssl/crypto.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/engine.h>
}
#ifdef _WINDOWS_
class CopGmSslClient {
public:
CopGmSslClient();
void ShowCerts();
int LoadSignCertAndKey();
int LoadEncCertAndKey();
int LoadCaCert();
int VerifyPeer();
int OpenTcpSslClient(const char* host);
size_t Recv(char* buf, size_t len);
size_t Send(const char* buf, size_t len);
int CloseTcpSslClient();
~CopGmSslClient();
private:
SSL* ssl;
SSL_CTX* ctx;
ENGINE* engine;
char sign_cert[2048];
char sign_key[2048];
char enc_cert[2048];
char enc_key[2048];
char ca_cert[2048];
char ca_path[2048];
};
#endif
#endif
gmssljni.cpp
extern "C"{
#include <windows.h>
#include <openssl/crypto.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/engine.h>
}
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include "gmssljni.h"
static unsigned char* PSK_ID = NULL;
static unsigned char PSK[256] = { 0 };
static size_t PSK_LEN = 0;
int DTLS = 0;
#include <iostream>
extern "C" {
int psk_cb(SSL* ssl, const EVP_MD* md,
const unsigned char** id,
size_t* idlen,
SSL_SESSION** sess)
{
SSL_SESSION* session = NULL;
const SSL_CIPHER* cipher = NULL;
if (!PSK_ID)
goto error;
*id = PSK_ID;
*idlen = strlen((char*)PSK_ID);
/* PSK Hash algorithm MUST compatible with cipher suite
* 0x1301 - TLS_AES_128_GCM_SHA256
* 0x1302 - TLS_AES_256_GCM_SHA384
* 0x00C6 - TLS_SM4_GCM_SM3
* 0x00C7 - TLS_SM4_CCM_SM3 */
cipher = SSL_CIPHER_find(ssl, (unsigned char *)"\x00\xC6");
if (md != NULL && md != SSL_CIPHER_get_handshake_digest(cipher))
goto error;
session = SSL_SESSION_new();
if (session == NULL
|| !SSL_SESSION_set1_master_key(session, PSK,
PSK_LEN)
|| !SSL_SESSION_set_cipher(session, cipher)
|| !SSL_SESSION_set_protocol_version(session, TLS1_3_VERSION))
goto error;
*sess = session; session = NULL;
error:
if (session) SSL_SESSION_free(session);
return 1;
}
}
CopGmSslClient::CopGmSslClient()
{
memset(ca_cert, 0x00, sizeof(ca_cert));
memset(ca_path, 0x00, sizeof(ca_path));
memset(this->enc_cert, 0x00, sizeof(enc_cert));
memset(this->enc_key, 0x00, sizeof(enc_key));
this->engine = nullptr;
this->ctx = nullptr;
}
void CopGmSslClient::ShowCerts()
{
X509* cert;
char* line;
cert = SSL_get_peer_certificate(ssl);
if (cert != NULL) {
std::cout << "对端证书信息:" << std::endl;
line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
std::cout << "证书: "<<line<< std::endl;
OPENSSL_free(line);
line = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
std::cout << "颁发者: " << line << std::endl;
OPENSSL_free(line);
X509_free(cert);
}
else
std::cout<<"无对端证书信息!"<<std::endl;
}
int CopGmSslClient::LoadCaCert()
{
int err = 0;
if ((NULL != ca_cert && 0 != ca_cert[0]) || (NULL != ca_path && 0 != ca_path[0]))
{
/* Load the CA certificate into the SSL_CTX structure */
err = SSL_CTX_load_verify_locations(ctx, ca_cert, ca_path);
if (err != 1) {
ERR_print_errors_fp(stderr);
return -1;
}
}
return 0;
}
int CopGmSslClient::LoadSignCertAndKey()
{
int err = 0;
EVP_PKEY* pkey = NULL;
BIO* in = NULL;
/* Load sign cert and sign key */
if ((NULL != sign_cert && 0 != sign_cert[0]) && (NULL != sign_key && 0 != sign_key[0]))
{
/* Load the sign certificate into the SSL_CTX structure */
err = SSL_CTX_use_certificate_file(ctx, sign_cert, SSL_FILETYPE_PEM);
if (err != 1) {
ERR_print_errors_fp(stderr);
return -1;
}
in = BIO_new(BIO_s_file());
if (in == NULL) {
ERR_print_errors_fp(stderr);
return -1;
}
err = BIO_read_filename(in, sign_key);
if (err != 1) {
ERR_print_errors_fp(stderr);
return -1;
}
pkey = PEM_read_bio_PrivateKey(in, NULL, NULL, NULL);
if (pkey == NULL) {
ERR_print_errors_fp(stderr);
return -1;
}
BIO_free(in);
/* Use the private-key corresponding to the sign certificate */
err = SSL_CTX_use_PrivateKey(ctx, pkey);
if (err != 1) {
ERR_print_errors_fp(stderr);
return -1;
}
/* Check if the certificate and private-key matches */
err = SSL_CTX_check_private_key(ctx);
if (err != 1) {
ERR_print_errors_fp(stderr);
return -1;
}
EVP_PKEY_free(pkey);
}
return 0;
}
int CopGmSslClient::LoadEncCertAndKey()
{
int err = 0;
EVP_PKEY* pkey = NULL;
BIO* in = NULL;
if ((NULL != enc_cert && 0 != enc_cert[0]) && (NULL != enc_key && 0 != enc_key[0]))
{
/* Load the encrypt certificate into the SSL_CTX structure */
err = SSL_CTX_use_certificate_file(ctx, enc_cert, SSL_FILETYPE_PEM);
if (err != 1) {
ERR_print_errors_fp(stderr);
return -1;
}
in = BIO_new(BIO_s_file());
if (in == NULL) {
ERR_print_errors_fp(stderr);
return -1;
}
err = BIO_read_filename(in, enc_key);
if (err != 1) {
ERR_print_errors_fp(stderr);
return -1;
}
pkey = PEM_read_bio_PrivateKey(in, NULL, NULL, NULL);
if (pkey == NULL) {
ERR_print_errors_fp(stderr);
return -1;
}
BIO_free(in);
/* Use the private-key corresponding to the encrypt certificate */
err = SSL_CTX_use_PrivateKey(ctx, pkey);
if (err != 1) {
ERR_print_errors_fp(stderr);
return -1;
}
/* Check if the encrypt certificate and private-key matches */
err = SSL_CTX_check_private_key(ctx);
if (err != 1) {
ERR_print_errors_fp(stderr);
return -1;
}
EVP_PKEY_free(pkey);
}
return 0;
}
int CopGmSslClient::VerifyPeer()
{
/* Set to verify peer certificate */
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
/* Set the verification depth to 1 */
SSL_CTX_set_verify_depth(ctx, 1);
return 0;
}
int CopGmSslClient::OpenTcpSslClient(const char* host)
{
int err;
int verify_peer = 0; /* To verify peer certificate, set ON */
int gmssl = 0; /* Use gmssl, set ON */
int sock = -1;
char* engine_name = NULL;
BIO* bio = NULL;
BIO_ADDR* peer = NULL;
char ip[32] = { 0 };
unsigned char* psk = NULL;
long psk_len = 0;
/* Load encryption & hashing algorithms for the SSL program */
SSL_library_init();
/* Load the error strings for SSL & CRYPTO APIs */
SSL_load_error_strings();
//we use GMTLSv1.1
ctx = SSL_CTX_new(CNTLS_client_method());
if (ctx == NULL) {
ERR_print_errors_fp(stderr);
goto error;
}
LoadCaCert();
LoadSignCertAndKey();
LoadEncCertAndKey();
/* TLS use TCP, Connect to address:port */
bio = BIO_new_connect(host);
if (bio == NULL) {
ERR_print_errors_fp(stderr);
goto error;
}
err = BIO_do_connect(bio);
if (err != 1) {
ERR_print_errors_fp(stderr);
goto error;
}
SSL_CTX_set_psk_use_session_callback(ctx, psk_cb);
ssl = SSL_new(ctx);
if (ssl == NULL) {
ERR_print_errors_fp(stderr);
goto error;
}
SSL_set_bio(ssl, bio, bio);
err = SSL_connect(ssl);
if (err != 1) {
ERR_print_errors_fp(stderr);
goto error;
}
/* Informational output (optional) */
fprintf(stdout, "SSL connection using %s, %s\n", SSL_get_version(ssl), SSL_get_cipher(ssl));
ShowCerts();
return 0;
error:
return -1;
}
/***
SSL_read有一个大坑在里面,要注意以下两点
一是ssl3_read_bytes,如果record中没有数据,才会触发从record中读取数据,此处的rr即为SSL *s中存储的最近一次的record的内容和偏移量,
然后会从record中拷贝数据,如果预期读取数据n大于record的长度,则只会读取record中的长度。
如果预期读取的数据n不到record的长度,则会读取n字节,然后调整record记录中已经读取的偏移off。下一次会从off处开始读取。
二是ssl3_get_record,该函数从socket中读取实际的数据,并执行解密和鉴权的操作,然后得到一条完整的record数据,从代码流程得知一次至多只会读取一条record记录。
总结下来就是:SSL_read每次只读一个ssl记录,这个记录可能比我们的指定字节数小,也可能大。
SSL_pending为您提供SSL_frame中尚未由SSL_read返回的部分。也就是一个ssl_record中未被读取的记录,这些未被读取的记录可能是另一个业务包的数据,
所以慎重使用ssl_pending
***/
size_t CopGmSslClient::Recv(char *buf, size_t len)
{
int bytes = 0, size = len;
/*
while (len > 0) {
bytes = SSL_read(ssl, buf, len);
if (bytes <= 0)
return bytes;
buf += bytes;
len -= bytes;
}
*/
bytes = SSL_read(ssl, buf, len);
return (size - len);
}
/***
一个ssl拥有一个SSL3_BUFFER类型的结构体(v3):
typedef struct ssl3_buffer_st {
unsigned char *buf; at least SSL3_RT_MAX_PACKET_SIZE bytes,
size_t len; buffer size
int offset; where to 'copy from'
int left; how many bytes left
} SSL3_BUFFER;
在一个SSL句柄的底层实现中read和write共用一个缓冲区,所以务必注意线程安全的问题。SSL_read/write本身并不支持线程安全,需应用层来控制
***/
size_t CopGmSslClient::Send(const char* buf, size_t len)
{
int bytes = 0, size = len;
/*
while(len > 0){
bytes = SSL_write(ssl, buf, len);
if (bytes <= 0)
return bytes;
buf += bytes;
len -= bytes;
}
*/
bytes = SSL_write(ssl, buf, len);
return (size - len);
}
int CopGmSslClient::CloseTcpSslClient()
{
if (engine != nullptr) {
ENGINE_finish(engine);
ENGINE_free(engine);
}
engine = nullptr;
if (ssl != nullptr) {
SSL_shutdown(ssl);
SSL_free(ssl);
}
ssl = nullptr;
/* Free the SSL_CTX structure */
if (ctx != nullptr)
SSL_CTX_free(ctx);
ctx = nullptr;
return 0;
}
CopGmSslClient::~CopGmSslClient()
{
CloseTcpSslClient();
}
5. 编译成dll文件
准备一个C语言的服务单(运行在Linux)
#include <openssl/crypto.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/engine.h>
#include <unistd.h>
#include <signal.h>
#include <sys/select.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include <fcntl.h>
#include <arpa/inet.h>
#define PKT_SOM 27
#define PKT_EOM 28
#define PKT_DLMTR 2
#define PKT_SIZE_LEN 5
#define PKT_FUNC_LEN 4
#define PKT_WIND_LEN 4
#define PKT_HEADER_LEN 2+PKT_SIZE_LEN+PKT_FUNC_LEN+PKT_WIND_LEN
#define PKT_TRAILER_LEN 2
#define PKT_UID_LEN 8
#define PKT_PWD_LEN 8
#define LF "\n"
static unsigned char* PSK_ID = NULL;
static unsigned char PSK[256] = { 0 };
static size_t PSK_LEN = 0;
int DTLS = 0;
FILE* gbWsGmsslFp = NULL;
char certdir[1024];
char sign_cert[2048];
char sign_key[2048];
char enc_cert[2048];
char enc_key[2048];
char engine_name[2048];
char ca_cert[2048];
char ca_path[2048];
static unsigned char* keylog_filename = NULL;
#define GM_WS_LOG(format,msgs...) do{ fprintf((gbWsGmsslFp),format,##msgs);}while(0)
SSL_CTX* gbCtx = NULL;
typedef struct _gmssl_handle_ gmssl_handle_t;
typedef struct _gmssl_handle_
{
int clientfd;
SSL* ssl;
int (*gmrecv)(gmssl_handle_t*, char*, int);
int (*gmsend)(gmssl_handle_t*, const char*, int);
int useGmssl;/*** 1:yes 0:no ***/
int tcpHasConnected;/*** 1:yes 0:no ***/
int sslHasConnected;/*** 1:yes 0:no ***/
int events;
}GmsslHandle;
void keylog_cb(const SSL* ssl, const char* line)
{
int ret;
BIO* bio = NULL;
if (NULL != keylog_filename) {
bio = BIO_new_file(keylog_filename, "ab+");
if (bio == NULL)
return;
if (strlen(line) != BIO_write(bio, line, strlen(line))
|| strlen(LF) != BIO_write(bio, LF, strlen(LF)))
{
fprintf(stderr, "wire keylog file error\n");
exit(1);
}
BIO_free(bio);
}
}
int psk_cb(SSL* ssl,const unsigned char* identity,size_t identity_len,SSL_SESSION** sess)
{
SSL_SESSION* session = NULL;
if (!PSK_ID)
goto error;
if (identity_len != strlen(PSK_ID) ||
0 != memcmp(identity, PSK_ID, identity_len))
goto error;
session = SSL_SESSION_new();
if (session == NULL
|| !SSL_SESSION_set1_master_key(session, PSK,
PSK_LEN)
|| !SSL_SESSION_set_cipher(session, SSL_get_pending_cipher(ssl))
|| !SSL_SESSION_set_protocol_version(session, TLS1_3_VERSION))
goto error;
*sess = session; session = NULL;
error:
if (session) SSL_SESSION_free(session);
return 1;
}
int init_gmssl()
{
int err = 0;
ENGINE* engine = NULL;
SSL_CTX* ctx = NULL;
SSL* ssl = NULL;
/* Load encryption & hashing algorithms for the SSL program */
SSL_library_init();
/* Load the error strings for SSL & CRYPTO APIs */
SSL_load_error_strings();
/* Load engine if use it */
if (NULL != engine_name && 0 != engine_name[0])
{
engine = ENGINE_by_id(engine_name);
if (engine == NULL)
goto error;
err = ENGINE_init(engine);
if (err != 1)
goto error;
}
ctx = SSL_CTX_new(TLS_server_method());
/* Load sign cert and sign key */
if (NULL != sign_cert && 0 != sign_cert[0] && NULL != sign_key && 0 != sign_key[0])
{
EVP_PKEY* pkey = NULL;
/* Load the sign certificate into the SSL_CTX structure */
err = SSL_CTX_use_certificate_file(ctx, sign_cert, SSL_FILETYPE_PEM);
if (err != 1)
goto error;
if (NULL != engine)
{
/* Load private key, maybe cipher key file or key index that generated by engine */
pkey = ENGINE_load_private_key(engine, sign_key, NULL, NULL);
if (pkey == NULL)
goto error;
}
else
{
/* Load common private key file*/
BIO* in = NULL;
in = BIO_new(BIO_s_file());
if (in == NULL)
goto error;
err = BIO_read_filename(in, sign_key);
if (err != 1)
goto error;
pkey = PEM_read_bio_PrivateKey(in, NULL, NULL, NULL);
if (pkey == NULL)
goto error;
BIO_free(in);
}
/* Use the private-key corresponding to the sign certificate */
err = SSL_CTX_use_PrivateKey(ctx, pkey);
if (err != 1)
goto error;
/* Check if the certificate and private-key matches */
err = SSL_CTX_check_private_key(ctx);
if (err != 1)
goto error;
EVP_PKEY_free(pkey);
}
/* Load enc cert and enc key */
if (NULL != enc_cert && 0 != enc_cert[0] && NULL != enc_key && 0 != enc_key[0])
{
EVP_PKEY* pkey = NULL;
/* Load the encrypt certificate into the SSL_CTX structure */
err = SSL_CTX_use_certificate_file(ctx, enc_cert, SSL_FILETYPE_PEM);
if (err != 1)
goto error;
if (NULL != engine)
{
/* Load private key, maybe cipher key file or key index that generated by engine */
pkey = ENGINE_load_private_key(engine, enc_key, NULL, NULL);
if (pkey == NULL)
goto error;
}
else
{
/* Load common private key file*/
BIO* in = NULL;
in = BIO_new(BIO_s_file());
if (in == NULL)
goto error;
err = BIO_read_filename(in, enc_key);
if (err != 1)
goto error;
pkey = PEM_read_bio_PrivateKey(in, NULL, NULL, NULL);
if (pkey == NULL)
goto error;
BIO_free(in);
}
/* Use the private-key corresponding to the encrypt certificate */
err = SSL_CTX_use_PrivateKey(ctx, pkey);
if (err != 1)
goto error;
/* Check if the encrypt certificate and private-key matches */
err = SSL_CTX_check_private_key(ctx);
if (err != 1)
goto error;
EVP_PKEY_free(pkey);
}
if( (ca_cert != NULL && 0 != ca_cert[0]) || (ca_path != NULL && 0 != ca_path[0]) )
{
/* Load the CA certificate into the SSL_CTX structure */
err = SSL_CTX_load_verify_locations(ctx, ca_cert, ca_path);
if (err != 1)
goto error;
}
/* Set to require peer (client) certificate verification */
SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL);
/* Set the verification depth to 1 */
SSL_CTX_set_verify_depth(ctx, 1);
/* set keylog file, maybe use for wireshark */
SSL_CTX_set_keylog_callback(ctx, keylog_cb);
SSL_CTX_set_psk_find_session_callback(ctx, psk_cb);
gbCtx = ctx;
return 0;
error:
if (engine)
{
ENGINE_finish(engine);
ENGINE_free(engine);
}
/* Free the SSL_CTX structure */
if (ctx)
SSL_CTX_free(ctx);
/* Free the SSL structure */
if (ssl)
SSL_free(ssl);
gbCtx = NULL;
return -1;
}
void sigchld_hdlr(int signo) {
pid_t pid = 0;
int status = 0;
while ((pid = waitpid(-1, &status, WNOHANG)) > (pid_t)0) {
GM_WS_LOG("recycle pid:%ld\n",pid);
}
return;
}
int SetFdNonBlock(int connfd)
{
int val = 0,err = 0;
val = fcntl(connfd, F_GETFL, 0);
if (val < 0)
goto error;
val = fcntl(connfd, F_SETFL, val | O_NONBLOCK);
if (val < 0)
goto error;
return val;
error:
GM_WS_LOG("set fd:[%d] non block failed:%s\n",strerror(errno));
return -1;
}
int recvn(int fd, char* buf, int size)
{
int nleft = 0, nread = 0;
char* ptr = NULL;
ptr = buf;
nleft = size;
while (nleft > 0)
{
if ((nread = read(fd, ptr, nleft)) < 0)
{
if (errno == EINTR)
nread = 0;
else
return(-1);
}
else if (nread == 0)
break;
nleft -= nread;
ptr += nread;
}
return (size - nleft);
}
int gm_nossl_recvn(GmsslHandle *ghandle, char* buf, int size)
{
int nleft = 0, nread = 0;
char* ptr = NULL;
int fd = ghandle->clientfd;
ptr = buf;
nleft = size;
while (nleft > 0)
{
if ((nread = read(fd, ptr, nleft)) < 0)
{
if (errno == EINTR)
nread = 0;
else
return(-1);
}
else if (nread == 0)
break;
nleft -= nread;
ptr += nread;
}
return (size - nleft);
}
int sendn(int fd, const char* buf, size_t size)
{
int nleft = 0, nwritten = 0;
const char* ptr = NULL;
ptr = buf;
nleft = size;
while (nleft > 0)
{
if (nwritten = write(fd, ptr, nleft) <= 0)
{
if (nwritten < 0 && errno == EINTR)
nwritten = 0;
else
return (-1);
}
nleft -= nwritten;
ptr += nwritten;
}
return size;
}
/***
返回值:
大于0:返回字节数
小于0:读取错误
等于0:对端关闭套接字
***/
int gmssl_recvn(GmsslHandle* ghandle, char* buf, int size)
{
int nleft = 0, nread = 0, err = 0, pending = 0;
char* ptr = NULL;
ptr = buf;
nleft = size;
SSL* ssl = ghandle->ssl;
while (nleft > 0) {
nread = SSL_read(ssl, ptr, nleft);
if (nread <= 0) {
goto error;
}
nleft -= nread;
ptr += nread;
pending = SSL_pending(ssl);
if (pending)
continue;
else
break;
}
return (size - nleft);
error:
return -1;
}
int gmssl_recv(GmsslHandle* ghandle, char* buf, int size)
{
int nleft = 0, nread = 0, err = 0, pending = 0;
char* ptr = NULL;
ptr = buf;
nleft = size;
SSL* ssl = ghandle->ssl;
nread = SSL_read(ssl, ptr, nleft);
return nread;
}
int gmssl_send(GmsslHandle* ghandle,const char* buf, int size)
{
int nleft = 0, nsend = 0, err = 0;
const char* ptr = NULL;
SSL* ssl = ghandle->ssl;
ptr = buf;
nleft = size;
nsend = SSL_write(ssl, ptr, nleft);
return nsend;
}
int gmssl_sendn(GmsslHandle* ghandle,const char* buf, int size)
{
int nleft = 0, nsend = 0, err = 0;
const char* ptr = NULL;
SSL* ssl = ghandle->ssl;
ptr = buf;
nleft = size;
while (nleft > 0) {
nsend = SSL_write(ssl, ptr, nleft);
err = SSL_get_error(ssl,nsend);
if (err == SSL_ERROR_NONE) {
nleft -= nsend;
ptr += nsend;
}
else {
goto error;
}
}
return size;
error:
return -1;
}
/***
以COP与前端通讯协议读取socket数据
前端发送的协议:
buf[0]:27 开始
buf[1]:27 开始
buf[2]...buf[6]:报文数据长度--2727(二字节)+长度(五字节)+fid
buf[7]...buf[10]:fid
buf[11]...buf[n-3]:报文内容(报文内容不包含2727和2828)
buf[n-2]:28 结束
buf[n-1]:28 结束
报文:2727(2字节)+报文长度(5字节)+fid(4字节)+报文体+ 结束2828(2字节
outsize = 报文总长度+1(即'\0')
***/
int gmssl_recv_by_cop_protocol(GmsslHandle* ghandle, char** outbuf, int* outsize)
{
char packhead[7 + 1]; /*** 报文:2727(2字节) + 报文长度(5字节) ***/
char packlen[5 + 1];
int len;
char* packbuf = NULL;
int err = 0, readlen = 0;
bzero(packhead, sizeof(packhead));
bzero(packlen, sizeof(packlen));
readlen = ghandle->gmrecv(ghandle,packhead,sizeof(packhead) -1);
if (readlen <= 0) {
GM_WS_LOG("gmrecv packhead error\n");
goto error;
}
if (packhead[0] == PKT_SOM && packhead[1] == PKT_SOM) {
memcpy(packlen, packhead + 2, 5);/*** 前端发送过来的包整体长度算 ***/
len = atoi(packlen);
}
else {
GM_WS_LOG("Illega start of message:%s ,it will discard\n",packhead);
goto error;
}
packbuf = (char*)malloc(len + 1);
if (packbuf == NULL) {
GM_WS_LOG("malloc error:%s\n",strerror(errno));
goto error;
}
bzero(packbuf, len + 1);
memcpy(packbuf, packhead, 7);
readlen = ghandle->gmrecv(ghandle, packbuf + 7, len - 7);/*** 减去 2727和长度五字节 ***/
if (readlen <= 0) {
GM_WS_LOG("gmrecv remain packbuf error\n");
goto error;
}
if (packbuf[len - 2] == PKT_EOM && packbuf[len - 1] == PKT_EOM) {
*outbuf = packbuf;
*outsize = len + 1;
return 0;
}
else {
GM_WS_LOG("Illega end of message:%s ,it will discard\n", packbuf);
goto error;
}
return len;
error:
if (packbuf)
free(packbuf);
return -1;
}
/***
以timeout方式接收数据,参数3中的指定bufsize只代表缓存最大长度,不作为读取指定字节数的长度
***/
int gmssl_recv_timeout(GmsslHandle *ghandle, char **outbuf, int *outsize,const int seconds)
{
struct timeval to;
int ret = 0,connfd = 0;
fd_set ifds;
bzero(&to,sizeof(struct timeval));
connfd = ghandle->clientfd;
to.tv_sec = seconds;
do {
FD_ZERO(&ifds);
FD_SET(connfd, &ifds);
//检查是否有读写,60S等待
ret = select(connfd + 1, &ifds, NULL, NULL, &to);
if (ret <= 0) {
GM_WS_LOG("select error:%s pid[%lu]\n",strerror(errno),getpid());
goto error;
}
ret = gmssl_recv_by_cop_protocol(ghandle, outbuf, outsize);
if (ret < 0)
goto error;
} while (0);
return 0;
error:
return -1;
}
int child_gmssl(GmsslHandle *ghandle)
{
int ret = 0,count = 0;
GM_WS_LOG("## child %ld \n", getpid());
char* prcv = NULL;
char rcvbuff[1024];
int bufsize = 0;
char sendbuff[1024];
/*
ret = SetFdNonBlock(ghandle->clientfd);
if (ret < 0) {
GM_WS_LOG("SetFdNonBlock error\n");
goto error;
}
ret = gmssl_recv_timeout(ghandle,&rcvbuf,&bufsize,60);
if (ret < 0) {
GM_WS_LOG("gmssl_recv_timeout error\n");
goto error;
}
*/
while(1){
bzero(rcvbuff,sizeof(rcvbuff));
bzero(sendbuff,sizeof(sendbuff));
ret = ghandle->gmrecv(ghandle,rcvbuff,sizeof(rcvbuff));
if(ret <= 0){
GM_WS_LOG("client exit!!!\n");
break;
}
GM_WS_LOG("recvie client :%s\n",rcvbuff);
if(0 == strcmp("exit",rcvbuff))
break;
snprintf(sendbuff,sizeof(sendbuff),"Reply count[%d] Hello Client\n",count++);
ghandle->gmsend(ghandle,sendbuff,sizeof(sendbuff));
}
// free(rcvbuf);
return 0;
}
int main()
{
int err = 0;
gbWsGmsslFp = stderr;
int listenfd = 0, connfd = 0;
int opt = 1;
pid_t childpid = 0;
int ntry = 0;
struct sockaddr_in cliaddr;
struct sockaddr_in sa_serv;
socklen_t clilen = 0;
SSL* ssl = NULL;
short s_port = 8888;
bzero(engine_name,sizeof(engine_name));
bzero(ca_cert, sizeof(ca_cert));
bzero(ca_path, sizeof(ca_path));
bzero(certdir, sizeof(certdir));
bzero(sign_cert, sizeof(sign_cert));
bzero(sign_key, sizeof(sign_key));
bzero(enc_cert, sizeof(enc_cert));
bzero(enc_key, sizeof(enc_key));
snprintf(certdir,sizeof(certdir),"/usr/local/lib64/tassl/tassl_demo/cert/certs");
snprintf(sign_cert, sizeof(sign_cert), "%s/SS.crt", certdir);
snprintf(sign_key, sizeof(sign_key), "%s/SS.key", certdir);
snprintf(enc_cert, sizeof(enc_cert), "%s/SE.crt", certdir);
snprintf(enc_key, sizeof(enc_key), "%s/SE.key", certdir);
err = init_gmssl();
if (err) {
GM_WS_LOG("init gmssl failed\n");
return 0;
}
signal(SIGCHLD, sigchld_hdlr);
signal(SIGPIPE, SIG_IGN);
memset(&sa_serv, '\0', sizeof(sa_serv));
sa_serv.sin_family = AF_INET;
sa_serv.sin_addr.s_addr = INADDR_ANY;
sa_serv.sin_port = htons(s_port); /* Server Port number */
/* TLS use TCP */
listenfd = socket(AF_INET, SOCK_STREAM, 0);
if (err) {
GM_WS_LOG("socket func error:%s\n", strerror(errno));
return -1;
}
/* bind server address */
err = bind(listenfd, (struct sockaddr*)&sa_serv, sizeof(sa_serv));
if (err) {
GM_WS_LOG("bind func error:%s\n", strerror(errno));
return -1;
}
/* reuse port */
setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (void*)&opt, sizeof(opt));
/* Wait for an incoming TCP connection. */
err = listen(listenfd, 5);
if (err) {
GM_WS_LOG("bind func error:%s\n", strerror(errno));
return -1;
}
clilen = sizeof(cliaddr);
for (;;) {
AGAIN:
if ((connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &clilen)) < 0 ){
if ((errno == EINTR) || (errno == ECONNABORTED)) {
GM_WS_LOG("[accept] FAILED..[ERRNO='%d', ERRSTR='%s']\n", errno, strerror(errno));
continue; /* back to for() */
}
else if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) {
if (ntry > 5) {
GM_WS_LOG("NO NEW CONNECTION OVER 5 SECONDS..\n");
continue;
}
else {
GM_WS_LOG("(ntry <= 5..\n");
sleep(1);
++ntry;
goto AGAIN;
}
}
else {
GM_WS_LOG("accept error");
}
}
GM_WS_LOG("New client .....");
if ((childpid = fork()) == 0) {
close(listenfd);
GmsslHandle* ghandle = (GmsslHandle*)malloc(sizeof(GmsslHandle));
if (ghandle == NULL) {
GM_WS_LOG("Client fd:%d malloc GmsslHandle failed\n", connfd);
goto childend;
}
bzero(ghandle, sizeof(GmsslHandle));
BIO* bio = NULL;
ssl = SSL_new(gbCtx);
bio = BIO_new(BIO_s_socket());
BIO_set_fd(bio, connfd, BIO_NOCLOSE);
SSL_set_bio(ssl, bio, bio);
SSL_accept(ssl);
ghandle->clientfd = connfd;
ghandle->ssl = ssl;
ghandle->sslHasConnected = 1;
ghandle->gmrecv = gmssl_recv;
ghandle->gmsend = gmssl_send;
child_gmssl(ghandle);
childend:
close(connfd);
exit(0);
}
}
return 0;
}
拿JAVA主函数测试一下
CopGmsslClient.java
package com.cop.gmssl;
import java.io.IOException;
import java.util.Scanner;
/**
*
* D:\java-ws\gmtlstest-ws\CopGmssl\src>javah -classpath D:\java-ws\gmtlstest-ws\CopGmssl\src -jni com.cop.gmssl.CopGmsslClient
* */
public class CopGmsslClient {
static {
System.load("E:\\gmssl-test\\bin\\libcrypto-1_1-x64.dll");
System.load("E:\\gmssl-test\\bin\\libssl-1_1-x64.dll");
System.load("E:\\CopGmsslLib\\CopGmJNIDll\\x64\\Release\\CopGmJNIDll.dll");
}
private String handle;
private String host;
public CopGmsslClient()
{
this.handle = this.Connect(this.host);
}
public CopGmsslClient(String host)
{
this.handle = this.Connect(host);
}
public void setHost(String host)
{
this.host = host;
}
public void setHandle(String handle)
{
this.handle = handle;
}
public String readMsg()
{
String buff = Recv(this.handle);
return buff;
}
public void sendMsg(String buff)
{
Send(buff,this.handle);
return;
}
public void Close()
{
Close(this.handle);
return;
}
public native String Connect(String host);
public native String Recv(String handle);
public native void Send(String buff,String handle);
public native void Close(String handle);
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println("=============================");
String host = "192.168.179.130:8888";
CopGmsslClient client = new CopGmsslClient(host);
for(int i = 0; i< 10; i++) {
// try (Scanner sc = new Scanner(System.in)) {
// String str = sc.nextLine();
// client.sendMsg(str);
// }
client.sendMsg("Hello I'm JNI test count["+i+"]");
String sbuf = client.readMsg();
System.out.println("=============================");
if(sbuf.equals("exit"))
break;
System.out.println("Server reply:"+sbuf.toString());
try {
Thread.sleep(3);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
client.Close();
System.out.println("-----------------exit-------------------------");
}
}
服务端接收到客户端的请求
客户端接收到服务端响应