Windows 10 下Java JNI方式调用开源国密库

这个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工具就可以。

  • 环境变量配置

  • 编译国密库
  1. 安装完VS2019之后,找到VS2019安装文件下面的CLI

如果需要编译64位的需要选x64_native

记得一定要用管理员身份打开

  1. 打开后并切换到下载国密库的路径

输入:

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 动态库
  1. 先建立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-------------------------");
	}

}

 服务端接收到客户端的请求

客户端接收到服务端响应

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值