Mysql 调用SM3 算法实现国密

Feat -[Mysql 调用SM3 算法实现国密]

MySQL 本身并不直接支持 SM3 加密算法,主要用于数字签名和验证。MySQL 内置的加密函数主要支持一些常见的哈希算法(如 MD5、SHA1、SHA2 等),但不包括 SM3。

如果需要在 MySQL 中使用 SM3 算法,可以通过以下方式实现:

  • 方法1:使用外部程序或脚本;

    应用程序层面(如 Python、Java 等)使用 SM3 算法对数据进行加密,然后将加密后的结果存储到 MySQL 中。

    灵活性高,可以使用任意变成语言实现;

    避免修改Mysql服务服务器配置;

  • 方法2:使用 MySQL 自定义函数(UDF),即以下使用;

    性能高,直接在Mysql 中运行

    适合频繁调用的场景


思路:

  1. 编写 C/C++ 代码实现 SM3 算法
    • 使用 OpenSSL 或其他支持 SM3 的库编写 SM3 哈希计算的代码;
    • 编译成共享库(如 .so 文件)
  2. 在 MySQL 中加载 UDF
  3. 调用自定义函数

CentOS 上的注意事项

前置条件
  • 操作系统 Centos 7.x
  • MySQL 8.xx
  • OpenSSL 版本 1.1.1 或更高(支持 SM3 算法)
  • GCC:用于编译 C/C++ 代码
检查 OpenSSL 版本

openssl version

如果版本低于 1.1.1,请升级 OpenSSL

  • CentOS 7 默认的 OpenSSL 版本可能较低(1.0.2),不支持 SM3。

    [root@iZhp39hauxbv1s8uh5x0vqZ mysql]# openssl version
    OpenSSL 1.0.2k-fips  26 Jan 2017
    
  • 如果需要升级 OpenSSL,可以参考以下步骤

    sudo yum install epel-release
    sudo yum install openssl11 openssl11-devel
    [root@iZhp39hauxbv1s8uh5x0vqZ mysql]# openssl version
    OpenSSL 1.0.2k-fips  26 Jan 2017
    
    # 以下默认使用 openssl11 version
    [root@iZhp39hauxbv1s8uh5x0vqZ mysql]# openssl11 version
    OpenSSL 1.1.1k  FIPS 25 Mar 2021
    

    然后编译时指定 OpenSSL 1.1.1 的路径:

    gcc -shared -fPIC -o sm3_udf.so sm3_udf.c -I /usr/include/mysql -I /usr/include/openssl11 -L /usr/lib64/openssl11 -lssl -lcrypto
    

    -shared :生成共享库

    -fPIC :生成位置无关代码

    -o sm3_udf.so :指定输出文件名

    -I /usr/inclode/mysql :指定MYSQL 头文件路径

    -lssl -lcrypto :链接 OpenSLL 库

MySQL 插件目录权限
  • 确保 MySQL 用户对插件目录有读写权限;
安装 GCC 和 OpenSSL

sudo yum install gcc openssl openssl-devel

安装 MySQL 开发库

sudo yum install mysql-devel


编写 SM3 的 C++ 代码

注:这里用电话号码举例:

变形输出=SM3(电话号码全文UTF-8编码)(64字符,英文按小写输出)。机构客户、保险公司员工与销售人员的联系电话不做变形。

例:

传真/座机号码:010-66279455,变形后:

0702465666b4df0c2f7ddefe86f4be1c69c239765e2e8d3288128b5e1ba0be31

手机号码:12345678912,变形后:

f30dbe8891134b9528adf0b6083246d7fa65eac0c546723ba79abeb595153f94

创建 sm3_phone_udf.cpp

#include <mysql.h>
#include <openssl/evp.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h> // 包含 bool 类型的头文件

// SM3 哈希计算函数
void sm3_hash(const char *input, size_t input_len, unsigned char *output) {
    EVP_MD_CTX *mdctx;
    const EVP_MD *md;
    unsigned int md_len;

    md = EVP_sm3(); // 获取 SM3 算法
    mdctx = EVP_MD_CTX_new(); // 创建上下文
    EVP_DigestInit_ex(mdctx, md, NULL); // 初始化
    EVP_DigestUpdate(mdctx, input, input_len); // 更新数据
    EVP_DigestFinal_ex(mdctx, output, &md_len); // 计算哈希
    EVP_MD_CTX_free(mdctx); // 释放上下文
}

// MySQL UDF 初始化函数
extern "C" bool sm3_phone_init(UDF_INIT *initid, UDF_ARGS *args, char *message) {
    if (args->arg_count != 1 || args->arg_type[0] != STRING_RESULT) {
        strcpy(message, "sm3_phone function requires exactly one string argument.");
        return true; // 返回 true 表示初始化失败
    }
    initid->max_length = 64; // 设置返回值的最大长度(64 字符)
    initid->ptr = (char *)malloc(65); // 分配内存存储哈希值
    if (initid->ptr == NULL) {
        strcpy(message, "Memory allocation failed.");
        return true;
    }
    return false; // 返回 false 表示初始化成功
}

// MySQL UDF 主函数
extern "C" char *sm3_phone(UDF_INIT *initid, UDF_ARGS *args, char *result, unsigned long *length, char *is_null, char *error) {
    if (args->args[0] == NULL) {
        *is_null = 1;
        return NULL;
    }

    const char *phone = args->args[0]; // 获取输入的电话号码
    size_t phone_len = args->lengths[0]; // 获取输入的长度

    // 计算 SM3 哈希
    unsigned char hash[EVP_MAX_MD_SIZE];
    sm3_hash(phone, phone_len, hash);

    // 将哈希值转换为十六进制字符串(小写)
    char *hex_hash = initid->ptr;
    for (int i = 0; i < EVP_MD_size(EVP_sm3()); i++) {
        sprintf(hex_hash + (i * 2), "%02x", hash[i]);
    }
    hex_hash[64] = '\0'; // 确保字符串以 null 结尾

    *length = 64; // 设置返回值的长度
    return hex_hash;
}

// MySQL UDF 清理函数
extern "C" void sm3_phone_deinit(UDF_INIT *initid) {
    if (initid->ptr) {
        free(initid->ptr); // 释放内存
    }
}
编译代码
g++ -shared -fPIC -o sm3_phone_udf.so sm3_phone_udf.cpp -I /usr/include/mysql -I /usr/include/openssl11 -L /usr/lib64/openssl11 -lssl -lcrypto
登录msyql,查看插件安装目录
mysql> SHOW VARIABLES LIKE 'plugin_dir';
+---------------+--------------------------+
| Variable_name | Value                    |
+---------------+--------------------------+
| plugin_dir    | /usr/lib64/mysql/plugin/ |
+---------------+--------------------------+
1 row in set (0.01 sec)
Copy 至插件安装目录
sudo cp sm3_phone_udf.so /usr/lib64/mysql/plugin/
登录msyql,查看插件安装目录
mysql> DROP FUNCTION sm3_phone_udf;
Query OK, 0 rows affected (0.00 sec)
mysql> CREATE FUNCTION sm3_phone RETURNS STRING SONAME 'sm3_phone_udf.so';
Query OK, 0 rows affected (0.00 sec)

-- 输出为 64 字符的小写十六进制字符串
mysql> SELECT '12345678912' AS phone, sm3('12345678912') AS sm3_hash
    -> UNION ALL
    -> SELECT '010-66279455' AS phone, sm3('010-66279455') AS sm3_hash;
+--------------+------------------------------------------------------------------------------------------------------------------------------------+
| phone        | sm3_hash                                                                                                                           |
+--------------+------------------------------------------------------------------------------------------------------------------------------------+
| 12345678912  | 0x66333064626538383931313334623935323861646630623630383332343664376661363565616330633534363732336261373961626562353935313533663934 |
| 010-66279455 | 0x30373032343635363636623464663063326637646465666538366634626531633639633233393736356532653864333238383132386235653162613062653331 |
+--------------+------------------------------------------------------------------------------------------------------------------------------------+
2 rows in set (0.00 sec)
Test
--  任意IP登录
mysql> use mysql;
Database changed
mysql> update user set host = '%' where user = 'root';
Query OK, 1 row affected (
### 实现SM4加密解密于Spring Boot 3 为了在Spring Boot 3中实现SM4加密解密功能,可以遵循如下方法: #### 配置依赖项 首先,在`pom.xml`文件里加入必要的依赖来支持SM4算法。由于SM4属于国密标准的一部分,并不是Java默认库的一部分,因此需要引入第三方库的支持。 ```xml <dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcpkix-jdk15on</artifactId> <version>1.70</version> </dependency> ``` 此部分操作确保了开发环境具备执行SM4加解密的能力[^2]。 #### 创建SM4工具类 接着创建一个名为`SM4Util.java`的工具类用于封装具体的加密和解密逻辑。此类应提供两个主要的方法:一个是用来加密字符串;另一个则是用来解密已加密过的字符串。下面是一个简单的例子: ```java import org.bouncycastle.jce.provider.BouncyCastleProvider; import javax.crypto.Cipher; import javax.crypto.spec.SecretKeySpec; import java.security.Key; import java.util.Base64; public class SM4Util { private static final String ALGORITHM_NAME = "SM4"; private static final String TRANSFORMATION_MODE = "SM4/CBC/PKCS7Padding"; public static byte[] encrypt(String content, String key) throws Exception{ Key sm4Key = new SecretKeySpec(key.getBytes(),ALGORITHM_NAME); Cipher cipher = Cipher.getInstance(TRANSFORMATION_MODE,BouncyCastleProvider.PROVIDER_NAME); cipher.init(Cipher.ENCRYPT_MODE,sm4Key); return Base64.getEncoder().encode(cipher.doFinal(content.getBytes())); } public static byte[] decrypt(byte[] encryptedContent, String key)throws Exception{ Key sm4Key = new SecretKeySpec(key.getBytes(),ALGORITHM_NAME); Cipher cipher = Cipher.getInstance(TRANSFORMATION_MODE,BouncyCastleProvider.PROVIDER_NAME); cipher.init(Cipher.DECRYPT_MODE,sm4Key); return cipher.doFinal(Base64.getDecoder().decode(encryptedContent)); } } ``` 这段代码实现了基本的加密和解密流程,其中包含了设置密钥、初始化Cipher对象以及调用doFinal()完成最终转换的过程[^3]。 #### 修改application.yml配置文件 对于数据库连接信息以及其他敏感字段,可以在`application.yml`中按照特定格式书写,比如前缀加上`@SM4@-`表示后面跟着的是经过SM4加密后的值。当应用启动时,可以通过自定义PropertySourceFactory解析并自动解码这些被标记的内容。 ```yaml spring: datasource: url: jdbc:mysql://localhost/testdb?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC username: root password: '@SM4@-your_encrypted_password_here' ``` 以上步骤描述了如何在一个基于Spring Boot框架的应用程序内部集成SM4加密机制的方式,不仅限于此处提到的具体细节,还可以根据实际情况调整优化方案以满足不同的需求[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值