udf_sm4.c
/**
* mysql udf sm4
*
* @author xieqiansong@gmail.com
* @date 2024-05-26
*/
#include <mysql.h>
#include <mysql_com.h>
#include <stdlib.h>
#include <string.h>
#include "sm4.c" // https://github.com/NEWPLAN/SMx/tree/master/SM4/Windows/SM4/src
my_bool encrypt_sm4_init(UDF_INIT *initid, UDF_ARGS *args, char *message);
my_bool decrypt_sm4_init(UDF_INIT *initid, UDF_ARGS *args, char *message);
char *encrypt_sm4(UDF_INIT *initid, UDF_ARGS *args, char *result, unsigned long *length, char *is_null, char *error);
char *decrypt_sm4(UDF_INIT *initid, UDF_ARGS *args, char *result, unsigned long *length, char *is_null, char *error);
void encrypt_sm4_deinit(UDF_INIT *initid);
void decrypt_sm4_deinit(UDF_INIT *initid);
my_bool encrypt_sm4_init(UDF_INIT *initid, UDF_ARGS *args, char *message) {
// 检查参数个数
if (args->arg_count != 1) {
strcpy(message, "wrong number of arguments: encrypt_sm4() requires one argument");
return 1;
}
// 检查参数类型
if (args->arg_type[0] != STRING_RESULT) {
strcpy(message, "encrypt_sm4() requires a string argument");
return 1;
}
initid->maybe_null = 1;
initid->max_length = 65535;
return 0;
}
my_bool decrypt_sm4_init(UDF_INIT *initid, UDF_ARGS *args, char *message) {
// 检查参数个数
if (args->arg_count != 1) {
strcpy(message, "wrong number of arguments: decrypt_sm4() requires one argument");
return 1;
}
// 检查参数类型
if (args->arg_type[0] != STRING_RESULT) {
strcpy(message, "decrypt_sm4() requires a string argument");
return 1;
}
initid->maybe_null = 1;
initid->max_length = 65535;
return 0;
}
char *do_crypt(UDF_INIT *inisid, UDF_ARGS *args, char *result, unsigned long *length, char *is_null, char *error,
int mode) {
if (args->args[0] == NULL) {
*is_null = 1;
return NULL;
}
// 入参以及入参长度
char *text = args->args[0];
int input_length = args->lengths[0];
// 构建加解密入参和出参
int max_len = input_length + 20;
char *input = (char *) malloc(max_len * sizeof(char));
char *output = (char *) malloc(max_len * sizeof(char));
if (input == NULL || output == NULL) {
*is_null = 1;
return NULL;
}
memset(input, 0, max_len * sizeof(char));
memset(output, 0, max_len * sizeof(char));
// args->args[0] 执行一次sql时是复用的,所以必须通过 input_length 来复制
memcpy(input, text, input_length);
// -------------------------------- 加解密 Start --------------------------------
sm4_context ctx;
char *key = "0000000000000000"; // 密钥好像要求16个字符
int encrypt_len = (input_length + 15) & ~15;
if (mode == 1) {
sm4_setkey_enc(&ctx, (unsigned char *) key);
sm4_crypt_ecb(&ctx, mode, encrypt_len, (unsigned char *) input, (unsigned char *) output);
*length = encrypt_len;
} else {
sm4_setkey_dec(&ctx, (unsigned char *) key);
sm4_crypt_ecb(&ctx, mode, encrypt_len, (unsigned char *) input, (unsigned char *) output);
*length = strlen(output);
}
// -------------------------------- 加解密 End --------------------------------
// *length 出参长度
// *length = mode == 1 ? encrypt_len : strlen(output);
*is_null = 0;
return output;
}
char *encrypt_sm4(UDF_INIT *initid, UDF_ARGS *args, char *result, unsigned long *length, char *is_null, char *error) {
return do_crypt(initid, args, result, length, is_null, error, 1);
}
char *decrypt_sm4(UDF_INIT *initid, UDF_ARGS *args, char *result, unsigned long *length, char *is_null, char *error) {
return do_crypt(initid, args, result, length, is_null, error, 0);
}
void encrypt_sm4_deinit(UDF_INIT *initid) {
}
void decrypt_sm4_deinit(UDF_INIT *initid) {
}
CMakeLists.txt
cmake_minimum_required(VERSION 3.27)
project(mysql_udf C)
set(CMAKE_C_STANDARD 11)
# 添加MySQL头文件夹路径
include_directories(D:/platform/middleware/mysql-5.7/include)
# 添加库文件并打包为DLL
add_library(mysql_udf SHARED udf_sm4.c)
# 在 Windows 平台上添加额外属性
if(WIN32)
set_target_properties(mysql_udf PROPERTIES
WIN32_EXECUTABLE TRUE
EXPORT_NAME mysql_udf)
endif()
Windows下我用的Clion编译的
Linux可以参考这个
g++ -I /usr/include/mysql -shared -fPIC -o udf_sm4.so udf_sm4.c
cp udf_sm4.so /usr/lib64/mysql/plugin/
生成dll或so复制文件到目录、引入函数
-- 复制到这个目录下
show variables like "%plugin_dir%";
DROP FUNCTION IF EXISTS encrypt_sm4;
DROP FUNCTION IF EXISTS decrypt_sm4;
CREATE FUNCTION encrypt_sm4 RETURNS string SONAME 'libmysql_udf.dll';
CREATE FUNCTION decrypt_sm4 RETURNS string SONAME 'libmysql_udf.dll';
-- 测试UDF 没有结果应该就是成功了
select description,
encrypt_sm4(description) en_description,
length(encrypt_sm4(description)) en_description_len,
decrypt_sm4(encrypt_sm4(description)) de_description,
cast(decrypt_sm4(encrypt_sm4(description)) as char) de_description_char,
length(decrypt_sm4(encrypt_sm4(description))) de_description_len
from mysql.help_topic
where description != cast(decrypt_sm4(encrypt_sm4(description)) as char);
要注意的地方,机器上已经安装了mysql才能编译,因为要引入mysql的头文件。编写UDF的时候注意入参和出参的长度。