近期的一个项目需要用到SM3加密算法,需要在iOS中使用Objective-C实现SM3国密加密算法。
SM3:是中国国家密码管理局发布的密码杂凑算法标准,适用于商用密码应用中的数字签名和验证、消息认证码的生成与验证以及随机数的生成等
由于iOS系统并未内置SM3算法,我们需要使用第三方开源库或自己实现
GMObjC库: 是一个基于 OpenSSL 的国密(SM2、SM3、SM4)算法的 Objective-C 开源库,适用于 iOS 和 macOS 开发。它封装了中国国家密码管理局发布的多种加密算法,包括:
1. SM2: 支持基于椭圆曲线(ECC)的加解密,密钥协商(ECDH)和签名算法2. SM3: 类似 SHA 系列的国密哈希算法,包含 SM3 和 HMAC 等
3. SM4: 实现对称分组加密算法
**GmSSL库:**GmSSL是由北京大学自主开发的国产商用密码开源库,实现了对国密算法、标准和安全通信协议的全面功能覆盖,支持包括移动端在内的主流操作系统和处理器,支持密码钥匙、密码卡等典型国产密码硬件,提供功能丰富的命令行工具及多种编译语言编程接口
方案一:使用第三方库(GMObjC)
集成GMObjC:集成GMObjC方法
因为我们的项目是SDK不便用CocoaPods方法,因此我只能选择直接集成和手动编译为 Framework。
1.直接集成 (demo)
1.从 Git 下载最新代码,找到和 README 同级的 GMObjC 文件夹,将 GMObjC 文件夹拖入项目
2.找到和 README 同级的 Frameworks 文件夹,将项目 Frameworks/OpenSSL.xcframework 拖入项目
3.在需要使用的地方导入头文件 GMObjC.h 即可使用 SM2、SM4 加解密,签名验签,计算 SM3 摘要等
注意事项
GMObjC 依赖 OpenSSL,可直接拖入 Frameworks/OpenSSL.xcframework 或通过pod GMOpenSSL安装 OpenSSL。
如果项目中已集成 OpenSSL 1.1.1l 以上版本,可共用同一个 OpenSSL;否则需要使用 Carthage 将 GMObjC 编译为动态库。
我按照以上步骤将文件导入后报错:
OpenSSL.xcframework 签名验证失败

终端执行强制重签名命令
codesign --force --deep --sign - 你的路径/OpenSSL.xcframework
返回:你的路径/OpenSSL.xcframework: replacing existing signature
现在就可以运行测试了:
#import "GMObjC.h"
NSString *str = @"123@1234";
NSString *digest = [GMSm3Utils hashWithText:str];
NSLog(@"%@", digest);
2.手动编译为 Framework (demo)
1.动态库:
从 GitHub 下载源码,打开项目GMObjC-master/Frameworks/GMObjC.xcframework把这个拖入项目
在 Xcode 的 General → Frameworks, Libraries, and Embedded Content 中需标记为 Embed & Sign

#import "GMObjC/GMObjC.h"
NSString *digest1 = [GMSm3Utils hashWithText:str];
NSLog(@"%@", digest1);
2.静态库:
从 GitHub 下载源码,打开项目 GMObjC.xcodeproj,设置 Build Settings - Linking-General - Mach-O Type 为 Static Library
手动编译为静态库 GMObjC.framework
**合并为 XCFramework:**通过xcodebuild -create-xcframework命令来合并为 XCFramework,通过合并 GMObjC 库的模拟器和真机版本来演示
# 创建合并包 GMObjC.xcframework
xcodebuild -create-xcframework \
-framework Release-iphoneos/GMObjC.framework \
-framework Release-iphonesimulator/GMObjC.framework \
-output GMObjC.xcframework
把生成的GMObjC.xcframework拖入项目即可
3.CocoaPods安装GMObjC (GMObjC-demo) (GMDynamic-demo)
GMObjC 和 GMDynamic 只能安装其中一个,二者不能同时安装。
GMObjC 为静态库,GMDynamic 为编译好的 GMObjC 动态库版本。
# 安装 GMObjC 的源码和 GMOpenSSL.xcframework (静态库)
pod 'GMObjC', '~> 4.0.3'
# 当 Podfile 中使用 use_frameworks! 时,安装 GMObjC.xcframework (动态库)
pod 'GMDynamic', '~> 4.0.3'
方案二:使用第三方库(GmSSL)(demo)
集成GmSSL:

但是我用这种方法不行,我用了其他的方法。
我们使用GmSSL 3.x(master分支)来编译iOS的静态库(libcrypto.a和libssl.a)。由于3.x版本采用了CMake构建系统,因此流程与2.x不同。
GmSSL 3.x 的构建系统已经发生了变化,生成的库文件名为 libgmssl.a 而不是传统的 libcrypto.a 和 libssl.a。
如果项目必须使用
libcrypto.a
和libssl.a
,请回退到 GmSSL 2.x
-
克隆代码并切换到master分支(或最新的稳定标签)
-
配置CMake工具链文件(为iOS交叉编译)
-
分别编译arm64(真机)和x86_64(模拟器)架构
-
使用lipo合并成通用静态库
-
将生成的静态库和头文件集成到iOS项目中。
创建编译脚本: build_ios.sh(放在GmSSL根目录)
#!/bin/bash
set -e
# 确保使用正确的路径
export PATH="/usr/local/bin:$PATH"
# 设置环境变量
export XCODE_PATH=$(xcode-select -p)
export IOS_SDK=$XCODE_PATH/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk
export SIM_SDK=$XCODE_PATH/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk
# 创建输出目录
OUTPUT_DIR="build-ios"
rm -rf $OUTPUT_DIR
mkdir -p $OUTPUT_DIR
# 编译函数
compile_arch() {
ARCH=$1
SDK=$2
BUILD_DIR="${OUTPUT_DIR}/${ARCH}"
mkdir -p $BUILD_DIR
pushd $BUILD_DIR > /dev/null
echo "▸ 配置 $ARCH..."
cmake ../.. \
-DCMAKE_SYSTEM_NAME=iOS \
-DCMAKE_OSX_ARCHITECTURES=$ARCH \
-DCMAKE_OSX_SYSROOT=$SDK \
-DCMAKE_OSX_DEPLOYMENT_TARGET=13.0 \
-DCMAKE_BUILD_TYPE=Release \
-DBUILD_SHARED_LIBS=OFF \
-DENABLE_SM2=ON \
-DENABLE_SM3=ON \
-DENABLE_SM4=ON \
-DENABLE_SM9=ON \
-G Ninja
echo "▸ 编译 $ARCH..."
ninja
# 关键修改:GmSSL 3.x 生成的库是 libgmssl.a
mkdir -p lib
cp bin/libgmssl.a lib/
popd > /dev/null
}
# 编译各架构
compile_arch "arm64" "$IOS_SDK"
compile_arch "x86_64" "$SIM_SDK"
# 合并通用库
UNIVERSAL_DIR="${OUTPUT_DIR}/universal"
mkdir -p $UNIVERSAL_DIR/lib
# 合并为单个库 (GmSSL 3.x 只生成一个库)
lipo -create \
"${OUTPUT_DIR}/arm64/lib/libgmssl.a" \
"${OUTPUT_DIR}/x86_64/lib/libgmssl.a" \
-output "$UNIVERSAL_DIR/lib/libgmssl.a"
# 复制头文件
if [ -d "${OUTPUT_DIR}/arm64/include" ]; then
cp -R "${OUTPUT_DIR}/arm64/include" "$UNIVERSAL_DIR/"
elif [ -d "../../include" ]; then
cp -R "../../include" "$UNIVERSAL_DIR/"
else
echo "⚠️ 警告: 找不到头文件目录"
fi
echo "✅ 编译成功!"
echo "库文件位置: $UNIVERSAL_DIR/lib/libgmssl.a"
echo "头文件位置: $UNIVERSAL_DIR/include"
# 验证文件
file "$UNIVERSAL_DIR/lib"/*.a
lipo -info "$UNIVERSAL_DIR/lib/libgmssl.a"
然后按照以下步骤进行执行:
# 安装构建工具
brew install cmake ninja pkg-config
# 获取最新代码
git clone https://github.com/guanzhi/GmSSL.git
cd GmSSL
git checkout master # 确保使用最新版本
git pull
# 2. 执行编译
chmod +x build_ios.sh
./build_ios.sh
-
将GmSSL/build-ios/universal/lib/libgmssl.a 拖入项目
-
将GmSSL/include/gmssl 拖入项目
-
import "sm3.h"
封装方法:
@interface GmSSLEncryptorSM3 : NSObject
+ (NSString *)sm3HashWithString:(NSString *)input;
+ (NSData *)sm3HashWithData:(NSData *)data;
@end
@implementation GmSSLEncryptorSM3
+ (instancetype)encryptor {
return [[GmSSLEncryptorSM3 alloc] init];
}
+ (NSData *)sm3HashWithData:(NSData *)data {
// 初始化 SM3 上下文
SM3_CTX ctx;
sm3_init(&ctx);
// 添加数据到哈希计算
sm3_update(&ctx, data.bytes, data.length);
// 准备存储结果的缓冲区 (SM3 输出为 32 字节)
uint8_t dgst[SM3_DIGEST_SIZE];
// 完成哈希计算
sm3_finish(&ctx, dgst);
// 转换为 NSData
return [NSData dataWithBytes:dgst length:SM3_DIGEST_SIZE];
}
+ (NSString *)sm3HashWithString:(NSString *)input {
NSData *inputData = [input dataUsingEncoding:NSUTF8StringEncoding];
// 计算 SM3 哈希
NSData *hashData = [GmSSLEncryptorSM3 sm3HashWithData:inputData];
// 转换为十六进制字符串显示
NSMutableString *hexString = [NSMutableString string];
const uint8_t *bytes = (const uint8_t *)hashData.bytes;
for (NSUInteger i = 0; i < hashData.length; i++) {
[hexString appendFormat:@"%02x", bytes[i]];
}
return hexString;
}
@end
就可以在项目中使用了:
NSString *encryptor = [GmSSLEncryptorSM3 sm3HashWithString:str];
NSLog(@"%@", encryptor);
方案三:纯 Objective-C 实现(无依赖)(demo)
SM3本质上不是加密算法,它是是一种杂凑函数,是在[SHA-256]基础上改进实现的一种算法,它不是对数据进行加密然后再解密,而是生成一个256位的散列值,因此SM3适用于内容摘要,数字签名验证或密码验证等。
SM3算法的执行过程:
根据SM3标准文档(GM/T 0004-2012)

**消息扩展:**将16个32位字扩展为68个字(W)和64个字(W1),使用P1宏。
**压缩函数:**64轮迭代更新寄存器(A-H),每轮使用FF1/GG1等宏。
**常量:**压缩函数中的常量0x7A879D8A(TJ的固定值)。
**结果输出:**将最终状态寄存器转换为大端序字节流(256位)。
//
// SM3Encryptor.m
// testDemo
//
// Created by wt on 2025/6/12.
//
#import "SM3Encryptor.h"
#include <stdint.h>
// SM3 上下文结构
typedef struct {
uint32_t state[8]; // 8个32位寄存器(A-H)
uint64_t totalLength; // 总消息长度(位)
uint8_t buffer[64]; // 当前数据块缓存
uint32_t bufferLength; // 当前缓冲区长度
} SM3Context;
// 循环左移
static inline uint32_t ROTL(uint32_t x, uint8_t n) {
return (x << n) | (x >> (32 - n));
}
// 布尔函数 FF0(0≤j≤15)
static inline uint32_t FF0(uint32_t x, uint32_t y, uint32_t z) {
return x ^ y ^ z;
}
// 布尔函数 FF1(16≤j≤63)
static inline uint32_t FF1(uint32_t x, uint32_t y, uint32_t z) {
return (x & y) | (x & z) | (y & z);
}
// 布尔函数 GG0(0≤j≤15)
static inline uint32_t GG0(uint32_t x, uint32_t y, uint32_t z) {
return x ^ y ^ z;
}
// 布尔函数 GG1(16≤j≤63)
static inline uint32_t GG1(uint32_t x, uint32_t y, uint32_t z) {
return (x & y) | ((~x) & z);
}
// 置换函数 P0
static inline uint32_t P0(uint32_t x) {
return x ^ ROTL(x, 9) ^ ROTL(x, 17);
}
// 置换函数 P1
static inline uint32_t P1(uint32_t x) {
return x ^ ROTL(x, 15) ^ ROTL(x, 23);
}
// 初始化SM3上下文
void SM3Init(SM3Context *context) {
// SM3标准初始值
context->state[0] = 0x7380166F;
context->state[1] = 0x4914B2B9;
context->state[2] = 0x172442D7;
context->state[3] = 0xDA8A0600;
context->state[4] = 0xA96F30BC;
context->state[5] = 0x163138AA;
context->state[6] = 0xE38DEE4D;
context->state[7] = 0xB0FB0E4E;
context->totalLength = 0;
context->bufferLength = 0;
memset(context->buffer, 0, 64);
}
// 处理单个64字节块(压缩函数核心)
void SM3Compress(SM3Context *context, const uint8_t block[64]) {
// 1. 消息扩展:16字 → 68字(W) + 64字(W1)
uint32_t W[68], W1[64];
// 初始化前16字(大端序转换)
for (int i = 0; i < 16; i++) {
W[i] = (uint32_t)block[i*4] << 24 |
(uint32_t)block[i*4+1] << 16 |
(uint32_t)block[i*4+2] << 8 |
(uint32_t)block[i*4+3];
}
// 计算W[16]-W[67]
for (int j = 16; j < 68; j++) {
uint32_t temp = W[j-16] ^ W[j-9] ^ ROTL(W[j-3], 15);
W[j] = P1(temp) ^ ROTL(W[j-13], 7) ^ W[j-6];
}
// 计算W1[0]-W1[63]
for (int j = 0; j < 64; j++) {
W1[j] = W[j] ^ W[j+4];
}
// 2. 寄存器初始化(A-H)
uint32_t A = context->state[0];
uint32_t B = context->state[1];
uint32_t C = context->state[2];
uint32_t D = context->state[3];
uint32_t E = context->state[4];
uint32_t F = context->state[5];
uint32_t G = context->state[6];
uint32_t H = context->state[7];
// 3. 64轮迭代(严格遵循标准)
for (int j = 0; j < 64; j++) {
uint32_t SS1, SS2, TT1, TT2;
// 常量选择(关键修正)
uint32_t TJ = (j < 16) ? 0x79CC4519 : 0x7A879D8A;
// 计算SS1/SS2(修正了TJ参数)
SS1 = ROTL(ROTL(A, 12) + E + ROTL(TJ, j % 32), 7);
SS2 = SS1 ^ ROTL(A, 12);
// 计算TT1/TT2(使用内联函数)
if (j < 16) {
TT1 = FF0(A, B, C) + D + SS2 + W1[j];
TT2 = GG0(E, F, G) + H + SS1 + W[j];
} else {
TT1 = FF1(A, B, C) + D + SS2 + W1[j];
TT2 = GG1(E, F, G) + H + SS1 + W[j];
}
// 更新寄存器(严格顺序)
D = C;
C = ROTL(B, 9);
B = A;
A = TT1;
H = G;
G = ROTL(F, 19);
F = E;
E = P0(TT2);
}
// 4. 更新最终状态(与初始IV异或)
context->state[0] ^= A;
context->state[1] ^= B;
context->state[2] ^= C;
context->state[3] ^= D;
context->state[4] ^= E;
context->state[5] ^= F;
context->state[6] ^= G;
context->state[7] ^= H;
}
// 更新数据(可分多次调用)
void SM3Update(SM3Context *context, const uint8_t *data, size_t length) {
context->totalLength += length * 8; // 更新总位数(字节转位)
// 处理缓冲区中的剩余空间
if (context->bufferLength > 0) {
size_t copySize = MIN(64 - context->bufferLength, length);
memcpy(context->buffer + context->bufferLength, data, copySize);
context->bufferLength += copySize;
data += copySize;
length -= copySize;
if (context->bufferLength == 64) {
SM3Compress(context, context->buffer);
context->bufferLength = 0;
}
}
// 处理完整块
while (length >= 64) {
SM3Compress(context, data);
data += 64;
length -= 64;
}
// 缓存剩余数据
if (length > 0) {
memcpy(context->buffer, data, length);
context->bufferLength = length;
}
}
// 完成哈希计算
void SM3Final(SM3Context *context, uint8_t output[32]) {
// 计算填充长度(SM3标准:补位1 + k个0 + 64位长度)
size_t totalBits = context->totalLength;
size_t paddingBits = (context->bufferLength < 56) ?
(56 - context->bufferLength) :
(120 - context->bufferLength);
// 构建填充数据
uint8_t padding[128] = {0};
padding[0] = 0x80; // 补位起始位(二进制10000000)
// 添加填充
SM3Update(context, padding, paddingBits);
// 添加消息长度(大端序64位)
uint64_t bitCount = CFSwapInt64HostToBig(totalBits);
SM3Update(context, (uint8_t *)&bitCount, 8);
// 确保最后一个块被处理
if (context->bufferLength > 0) {
memset(context->buffer + context->bufferLength, 0, 64 - context->bufferLength);
SM3Compress(context, context->buffer);
}
// 输出最终哈希(256位,大端序)
for (int i = 0; i < 8; i++) {
output[i*4] = (uint8_t)(context->state[i] >> 24);
output[i*4 + 1] = (uint8_t)(context->state[i] >> 16);
output[i*4 + 2] = (uint8_t)(context->state[i] >> 8);
output[i*4 + 3] = (uint8_t)(context->state[i]);
}
}
// Objective-C 封装接口
@implementation SM3Encryptor
+ (NSData *)hashWithData:(NSData *)inputData {
SM3Context context;
SM3Init(&context);
// 处理输入数据
SM3Update(&context, inputData.bytes, inputData.length);
// 获取结果
uint8_t output[32];
SM3Final(&context, output);
return [NSData dataWithBytes:output length:32];
}
+ (NSString *)hexStringWithData:(NSData *)inputData {
NSData *hashData = [self hashWithData:inputData];
const uint8_t *bytes = (const uint8_t *)hashData.bytes;
NSMutableString *hex = [NSMutableString string];
for (NSUInteger i = 0; i < hashData.length; i++) {
[hex appendFormat:@"%02X", bytes[i]];
}
return [hex copy];
}
+ (NSString *)hexStringWithInput:(NSString *)inputStr {
NSData *inputData = [inputStr dataUsingEncoding:NSUTF8StringEncoding];
NSData *hashData = [self hashWithData:inputData];
const uint8_t *bytes = (const uint8_t *)hashData.bytes;
NSMutableString *hex = [NSMutableString string];
for (NSUInteger i = 0; i < hashData.length; i++) {
[hex appendFormat:@"%02X", bytes[i]];
}
return [hex copy];
}
@end