c语言开发 php扩展 sm4
首先 php可以直接调用openssl 直接进行sm4 sm3的加密
如:
openssl_encrypt($plaintext, 'sm4-cbc', $key, OPENSSL_RAW_DATA , $iv);
openssl_digest('123','sm3')
php如果直接调用sm2 需要统一使用openssl的evp接口
openssl1.1的源码在sm2_crypt文件里面
此处只是学习
/* gmtest extension for PHP */
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "php.h"
#include "ext/standard/info.h"
#include "php_gmtest.h"
#include "zend_exceptions.h"
//这里使用openssl 1.1版本 include 为加载库 lib 为链接库
#include <openssl/evp.h>
#include "ext/standard/base64.h"
/* For compatibility with older PHP versions */
#ifndef ZEND_PARSE_PARAMETERS_NONE
#define ZEND_PARSE_PARAMETERS_NONE() \
ZEND_PARSE_PARAMETERS_START(0, 0) \
ZEND_PARSE_PARAMETERS_END()
#endif
/* {{{ void gmtest_test1()
*/
PHP_FUNCTION(gmtest_test1)
{
ZEND_PARSE_PARAMETERS_NONE();
php_printf("The extension %s is loaded and working!\r\n", "gmtest");
}
/* }}} */
/* {{{ string gmtest_test2( [ string $var ] )
*/
PHP_FUNCTION(gmtest_test2)
{
char *var = "World";
size_t var_len = sizeof("World") - 1;
zend_string *retval;
ZEND_PARSE_PARAMETERS_START(0, 1)
Z_PARAM_OPTIONAL
Z_PARAM_STRING(var, var_len)
ZEND_PARSE_PARAMETERS_END();
retval = strpprintf(0, "Hello %s", var);
RETURN_STR(retval);
}
/* }}}*/
/* {{{ sm4解密 类似为aes string gmtest_caotl_sm4_jiami( [ string $var ] )
*/
PHP_FUNCTION(gmtest_caotl_sm4_jiami)
{
zend_string *plaintext;
zend_string *key = NULL;
zend_string *iv = NULL;
unsigned char *ciphertext;
int key_len, iv_len, plaintext_len, ciphertext_len;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "S|SS", &plaintext, &key, &iv) == FAILURE) {
return;
}
key_len = key ? ZSTR_LEN(key) : 16;
iv_len = iv ? ZSTR_LEN(iv) : 16;
plaintext_len = ZSTR_LEN(plaintext);
ciphertext_len = plaintext_len + EVP_MAX_BLOCK_LENGTH; // 最大可能的密文长度
ciphertext = emalloc(ciphertext_len);
int len;
EVP_CIPHER_CTX *cipher_ctx;
cipher_ctx = EVP_CIPHER_CTX_new();
if (!cipher_ctx) {
php_error_docref(NULL, E_WARNING, "Failed to create cipher context");
RETVAL_FALSE;
}
EVP_EncryptInit_ex(cipher_ctx, EVP_sm4_ecb(), NULL, (unsigned char *)ZSTR_VAL(key), (unsigned char *)ZSTR_VAL(iv));
EVP_EncryptUpdate(cipher_ctx, ciphertext, &len, (unsigned char *)ZSTR_VAL(plaintext), plaintext_len);
// 结束加密过程
EVP_EncryptFinal_ex(cipher_ctx, ciphertext + len, &len);
EVP_CIPHER_CTX_free(cipher_ctx);
zend_string *str = zend_string_init((const char *)ciphertext, len, 0);
RETVAL_STR(str);
}
/* }}}*/
/* {{{ sm4解密 类似为aes string gmtest_caotl_sm4_jiami( [ string $var ] )
*/
PHP_FUNCTION(gmtest_caotl_sm4_jiemi)
{
zend_string *plaintext;
zend_string *key = NULL;
zend_string *iv = NULL;
unsigned char *ciphertext;
int key_len, iv_len, plaintext_len, ciphertext_len;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "S|SS", &plaintext, &key, &iv) == FAILURE) {
return;
}
key_len = key ? ZSTR_LEN(key) : 16;
iv_len = iv ? ZSTR_LEN(iv) : 16;
plaintext_len = ZSTR_LEN(plaintext);
ciphertext_len = plaintext_len + EVP_MAX_BLOCK_LENGTH; // 最大可能的密文长度
ciphertext = emalloc(ciphertext_len);
int len;
EVP_CIPHER_CTX *cipher_ctx;
cipher_ctx = EVP_CIPHER_CTX_new();
if (!cipher_ctx) {
php_error_docref(NULL, E_WARNING, "Failed to create cipher context");
RETVAL_FALSE;
}
EVP_DecryptInit_ex(cipher_ctx, EVP_sm4_ecb(), NULL, (unsigned char *)ZSTR_VAL(key), (unsigned char *)ZSTR_VAL(iv));
EVP_DecryptUpdate(cipher_ctx, ciphertext, &len, (unsigned char *)ZSTR_VAL(plaintext), plaintext_len);
// 结束加密过程
EVP_DecryptFinal_ex(cipher_ctx, ciphertext + len, &len);
EVP_CIPHER_CTX_free(cipher_ctx);
zend_string *str = zend_string_init((const char *)ciphertext, len, 0);
RETVAL_STR(str);
}
/* }}}*/
/* {{{ PHP_RINIT_FUNCTION
*/
PHP_RINIT_FUNCTION(gmtest)
{
#if defined(ZTS) && defined(COMPILE_DL_GMTEST)
ZEND_TSRMLS_CACHE_UPDATE();
#endif
return SUCCESS;
}
/* }}} */
/* {{{ PHP_MINFO_FUNCTION
*/
PHP_MINFO_FUNCTION(gmtest)
{
php_info_print_table_start();
php_info_print_table_header(2, "gmtest support", "enabled");
php_info_print_table_end();
}
/* }}} */
/* {{{ arginfo
*/
ZEND_BEGIN_ARG_INFO(arginfo_gmtest_test1, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO(arginfo_gmtest_test2, 0)
ZEND_ARG_INFO(0, str)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_gmtest_caotl_sm4_jiami, 0, 0, 1)
ZEND_ARG_INFO(0, plaintext)
ZEND_ARG_INFO(0, key)
ZEND_ARG_INFO(0, iv)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_gmtest_caotl_sm4_jiemi, 0, 0, 1)
ZEND_ARG_INFO(0, plaintext)
ZEND_ARG_INFO(0, key)
ZEND_ARG_INFO(0, iv)
ZEND_END_ARG_INFO()
/* }}} */
/* {{{ gmtest_functions[]
*/
static const zend_function_entry gmtest_functions[] = {
PHP_FE(gmtest_test1, arginfo_gmtest_test1)
PHP_FE(gmtest_test2, arginfo_gmtest_test2)
PHP_FE(gmtest_caotl_sm4_jiami, arginfo_gmtest_caotl_sm4_jiami)
PHP_FE(gmtest_caotl_sm4_jiemi, arginfo_gmtest_caotl_sm4_jiemi)
PHP_FE_END
};
/* }}} */
/* {{{ gmtest_module_entry
*/
zend_module_entry gmtest_module_entry = {
STANDARD_MODULE_HEADER,
"gmtest", /* Extension name */
gmtest_functions, /* zend_function_entry */
NULL, /* PHP_MINIT - Module initialization */
NULL, /* PHP_MSHUTDOWN - Module shutdown */
PHP_RINIT(gmtest), /* PHP_RINIT - Request initialization */
NULL, /* PHP_RSHUTDOWN - Request shutdown */
PHP_MINFO(gmtest), /* PHP_MINFO - Module info */
PHP_GMTEST_VERSION, /* Version */
STANDARD_MODULE_PROPERTIES
};
/* }}} */
#ifdef COMPILE_DL_GMTEST
# ifdef ZTS
ZEND_TSRMLS_CACHE_DEFINE()
# endif
ZEND_GET_MODULE(gmtest)
#endif
php ext_skel.php --ext sm2
phpize
./configure --enable-sm2
make
sudo make install
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <php.h>
#include <openssl/ec.h>
#include <openssl/evp.h>
#include <openssl/sm2.h>
#include <openssl/pem.h>
#include <openssl/err.h>
// 声明模块的函数
PHP_FUNCTION(sm2_encrypt);
PHP_FUNCTION(sm2_decrypt);
PHP_FUNCTION(sm2_generate_keypair); // 新增的密钥生成函数声明
// 函数列表
const zend_function_entry sm2_functions[] = {
PHP_FE(sm2_encrypt, NULL)
PHP_FE(sm2_decrypt, NULL)
PHP_FE(sm2_generate_keypair, NULL) // 注册新函数
PHP_FE_END
};
// 声明模块信息
zend_module_entry sm2_module_entry = {
STANDARD_MODULE_HEADER,
"sm2", // 扩展名
sm2_functions, // 函数列表
NULL, // 模块初始化
NULL, // 模块关闭
NULL, // 请求初始化
NULL, // 请求关闭
NULL, // PHP 信息显示
NO_VERSION_YET, // 扩展版本
STANDARD_MODULE_PROPERTIES
};
#ifdef COMPILE_DL_SM2
ZEND_GET_MODULE(sm2)
#endif
// SM2 加密函数
PHP_FUNCTION(sm2_encrypt)
{
char *plaintext;
size_t plaintext_len;
char *public_key_pem;
size_t public_key_len;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss", &plaintext, &plaintext_len, &public_key_pem, &public_key_len) == FAILURE) {
RETURN_FALSE;
}
BIO *bio = BIO_new_mem_buf(public_key_pem, public_key_len);
EVP_PKEY *pkey = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL);
BIO_free(bio);
if (!pkey) {
RETURN_FALSE;
}
EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(pkey, NULL);
if (!ctx || EVP_PKEY_encrypt_init(ctx) <= 0) {
EVP_PKEY_free(pkey);
RETURN_FALSE;
}
size_t outlen;
if (EVP_PKEY_encrypt(ctx, NULL, &outlen, (unsigned char *)plaintext, plaintext_len) <= 0) {
EVP_PKEY_CTX_free(ctx);
EVP_PKEY_free(pkey);
RETURN_FALSE;
}
unsigned char *outbuf = emalloc(outlen);
if (EVP_PKEY_encrypt(ctx, outbuf, &outlen, (unsigned char *)plaintext, plaintext_len) <= 0) {
efree(outbuf);
EVP_PKEY_CTX_free(ctx);
EVP_PKEY_free(pkey);
RETURN_FALSE;
}
EVP_PKEY_CTX_free(ctx);
EVP_PKEY_free(pkey);
RETVAL_STRINGL((char *)outbuf, outlen);
efree(outbuf);
}
// SM2 解密函数
PHP_FUNCTION(sm2_decrypt)
{
char *ciphertext;
size_t ciphertext_len;
char *private_key_pem;
size_t private_key_len;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss", &ciphertext, &ciphertext_len, &private_key_pem, &private_key_len) == FAILURE) {
RETURN_FALSE;
}
BIO *bio = BIO_new_mem_buf(private_key_pem, private_key_len);
EVP_PKEY *pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
BIO_free(bio);
if (!pkey) {
RETURN_FALSE;
}
EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(pkey, NULL);
if (!ctx || EVP_PKEY_decrypt_init(ctx) <= 0) {
EVP_PKEY_free(pkey);
RETURN_FALSE;
}
size_t outlen;
if (EVP_PKEY_decrypt(ctx, NULL, &outlen, (unsigned char *)ciphertext, ciphertext_len) <= 0) {
EVP_PKEY_CTX_free(ctx);
EVP_PKEY_free(pkey);
RETURN_FALSE;
}
unsigned char *outbuf = emalloc(outlen);
if (EVP_PKEY_decrypt(ctx, outbuf, &outlen, (unsigned char *)ciphertext, ciphertext_len) <= 0) {
efree(outbuf);
EVP_PKEY_CTX_free(ctx);
EVP_PKEY_free(pkey);
RETURN_FALSE;
}
EVP_PKEY_CTX_free(ctx);
EVP_PKEY_free(pkey);
RETVAL_STRINGL((char *)outbuf, outlen);
efree(outbuf);
}
// SM2 密钥对生成函数
PHP_FUNCTION(sm2_generate_keypair)
{
// 创建 SM2 密钥对
EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL);
if (!pctx) {
RETURN_FALSE;
}
if (EVP_PKEY_paramgen_init(pctx) <= 0) {
EVP_PKEY_CTX_free(pctx);
RETURN_FALSE;
}
if (EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pctx, NID_sm2) <= 0) {
EVP_PKEY_CTX_free(pctx);
RETURN_FALSE;
}
EVP_PKEY *params = NULL;
if (EVP_PKEY_paramgen(pctx, ¶ms) <= 0) {
EVP_PKEY_CTX_free(pctx);
RETURN_FALSE;
}
EVP_PKEY_CTX *kctx = EVP_PKEY_CTX_new(params, NULL);
EVP_PKEY_free(params);
EVP_PKEY_CTX_free(pctx);
if (!kctx) {
RETURN_FALSE;
}
if (EVP_PKEY_keygen_init(kctx) <= 0) {
EVP_PKEY_CTX_free(kctx);
RETURN_FALSE;
}
EVP_PKEY *pkey = NULL;
if (EVP_PKEY_keygen(kctx, &pkey) <= 0) {
EVP_PKEY_CTX_free(kctx);
RETURN_FALSE;
}
EVP_PKEY_CTX_free(kctx);
// 将密钥转换为 PEM 格式
BIO *bio = BIO_new(BIO_s_mem());
PEM_write_bio_PrivateKey(bio, pkey, NULL, NULL, 0, NULL, NULL);
BUF_MEM *private_key_ptr;
BIO_get_mem_ptr(bio, &private_key_ptr);
char *private_key = emalloc(private_key_ptr->length + 1);
memcpy(private_key, private_key_ptr->data, private_key_ptr->length);
private_key[private_key_ptr->length] = '\0';
BIO_free(bio);
bio = BIO_new(BIO_s_mem());
PEM_write_bio_PUBKEY(bio, pkey);
BUF_MEM *public_key_ptr;
BIO_get_mem_ptr(bio, &public_key_ptr);
char *public_key = emalloc(public_key_ptr->length + 1);
memcpy(public_key, public_key_ptr->data, public_key_ptr->length);
public_key[public_key_ptr->length] = '\0';
BIO_free(bio);
EVP_PKEY_free(pkey);
// 返回关联数组,包括私钥和公钥
array_init(return_value);
add_assoc_string(return_value, "private_key", private_key);
add_assoc_string(return_value, "public_key", public_key);
efree(private_key);
efree(public_key);
}