iOS-SM3加密算法N种集成

近期的一个项目需要用到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 签名验证失败

OpenSSL.xcframework报错

终端执行强制重签名命令

codesign –force –deep –sign - 你的路径/OpenSSL.xcframework

返回:你的路径/OpenSSL.xcframework: replacing existing signature

现在就可以运行测试了:

1
2
3
4
5
#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

Embed & Sign

1
2
3
4
#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 库的模拟器和真机版本来演示

1
2
3
4
5
6
# 创建合并包 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 动态库版本。

1
2
3
4
5
# 安装 GMObjC 的源码和 GMOpenSSL.xcframework (静态库)
pod 'GMObjC', '~> 4.0.3'

# 当 Podfile 中使用 use_frameworks! 时,安装 GMObjC.xcframework (动态库)
pod 'GMDynamic', '~> 4.0.3'

方案二:使用第三方库(GmSSL)(demo)

集成GmSSL:

集成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.alibssl.a,请回退到 GmSSL 2.x

  1. 克隆代码并切换到master分支(或最新的稳定标签)

  2. 配置CMake工具链文件(为iOS交叉编译)

  3. 分别编译arm64(真机)和x86_64(模拟器)架构

  4. 使用lipo合并成通用静态库

  5. 将生成的静态库和头文件集成到iOS项目中。

创建编译脚本: build_ios.sh(放在GmSSL根目录)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
#!/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"

然后按照以下步骤进行执行:

1
2
3
4
5
6
7
8
9
10
11
12
# 安装构建工具
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
  1. 将GmSSL/build-ios/universal/lib/libgmssl.a 拖入项目

  2. 将GmSSL/include/gmssl 拖入项目

  3. import “sm3.h”

封装方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
@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

就可以在项目中使用了:

1
2
NSString *encryptor = [GmSSLEncryptorSM3 sm3HashWithString:str];
NSLog(@"%@", encryptor);

方案三:纯 Objective-C 实现(无依赖)(demo)

SM3本质上不是加密算法,它是是一种杂凑函数,是在[SHA-256]基础上改进实现的一种算法,它不是对数据进行加密然后再解密,而是生成一个256位的散列值,因此SM3适用于内容摘要,数字签名验证或密码验证等。

SM3算法的执行过程:

根据SM3标准文档(GM/T 0004-2012)

sm3流程.png

消息扩展:将16个32位字扩展为68个字(W)和64个字(W1),使用P1宏。
压缩函数:64轮迭代更新寄存器(A-H),每轮使用FF1/GG1等宏。
常量:压缩函数中的常量0x7A879D8A(TJ的固定值)。
结果输出:将最终状态寄存器转换为大端序字节流(256位)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
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

打赏
  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2015-2025 kindyourself@163.com
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~

支付宝
微信