文章目录
- 《密码系统设计》实验
- 实验项目
- 实验一 嵌入式开发基础
- 4-6 学时实践要求(30 分)
- 1. 参考相关内容,在 Ubuntu或openEuler中(推荐 openEuler)中使用OpenSSL库编程实现调用SM2(加密解密,签名验签),SM3(摘要计算,HMAC 计算),SM4(加密解密)算法,使用Markdown记录详细记录实践过程,每完成一项git commit 一次。(5')
- 2. 参考相关内容,在 Ubuntu或openEuler中(推荐 openEuler)中使用GmSSL库编程实现调用SM2(加密解密,签名验签),SM3(摘要计算,HMAC 计算),SM4(加密解密)算法,使用Markdown记录详细记录实践过程,每完成一项git commit 一次。(5')
- 3. 两人一组,在 Ubuntu或openEuler中(推荐 openEuler)中使用OpenSSL编程实现带签名的数字信封协议。使用OpenSSL库时,Alice发送,Bob接收。Ailice,Bob在实验中要替换为自己的8位学号+姓名。 使用Markdown记录详细记录实践过程,每完成一项git commit 一次。(5分)
- 4. 两人一组,在 Ubuntu或openEuler中(推荐 openEuler)中使用GmSSL编程实现带签名的数字信封协议。使用GmSSL库时,Bob发送,Alice接收。Ailice,Bob在实验中要替换为自己的8位学号+姓名。 使用Markdown记录详细记录实践过程,每完成一项git commit 一次。(5分)
- 生成公私钥对
- 读取明文加密
- 对密文生成签名
- 使用Bob的公钥对sm4的密钥进行加密
- 5. 使用Rust完成带签名的数字信封协议(选做,10分)
- 6. 实验记录中提交 gitee 课程项目链接,提交本次实验相关 git log运行结果
- 7. 提交要求:
《密码系统设计》实验
实验项目
实验序号 | 实验名称 | 实验学时数 | 实验目的 | 实验内容 | 实验类型 | 学生学习预期成果 |
---|---|---|---|---|---|---|
实验一 | 嵌入式开发基础 | 6 | 掌握Linux系统使用与开发方法 | Linux命令,OpenSSL(GmSSL)命令与开发 | 验证性 | 1.掌握常见的Linux命令与C语言开发方法; 2.掌握OpenSSL(GmSSL)的基本用法与开发; 3.掌握常见商用密码算法的使用 |
实验二 | 密码算法实现 | 6 | 掌握常见商用密码算法的原理与实现 | 基于国产化平台使用C语言编程实现SM2、SM3、SM4等算法; | 验证性 | 1.基于Arm等平台和国产化操作系统使用C语言编程实现SM2、SM3、SM4算法; 2.对比分析算法实现的正确性和效率。 |
实验三 | 密码模块实现 | 6 | 基于商用密码标准的密码模块的实现 | 实现简单的密码引擎,能够提供对称密码算法、非对称密码算法、Hash算法等的密码服务。 | 综合性 | 1.理解密码系统固件、接口等的设计和开发流程; 2.参考《GMT 0018-2023密码设备应用接口规范》等商用密码标准设计实现密码算法进行加密/解密、签名/验签、密钥生成/导出等的接口; 3.与其他商用密码模块进行兼容性测试。 |
实验四 | 密码模块的应用 | 6 | 基于商用密码标准的密码模块的应用 | 对电子公文系统等密码系统进行威胁建模,安全性分析和安全性设计。 | 综合性 | 1.系统采用B/S,或者C/S架构,参考《GM∕T 0054-2018 信息系统密码应用基本要求》和《GB/T 39786-2021《信息安全技术 信息系统密码应用基本要求》》 2.设计实现的系统管理功能要简单易用; 3.用户管理采用“三员”管理; 4.设计实现的密钥管理功能应覆盖密钥的全生命周期; |
实验一 嵌入式开发基础
4-6 学时实践要求(30 分)
1. 参考相关内容,在 Ubuntu或openEuler中(推荐 openEuler)中使用OpenSSL库编程实现调用SM2(加密解密,签名验签),SM3(摘要计算,HMAC 计算),SM4(加密解密)算法,使用Markdown记录详细记录实践过程,每完成一项git commit 一次。(5’)
sm3摘要、HMAC计算
// test.c
#include <stdio.h>
#include <string.h>
#include <openssl/evp.h>
#include "sm3hash.h" // 打印缓冲区为十六进制格式
void dumpbuf(const unsigned char *buf, int len) { for (int i = 0; i < len; i++) { printf("%02x", buf[i]); } printf("\n");
} int main(void)
{ const unsigned char sample1[] = { 'a', 'b', 'c', 0 }; unsigned int sample1_len = strlen((const char *)sample1); const unsigned char sample2[] = { 0x61, 0x62, 0x63, 0x64, 0x61, 0x62, 0x63, 0x64, 0x61, 0x62, 0x63, 0x64, 0x61, 0x62, 0x63, 0x64, 0x61, 0x62, 0x63, 0x64, 0x61, 0x62, 0x63, 0x64, 0x61, 0x62, 0x63, 0x64, 0x61, 0x62, 0x63, 0x64, 0x61, 0x62, 0x63, 0x64, 0x61, 0x62, 0x63, 0x64, 0x61, 0x62, 0x63, 0x64, 0x61, 0x62, 0x63, 0x64, 0x61, 0x62, 0x63, 0x64, 0x61, 0x62, 0x63, 0x64, 0x61, 0x62, 0x63, 0x64, 0x61, 0x62, 0x63, 0x64 }; unsigned int sample2_len = sizeof(sample2); unsigned char hash_value[32]; // SM3 输出 32 字节 unsigned int hash_len; int ret, i; // 处理 sample1 ret = sm3_hash(sample1, sample1_len, hash_value, &hash_len); if (ret != 0) { fprintf(stderr, "SM3 Hash failed for sample1\n"); return 1; } printf("Raw data (sample1): %s\n", sample1); printf("Hash length: %u bytes.\n", hash_len); printf("Hash value:\n"); dumpbuf(hash_value, hash_len); printf("\n"); // 处理 sample2 ret = sm3_hash(sample2, sample2_len, hash_value, &hash_len); if (ret != 0) { fprintf(stderr, "SM3 Hash failed for sample2\n"); return 1; } printf("Raw data (sample2):\n"); for (i = 0; i < sample2_len; i++) { printf("0x%x ", sample2[i]); } printf("\n"); printf("Hash length: %u bytes.\n", hash_len); printf("Hash value:\n"); dumpbuf(hash_value, hash_len); printf("\n"); return 0;
}
// sm3hash.c
#include <openssl/evp.h>
#include "sm3hash.h" int sm3_hash(const unsigned char *message, size_t len, unsigned char *hash, unsigned int *hash_len)
{ EVP_MD_CTX *md_ctx = NULL; const EVP_MD *md = NULL; int ret = -1; // 默认返回值为失败 md_ctx = EVP_MD_CTX_new(); if (md_ctx == NULL) { return ret; } md = EVP_sm3(); if (md == NULL) { EVP_MD_CTX_free(md_ctx); return ret; } if (EVP_DigestInit_ex(md_ctx, md, NULL) != 1) { goto cleanup; } if (EVP_DigestUpdate(md_ctx, message, len) != 1) { goto cleanup; } if (EVP_DigestFinal_ex(md_ctx, hash, hash_len) != 1) { goto cleanup; } ret = 0; // 成功 cleanup: EVP_MD_CTX_free(md_ctx); return ret;
}
// sm3hash.h
#ifndef SM3HASH_H
#define SM3HASH_H #include <stddef.h> #ifdef __cplusplus
extern "C" {
#endif int sm3_hash(const unsigned char *message, size_t len, unsigned char *hash, unsigned int *hash_len); #ifdef __cplusplus
}
#endif #endif // SM3HASH_H
配置makefile
# Makefile CC = gcc
CFLAGS = -Wall -O2
LIBS = -lssl -lcrypto TARGET = test
SRCS = test.c sm3hash.c
HEADERS = sm3hash.h all: $(TARGET) $(TARGET): $(SRCS) $(HEADERS) $(CC) $(CFLAGS) -o $(TARGET) $(SRCS) $(LIBS) clean: rm -f $(TARGET)
命令
[wzy@LAPTOP-PRC71A0C src1]$ vim test.c
[wzy@LAPTOP-PRC71A0C src1]$ vim sm3hash.c
[wzy@LAPTOP-PRC71A0C src1]$ vim sm3hash.h
[wzy@LAPTOP-PRC71A0C src1]$ vim makefile
[wzy@LAPTOP-PRC71A0C src1]$ make
gcc -Wall -O2 -I/usr/bin -o test test.c sm3hash.c -lssl -lcrypto
[wzy@LAPTOP-PRC71A0C src1]$ ./test
Raw data (sample1): abc
Hash length: 32 bytes.
Hash value:
66c7f0f462eeedd9d1f2d46bdc10e4e24167c4875cf2f7a2297da02b8f4ba8e0Raw data (sample2):
0x61 0x62 0x63 0x64 0x61 0x62 0x63 0x64 0x61 0x62 0x63 0x64 0x61 0x62 0x63 0x64 0x61 0x62 0x63 0x64 0x61 0x62 0x63 0x64 0x61 0x62 0x63 0x64 0x61 0x62 0x63 0x64 0x61 0x62 0x63 0x64 0x61 0x62 0x63 0x64 0x61 0x62 0x63 0x64 0x61 0x62 0x63 0x64 0x61 0x62 0x63 0x64 0x61 0x62 0x63 0x64 0x61 0x62 0x63 0x64 0x61 0x62 0x63 0x64
Hash length: 32 bytes.
Hash value:
debe9ff92275b8a138604889c18e5a4d6fdb70e5387e5765293dcba39c0c5732[wzy@LAPTOP-PRC71A0C src1]$ git add .
[wzy@LAPTOP-PRC71A0C src1]$ git commit -m "sm3摘要、Hmac计算"
[master (root-commit) 1c5fa35] sm3摘要、Hmac计算5 files changed, 149 insertions(+)create mode 100644 makefilecreate mode 100644 sm3hash.ccreate mode 100644 sm3hash.hcreate mode 100755 testcreate mode 100644 test.c
注意如果出现报错(来自于openssl的库文件配置问题的报错时,比如缺少evp.h头文件)
需要重新配置openssl库文件
使用此条命令
[wzy@LAPTOP-PRC71A0C ~]$ export OPENSSL_CONF=/usr/bin/openssl.cnf
这里/usr/bin/是我的openssl所在路径,可以根据实际情况更改
sm4加密解密
#define _CRT_SECURE_NO_WARNINGS #include "openssl/evp.h"
#include <string.h>
#include <stdio.h> #define FAILURE -1
#define SUCCESS 0 // Function to print hexadecimal data
void print_hex(const char *title, const unsigned char *data, int len) { printf("%s:", title); for (int i = 0; i < len; i++) { if (i % 16 == 0) printf("\n%04X: ", i); printf("%02X ", data[i]); } printf("\n");
} int do_encrypt(const EVP_CIPHER *type, const char *ctype)
{ unsigned char outbuf[1024]; int outlen, tmplen; // SM4 uses a 16-byte (128-bit) key unsigned char key[] = { 0x60, 0x3D, 0xEB, 0x10, 0x15, 0xCA, 0x71, 0xBE, 0x2B, 0x73, 0xAE, 0xF0, 0x85, 0x7D, 0x77, 0x81 }; // SM4 block size is 16 bytes, so IV should also be 16 bytes unsigned char iv[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F }; char intext[] = "Helloworld"; // Plaintext EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); if (!ctx) { printf("Failed to create EVP_CIPHER_CTX\n"); return FAILURE; } // Initialize encryption operation if (EVP_EncryptInit_ex(ctx, type, NULL, key, iv) != 1) { printf("EVP_EncryptInit_ex failed for %s\n", ctype); EVP_CIPHER_CTX_free(ctx); return FAILURE; } // Provide plaintext and get ciphertext if (EVP_EncryptUpdate(ctx, outbuf, &outlen, (unsigned char*)intext, strlen(intext)) != 1) { printf("EVP_EncryptUpdate failed for %s\n", ctype); EVP_CIPHER_CTX_free(ctx); return FAILURE; } // Finalize encryption if (EVP_EncryptFinal_ex(ctx, outbuf + outlen, &tmplen) != 1) { printf("EVP_EncryptFinal_ex failed for %s\n", ctype); EVP_CIPHER_CTX_free(ctx); return FAILURE; } outlen += tmplen; // Clean up context EVP_CIPHER_CTX_free(ctx); // Write ciphertext to file char filename[64]; sprintf(filename, "./cipher_%s.dat", ctype); FILE *out = fopen(filename, "wb+"); if (!out) { printf("Cannot open file: %s\n", filename); return FAILURE; } fwrite(outbuf, 1, outlen, out); fflush(out); fclose(out); printf("%s encryption successful. Ciphertext length: %d bytes\n", ctype, outlen); print_hex("Ciphertext", outbuf, outlen); return SUCCESS;
} int do_decrypt(const EVP_CIPHER *type, const char *ctype)
{ unsigned char inbuf[1024] = { 0 }; unsigned char outbuf[1024] = { 0 }; int outlen, inlen, tmplen; // SM4 uses a 16-byte (128-bit) key unsigned char key[] = { 0x60, 0x3D, 0xEB, 0x10, 0x15, 0xCA, 0x71, 0xBE, 0x2B, 0x73, 0xAE, 0xF0, 0x85, 0x7D, 0x77, 0x81 }; // SM4 block size is 16 bytes, so IV should also be 16 bytes unsigned char iv[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F }; EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); if (!ctx) { printf("Failed to create EVP_CIPHER_CTX\n"); return FAILURE; } // Initialize decryption operation if (EVP_DecryptInit_ex(ctx, type, NULL, key, iv) != 1) { printf("EVP_DecryptInit_ex failed for %s\n", ctype); EVP_CIPHER_CTX_free(ctx); return FAILURE; } // Open the encrypted file char filename[64]; sprintf(filename, "./cipher_%s.dat", ctype); FILE *in = fopen(filename, "rb"); if (!in) { printf("Cannot open file: %s\n", filename); EVP_CIPHER_CTX_free(ctx); return FAILURE; } // Read ciphertext inlen = fread(inbuf, 1, sizeof(inbuf), in); fclose(in); printf("%s decryption: Read ciphertext length: %d bytes\n", ctype, inlen); // Provide ciphertext and get plaintext if (EVP_DecryptUpdate(ctx, outbuf, &outlen, inbuf, inlen) != 1) { printf("EVP_DecryptUpdate failed for %s\n", ctype); EVP_CIPHER_CTX_free(ctx); return FAILURE; } // Finalize decryption if (EVP_DecryptFinal_ex(ctx, outbuf + outlen, &tmplen) != 1) { printf("EVP_DecryptFinal_ex failed for %s\n", ctype); EVP_CIPHER_CTX_free(ctx); return FAILURE; } outlen += tmplen; // Clean up context EVP_CIPHER_CTX_free(ctx); printf("%s decryption result: \n%.*s\n", ctype, outlen, outbuf); print_hex("Decrypted plaintext (hex)", outbuf, outlen); return SUCCESS;
} int main(int argc, char *argv[])
{ // Initialize OpenSSL algorithms OpenSSL_add_all_algorithms(); // Use SM4-CBC for encryption and decryption const EVP_CIPHER *sm4_cbc = EVP_sm4_cbc(); if (!sm4_cbc) { printf("Cannot get SM4-CBC cipher\n"); return FAILURE; } printf("===== SM4-CBC Encryption and Decryption =====\n"); if (do_encrypt(sm4_cbc, "sm4-cbc") != SUCCESS) { printf("SM4-CBC encryption failed\n"); return FAILURE; } if (do_decrypt(sm4_cbc, "sm4-cbc") != SUCCESS) { printf("SM4-CBC decryption failed\n"); return FAILURE; } // Clean up OpenSSL algorithms EVP_cleanup(); return 0;
}
//cipher_sm4-cbc.dat
E:6MdcJ
# Makefile CC = gcc
CFLAGS = -Wall -O2 -I ~/rocopenssl/include -L ~/rocopenssl/lib
LIBS = -lssl -lcrypto TARGET = test
SRCS = sm4openssl.c
HEADERS = all: $(TARGET) $(TARGET): $(SRCS) $(HEADERS) $(CC) $(CFLAGS) -o $(TARGET) $(SRCS) $(LIBS) clean: rm -f $(TARGET)
[wzy@LAPTOP-PRC71A0C sm4]$ vim makefile
[wzy@LAPTOP-PRC71A0C sm4]$ vim cipher_sm4-cbc.bat
[wzy@LAPTOP-PRC71A0C sm4]$ vim sm4openssl.c
[wzy@LAPTOP-PRC71A0C sm4]$ make
gcc -Wall -O2 -I ~/rocopenssl/include -L ~/rocopenssl/lib -o test sm4openssl.c -lssl -lcrypto
[wzy@LAPTOP-PRC71A0C sm4]$ ls
cipher_sm4-cbc.bat makefile sm4openssl.c test
[wzy@LAPTOP-PRC71A0C sm4]$ ./test
===== SM4-CBC Encryption and Decryption =====
sm4-cbc encryption successful. Ciphertext length: 16 bytes
Ciphertext:
0000: 45 E9 16 3A 1C 88 36 4D DD 64 1D 63 18 94 4A F4
sm4-cbc decryption: Read ciphertext length: 16 bytes
sm4-cbc decryption result:
Helloworld
Decrypted plaintext (hex):
0000: 48 65 6C 6C 6F 77 6F 72 6C 64
[wzy@LAPTOP-PRC71A0C sm4]$ git add .
[wzy@LAPTOP-PRC71A0C sm4]$ git commit -m "sm4加密解密"
[master (root-commit) a1daac6] sm4加密解密5 files changed, 215 insertions(+)create mode 100644 cipher_sm4-cbc.batcreate mode 100644 cipher_sm4-cbc.datcreate mode 100644 makefilecreate mode 100644 sm4openssl.ccreate mode 100755 test
2. 参考相关内容,在 Ubuntu或openEuler中(推荐 openEuler)中使用GmSSL库编程实现调用SM2(加密解密,签名验签),SM3(摘要计算,HMAC 计算),SM4(加密解密)算法,使用Markdown记录详细记录实践过程,每完成一项git commit 一次。(5’)
(5分)
先查看一下
gmssl中包含的头文件名
[wzy@LAPTOP-PRC71A0C include]$ cd gmssl
[wzy@LAPTOP-PRC71A0C gmssl]$ ls
aes.h digest.h ghash.h pbkdf2.h sha1.h sm2_key_share.h sm4_cbc_mac.h socket.h x509.h
asm.h dylib.h hex.h pem.h sha2.h sm2_recover.h sm4_cbc_sm3_hmac.h tls.h x509_req.h
asn1.h ec.h hkdf.h pkcs8.h skf.h sm2_ring.h sm4_cl.h version.h zuc.h
base64.h endian.h hmac.h rand.h sm2_blind.h sm2_z256.h sm4_ctr_sm3_hmac.h x509_alg.h
block_cipher.h error.h http.h rdrand.h sm2_commit.h sm3.h sm4.h x509_cer.h
chacha20.h file.h mem.h rsa.h sm2_elgamal.h sm3_x8_avx2.h sm9.h x509_crl.h
cms.h gf128.h oid.h sdf.h sm2.h sm3_xmss.h sm9_z256.h x509_ext.h[wzy@LAPTOP-PRC71A0C gmssl]$ pwd
/home/wzy/GmSSL/include/gmssl
sm2加密解密签名验签的C语言
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <gmssl/sm2.h>
#include <gmssl/sm3.h>
#include <gmssl/error.h> // 辅助函数:以十六进制格式打印字节数组
void print_hex(const char *label, const uint8_t *data, size_t len) { printf("%s: ", label); for(size_t i = 0; i < len; i++) { printf("%02X", data[i]); } printf("\n");
} int main() { int ret = 0; // 1. 生成 SM2 密钥对 SM2_KEY key; printf("生成 SM2 密钥对...\n"); ret = sm2_key_generate(&key); if (ret != 1) { fprintf(stderr, "SM2 密钥生成失败\n"); return EXIT_FAILURE; } printf("SM2 密钥生成成功。\n"); // 打印生成的公钥和私钥(可选) /* sm2_key_print(stdout, 0, 4, "SM2 公钥", &key); sm2_key_print(stdout, 0, 4, "SM2 私钥", &key); */ // 2. 定义要加密的明文 const char *plaintext = "Hello, SM2!"; size_t plaintext_len = strlen(plaintext); printf("明文: %s\n", plaintext); // 3. 加密明文 uint8_t ciphertext[SM2_MAX_CIPHERTEXT_SIZE]; size_t ciphertext_len = sizeof(ciphertext); printf("加密明文...\n"); ret = sm2_encrypt(&key, (const uint8_t *)plaintext, plaintext_len, ciphertext, &ciphertext_len); if (ret != 1) { fprintf(stderr, "SM2 加密失败\n"); return EXIT_FAILURE; } printf("加密成功。\n"); // 打印密文 print_hex("密文", ciphertext, ciphertext_len); // 4. 解密密文 uint8_t decrypted[SM2_MAX_PLAINTEXT_SIZE]; size_t decrypted_len = sizeof(decrypted); printf("解密密文...\n"); ret = sm2_decrypt(&key, ciphertext, ciphertext_len, decrypted, &decrypted_len); if (ret != 1) { fprintf(stderr, "SM2 解密失败\n"); return EXIT_FAILURE; } printf("解密成功。\n"); // 确认解密后的明文与原始明文一致 decrypted[decrypted_len] = '\0'; // 添加字符串终止符 printf("解密后的明文: %s\n", decrypted); // 5. 签名 const char *message = "This is a message to be signed."; size_t message_len = strlen(message); printf("要签名的消息: %s\n", message); // 初始化签名上下文 SM2_SIGN_CTX sign_ctx; sm2_sign_init(&sign_ctx, &key, SM2_DEFAULT_ID, SM2_DEFAULT_ID_LENGTH); // 更新签名上下文 ret = sm2_sign_update(&sign_ctx, (const uint8_t *)message, message_len); if (ret != 1) { fprintf(stderr, "SM2 签名更新失败\n"); return EXIT_FAILURE; } // 完成签名 uint8_t signature[SM2_MAX_SIGNATURE_SIZE]; size_t signature_len = sizeof(signature); ret = sm2_sign_finish(&sign_ctx, signature, &signature_len); if (ret != 1) { fprintf(stderr, "SM2 签名失败\n"); return EXIT_FAILURE; } printf("签名成功。\n"); // 打印签名 print_hex("签名", signature, signature_len); // 6. 验证签名 SM2_SIGN_CTX verify_ctx; sm2_verify_init(&verify_ctx, &key, SM2_DEFAULT_ID, SM2_DEFAULT_ID_LENGTH); ret = sm2_verify_update(&verify_ctx, (const uint8_t *)message, message_len); if (ret != 1) { fprintf(stderr, "SM2 签名验证更新失败\n"); return EXIT_FAILURE; } ret = sm2_verify_finish(&verify_ctx, signature, signature_len); if (ret == 1) { printf("签名验证成功。\n"); } else { printf("签名验证失败。\n"); return EXIT_FAILURE; } return EXIT_SUCCESS;
}
[wzy@LAPTOP-PRC71A0C src]$ vim sm2gmssl.c
[wzy@LAPTOP-PRC71A0C src]$ gcc -o sm2gmssl sm2gmssl.c -lgmssl
[wzy@LAPTOP-PRC71A0C src]$ ls
sm2gmssl sm2gmssl.c
[wzy@LAPTOP-PRC71A0C src]$ ./sm2gmssl
生成 SM2 密钥对...
SM2 密钥生成成功。
明文: Hello, SM2!
加密明文...
加密成功。
密文: 30730220478CB5F8543046613FD4B538EDA4CEF474004577106A517219B533A3EC510CC102206F78B73DAF984C44ABAD0EC634CC1805AD2CE9CD528E8E92E56FF856AB25C08E04209061024BEC21336B07672CC73ACD93D97854030537A0D8A26B2AA9E849503744040B6966CFEDF445E18F60CD5E
解密密文...
解密成功。
解密后的明文: Hello, SM2!
要签名的消息: This is a message to be signed.
签名成功。
签名: 304402203E3A60610A9CB5052773EADDA48E5B92B7F5667B79F650EA5EE56F540063E72202204C1815E39DE286BC4EB5114BD6698EF541B46EAE49176C48E457F7FB86A93B97
签名验证成功。
[wzy@LAPTOP-PRC71A0C src]$ git add .
[wzy@LAPTOP-PRC71A0C src]$ git commit -m "sm2加密解密签名验签"
[master (root-commit) 1c17064] sm2加密解密签名验签2 files changed, 121 insertions(+)create mode 100755 src/sm2gmsslcreate mode 100644 src/sm2gmssl.c
sm3生成摘要和HMAC
#include <stdio.h>
#include <string.h>
#include <gmssl/sm3.h>int main() {const char *data = "Hello, GmSSL SM3!";unsigned char hash[SM3_DIGEST_SIZE]; // SM3_DIGEST_SIZE is the length of the hashSM3_CTX ctx;sm3_init(&ctx); // Initialize the hash contextsm3_update(&ctx, (const unsigned char *)data, strlen(data)); // Hash the datasm3_finish(&ctx, hash); // Finalize the hashprintf("SM3 hash: ");for (int i = 0; i < SM3_DIGEST_SIZE; i++) {printf("%02x", hash[i]);}printf("\n");return 0;
}
[wzy@LAPTOP-PRC71A0C src]$ vim testsm3.c
[wzy@LAPTOP-PRC71A0C src]$ gcc -o testsm3 testsm3.c -lgmssl
[wzy@LAPTOP-PRC71A0C src]$ ls
sm2gmssl sm2gmssl.c testsm3 testsm3.c
[wzy@LAPTOP-PRC71A0C src]$ ./testsm3
SM3 hash: 8b3145130a678ef0049952d0c3d78f878b3c825bd3e73a0fb2319b34cabea9e0
HMAC
// test.c
#include <stdio.h>
#include <string.h>
#include "sm3.h" // 打印缓冲区为十六进制格式
void dumpbuf(const unsigned char *buf, int len) { for (int i = 0; i < len; i++) { printf("%02x", buf[i]); if (((i + 1) % 4) == 0) printf(" "); } printf("\n");
} int main(int argc, char *argv[])
{ const unsigned char *input = (const unsigned char*)"abc"; const unsigned char *key = (const unsigned char*)"123456"; int ilen = strlen((const char *)input); // 输入数据长度 int keylen = strlen((const char *)key); // 密钥长度 unsigned char output[32]; printf("Message: %s\n", input); sm3_hmac(key, keylen, input, ilen, output); printf("HMAC: "); dumpbuf(output, 32); return 0;
}
// sm3.c
#include "sm3.h"
#include <string.h>
#include <stdio.h> /* * 32-bit integer manipulation macros (big endian) */
#ifndef GET_UINT32_BE
#define GET_UINT32_BE(n,b,i) \{ \(n) = ((uint32_t)(b)[(i) ] << 24) \| ((uint32_t)(b)[(i) + 1] << 16) \| ((uint32_t)(b)[(i) + 2] << 8) \| ((uint32_t)(b)[(i) + 3] ); \}
#endif #ifndef PUT_UINT32_BE
#define PUT_UINT32_BE(n,b,i) \{ \(b)[(i) ] = (unsigned char) ( (n) >> 24 ); \(b)[(i) + 1] = (unsigned char) ( (n) >> 16 ); \(b)[(i) + 2] = (unsigned char) ( (n) >> 8 ); \(b)[(i) + 3] = (unsigned char) ( (n) ); \}
#endif /* * SM3 context setup */
void sm3_starts(sm3_context *ctx)
{ ctx->total[0] = 0; ctx->total[1] = 0; ctx->state[0] = 0x7380166F; ctx->state[1] = 0x4914B2B9; ctx->state[2] = 0x172442D7; ctx->state[3] = 0xDA8A0600; ctx->state[4] = 0xA96F30BC; ctx->state[5] = 0x163138AA; ctx->state[6] = 0xE38DEE4D; ctx->state[7] = 0xB0FB0E4E;
} static void sm3_process(sm3_context *ctx, const unsigned char data[64])
{ uint32_t SS1, SS2, TT1, TT2, W[68], W1[64]; uint32_t A, B, C, D, E, F, G, H; uint32_t T[64]; uint32_t Temp1, Temp2, Temp3, Temp4, Temp5; int j;
#ifdef _DEBUG int i;
#endif for (j = 0; j < 16; j++) T[j] = 0x79CC4519; for (j = 16; j < 64; j++) T[j] = 0x7A879D8A; for (j = 0; j < 16; j++) GET_UINT32_BE(W[j], data, j * 4); #ifdef _DEBUG printf("Message with padding:\n"); for (i = 0; i < 8; i++) printf("%08x ", W[i]); printf("\n"); for (i = 8; i < 16; i++) printf("%08x ", W[i]); printf("\n");
#endif #define FF0(x,y,z) ( (x) ^ (y) ^ (z))
#define FF1(x,y,z) (((x) & (y)) | ( (x) & (z)) | ( (y) & (z))) #define GG0(x,y,z) ( (x) ^ (y) ^ (z))
#define GG1(x,y,z) (((x) & (y)) | ( (~(x)) & (z)) ) #define SHL(x,n) (((x) & 0xFFFFFFFF) << (n))
#define ROTL(x,n) (SHL((x),n) | ((x) >> (32 - (n)))) #define P0(x) ((x) ^ ROTL((x),9) ^ ROTL((x),17))
#define P1(x) ((x) ^ ROTL((x),15) ^ ROTL((x),23)) for (j = 16; j < 68; j++) { Temp1 = W[j - 16] ^ W[j - 9]; Temp2 = ROTL(W[j - 3], 15); Temp3 = Temp1 ^ Temp2; Temp4 = P1(Temp3); Temp5 = ROTL(W[j - 13], 7) ^ W[j - 6]; W[j] = Temp4 ^ Temp5; } #ifdef _DEBUG printf("Expanding message W0-67:\n"); for (i = 0; i < 68; i++) { printf("%08x ", W[i]); if (((i + 1) % 8) == 0) printf("\n"); } printf("\n");
#endif for (j = 0; j < 64; j++) { W1[j] = W[j] ^ W[j + 4]; } #ifdef _DEBUG printf("Expanding message W'0-63:\n"); for (i = 0; i < 64; i++) { printf("%08x ", W1[i]); if (((i + 1) % 8) == 0) printf("\n"); } printf("\n");
#endif A = ctx->state[0]; B = ctx->state[1]; C = ctx->state[2]; D = ctx->state[3]; E = ctx->state[4]; F = ctx->state[5]; G = ctx->state[6]; H = ctx->state[7];
#ifdef _DEBUG printf("j A B C D E F G H\n"); printf(" %08x %08x %08x %08x %08x %08x %08x %08x\n", A, B, C, D, E, F, G, H);
#endif for (j = 0; j < 16; j++) { SS1 = ROTL((ROTL(A, 12) + E + ROTL(T[j], j)), 7); SS2 = SS1 ^ ROTL(A, 12); TT1 = FF0(A, B, C) + D + SS2 + W1[j]; TT2 = GG0(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);
#ifdef _DEBUG printf("%02d %08x %08x %08x %08x %08x %08x %08x %08x\n", j, A, B, C, D, E, F, G, H);
#endif } for (j = 16; j < 64; j++) { SS1 = ROTL((ROTL(A, 12) + E + ROTL(T[j], j)), 7); SS2 = SS1 ^ ROTL(A, 12); 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);
#ifdef _DEBUG printf("%02d %08x %08x %08x %08x %08x %08x %08x %08x\n", j, A, B, C, D, E, F, G, H);
#endif } ctx->state[0] ^= A; ctx->state[1] ^= B; ctx->state[2] ^= C; ctx->state[3] ^= D; ctx->state[4] ^= E; ctx->state[5] ^= F; ctx->state[6] ^= G; ctx->state[7] ^= H;
#ifdef _DEBUG printf(" %08x %08x %08x %08x %08x %08x %08x %08x\n", ctx->state[0], ctx->state[1], ctx->state[2], ctx->state[3], ctx->state[4], ctx->state[5], ctx->state[6], ctx->state[7]);
#endif
} /* * SM3 process buffer */
void sm3_update(sm3_context *ctx, const unsigned char *input, int ilen)
{ int fill; uint32_t left; if (ilen <= 0) return; left = ctx->total[0] & 0x3F; fill = 64 - left; ctx->total[0] += ilen; ctx->total[0] &= 0xFFFFFFFF; if (ctx->total[0] < (uint32_t)ilen) ctx->total[1]++; if (left && ilen >= fill) { memcpy(ctx->buffer + left, input, fill); sm3_process(ctx, ctx->buffer); input += fill; ilen -= fill; left = 0; } while (ilen >= 64) { sm3_process(ctx, input); input += 64; ilen -= 64; } if (ilen > 0) { memcpy(ctx->buffer + left, input, ilen); }
} static const unsigned char sm3_padding[64] =
{ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
}; /* * SM3 final digest */
void sm3_finish(sm3_context *ctx, unsigned char output[32])
{ uint32_t last, padn; uint32_t high, low; unsigned char msglen[8]; high = (ctx->total[0] >> 29) | (ctx->total[1] << 3); low = (ctx->total[0] << 3); PUT_UINT32_BE(high, msglen, 0); PUT_UINT32_BE(low, msglen, 4); last = ctx->total[0] & 0x3F; padn = (last < 56) ? (56 - last) : (120 - last); sm3_update(ctx, sm3_padding, padn); sm3_update(ctx, msglen, 8); PUT_UINT32_BE(ctx->state[0], output, 0); PUT_UINT32_BE(ctx->state[1], output, 4); PUT_UINT32_BE(ctx->state[2], output, 8); PUT_UINT32_BE(ctx->state[3], output, 12); PUT_UINT32_BE(ctx->state[4], output, 16); PUT_UINT32_BE(ctx->state[5], output, 20); PUT_UINT32_BE(ctx->state[6], output, 24); PUT_UINT32_BE(ctx->state[7], output, 28);
} /* * output = SM3( input buffer ) */
void sm3(const unsigned char *input, int ilen, unsigned char output[32])
{ sm3_context ctx; sm3_starts(&ctx); sm3_update(&ctx, input, ilen); sm3_finish(&ctx, output); memset(&ctx, 0, sizeof(sm3_context));
} /* * output = SM3( file contents ) */
int sm3_file(const char *path, unsigned char output[32])
{ FILE *f; size_t n; sm3_context ctx; unsigned char buf[1024]; if ((f = fopen(path, "rb")) == NULL) return 1; sm3_starts(&ctx); while ((n = fread(buf, 1, sizeof(buf), f)) > 0) sm3_update(&ctx, buf, n); sm3_finish(&ctx, output); memset(&ctx, 0, sizeof(sm3_context)); if (ferror(f) != 0) { fclose(f); return 2; } fclose(f); return 0;
} /* * SM3 HMAC context setup */
void sm3_hmac_starts(sm3_context *ctx, const unsigned char *key, int keylen)
{ int i; unsigned char sum[32]; if (keylen > 64) { sm3(key, keylen, sum); keylen = 32; //key = sum; key = (const unsigned char*)sum; } memset(ctx->ipad, 0x36, 64); memset(ctx->opad, 0x5C, 64); for (i = 0; i < keylen; i++) { ctx->ipad[i] ^= key[i]; ctx->opad[i] ^= key[i]; } sm3_starts(ctx); sm3_update(ctx, ctx->ipad, 64); memset(sum, 0, sizeof(sum));
} /* * SM3 HMAC process buffer */
void sm3_hmac_update(sm3_context *ctx, const unsigned char *input, int ilen)
{ sm3_update(ctx, input, ilen);
} /* * SM3 HMAC final digest */
void sm3_hmac_finish(sm3_context *ctx, unsigned char output[32])
{ int hlen = 32; unsigned char tmpbuf[32]; sm3_finish(ctx, tmpbuf); sm3_starts(ctx); sm3_update(ctx, ctx->opad, 64); sm3_update(ctx, tmpbuf, hlen); sm3_finish(ctx, output); memset(tmpbuf, 0, sizeof(tmpbuf));
} /* * output = HMAC-SM3( hmac key, input buffer ) */
void sm3_hmac(const unsigned char *key, int keylen, const unsigned char *input, int ilen, unsigned char output[32])
{ sm3_context ctx; sm3_hmac_starts(&ctx, key, keylen); sm3_hmac_update(&ctx, input, ilen); sm3_hmac_finish(&ctx, output); memset(&ctx, 0, sizeof(sm3_context));
}
// sm3.h
#ifndef SM3_H
#define SM3_H #include <stdint.h> // 使用固定宽度的整数类型 #ifdef __cplusplus
extern "C" {
#endif typedef struct { uint32_t total[2]; // 消息总长度(以位为单位) uint32_t state[8]; // 哈希状态 unsigned char buffer[64]; // 数据缓冲区 // 用于 HMAC unsigned char ipad[64]; unsigned char opad[64];
} sm3_context; // SM3 函数
void sm3_starts(sm3_context *ctx);
void sm3_update(sm3_context *ctx, const unsigned char *input, int ilen);
void sm3_finish(sm3_context *ctx, unsigned char output[32]);
void sm3(const unsigned char *input, int ilen, unsigned char output[32]);
int sm3_file(const char *path, unsigned char output[32]); // SM3 HMAC 函数
void sm3_hmac_starts(sm3_context *ctx, const unsigned char *key, int keylen);
void sm3_hmac_update(sm3_context *ctx, const unsigned char *input, int ilen);
void sm3_hmac_finish(sm3_context *ctx, unsigned char output[32]);
void sm3_hmac(const unsigned char *key, int keylen, const unsigned char *input, int ilen, unsigned char output[32]); #ifdef __cplusplus
}
#endif #endif // SM3_H
[wzy@LAPTOP-PRC71A0C src]$ vim sm3.c
[wzy@LAPTOP-PRC71A0C src]$ vim sm3.h
[wzy@LAPTOP-PRC71A0C src]$ vim test.c
[wzy@LAPTOP-PRC71A0C src]$ ls
sm2gmssl sm2gmssl.c sm3.c sm3.h test.c testsm3 testsm3.c
[wzy@LAPTOP-PRC71A0C src]$ ls
sm2gmssl sm2gmssl.c sm3.c sm3.h sm4gmssl sm4gmssl.c test.c testsm3 testsm3.c
[wzy@LAPTOP-PRC71A0C src]$ vim makefile
[wzy@LAPTOP-PRC71A0C src]$ make
gcc -Wall -O2 -o test test.c sm3.c
[wzy@LAPTOP-PRC71A0C src]$ ./test
Message: abc
HMAC: ec76c401 b2ddceb3 916bdffa 0469b85f 90536ffc f4ecac77 539f3d8b 8bbe046c
[wzy@LAPTOP-PRC71A0C src]$ git add .
[wzy@LAPTOP-PRC71A0C src]$ git commit -m "sm3生成摘要和HMAC"
[master 717b04b] sm3生成摘要和HMAC2 files changed, 15 insertions(+)create mode 100644 src/makefilecreate mode 100755 src/test
# Makefile CC = gcc
CFLAGS = -Wall -O2
TARGET = test
SRCS = test.c sm3.c
HEADERS = sm3.h all: $(TARGET) $(TARGET): $(SRCS) $(HEADERS) $(CC) $(CFLAGS) -o $(TARGET) $(SRCS) clean: rm -f $(TARGET)
sm4加密解密
/* * Copyright 2014-2023 The GmSSL Project. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the License); you may * not use this file except in compliance with the License. * * http://www.apache.org/licenses/LICENSE-2.0 */ #include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include "gmssl/sm4.h"
#include "gmssl/rand.h"// 定义明文最小长度
#define PLAINTEXT_MIN_SIZE 130 int main() { // 生成随机密钥 uint8_t key[SM4_KEY_SIZE]; if (rand_bytes(key, SM4_KEY_SIZE) != 1) { fprintf(stderr, "随机密钥生成失败。\n"); return EXIT_FAILURE; } // 生成随机IV uint8_t iv[SM4_BLOCK_SIZE]; if (rand_bytes(iv, SM4_BLOCK_SIZE) != 1) { fprintf(stderr, "随机IV生成失败。\n"); return EXIT_FAILURE; } // 打印生成的密钥 printf("随机生成的密钥: "); for (size_t i = 0; i < SM4_KEY_SIZE; i++) { printf("%02X ", key[i]); } printf("\n"); // 打印生成的IV printf("随机生成的IV: "); for (size_t i = 0; i < SM4_BLOCK_SIZE; i++) { printf("%02X ", iv[i]); } printf("\n"); // 定义明文(不少于130字节) uint8_t plaintext[PLAINTEXT_MIN_SIZE]; if (rand_bytes(plaintext, PLAINTEXT_MIN_SIZE) != 1) { fprintf(stderr, "随机明文生成失败。\n"); return EXIT_FAILURE; } // 打印明文(以十六进制格式) printf("明文 (%d 字节): ", PLAINTEXT_MIN_SIZE); for (size_t i = 0; i < PLAINTEXT_MIN_SIZE; i++) { printf("%02X ", plaintext[i]); } printf("\n"); // 初始化 SM4 密钥结构 SM4_KEY enc_key, dec_key; sm4_set_encrypt_key(&enc_key, key); sm4_set_decrypt_key(&dec_key, key); // 计算加密后可能的最大长度(明文长度 + 一个块的填充) size_t plaintext_len = PLAINTEXT_MIN_SIZE; size_t max_ciphertext_len = plaintext_len + SM4_BLOCK_SIZE; uint8_t *ciphertext = malloc(max_ciphertext_len); if (ciphertext == NULL) { fprintf(stderr, "内存分配失败。\n"); return EXIT_FAILURE; } // 由于加密和解密后的明文长度可能不同,预留足够的空间 uint8_t *decryptedtext = malloc(max_ciphertext_len + 1); // +1用于终止符 if (decryptedtext == NULL) { fprintf(stderr, "内存分配失败。\n"); free(ciphertext); return EXIT_FAILURE; } size_t outlen; int ret; // 加密带填充的明文 ret = sm4_cbc_padding_encrypt(&enc_key, iv, plaintext, plaintext_len, ciphertext, &outlen); if (ret != 1) { fprintf(stderr, "加密失败。\n"); free(ciphertext); free(decryptedtext); return EXIT_FAILURE; } // 输出密文(十六进制格式) printf("密文 (%zu 字节): ", outlen); for (size_t i = 0; i < outlen; i++) { printf("%02X ", ciphertext[i]); } printf("\n"); // 解密带填充的密文 ret = sm4_cbc_padding_decrypt(&dec_key, iv, ciphertext, outlen, decryptedtext, &outlen); if (ret != 1) { fprintf(stderr, "解密失败。\n"); free(ciphertext); free(decryptedtext); return EXIT_FAILURE; } // 确保解密后的文本是以空字符结尾的字符串 decryptedtext[outlen] = '\0'; // 打印解密后的明文(以十六进制格式) printf("解密后的明文 (%zu 字节): ", outlen); for (size_t i = 0; i < outlen; i++) { printf("%02X ", decryptedtext[i]); } printf("\n"); // 验证解密后的明文是否与原始明文相同 if (memcmp(plaintext, decryptedtext, plaintext_len) == 0) { printf("解密验证成功:解密后的明文与原始明文一致。\n"); } else { printf("解密验证失败:解密后的明文与原始明文不一致。\n"); } // 释放分配的内存 free(ciphertext); free(decryptedtext); return EXIT_SUCCESS;
}
[wzy@LAPTOP-PRC71A0C src]$ vim sm4gmssl.c
[wzy@LAPTOP-PRC71A0C src]$ gcc -o sm4gmssl sm4gmssl.c -lgmssl
[wzy@LAPTOP-PRC71A0C src]$ ./sm4gmssl
随机生成的密钥: 32 13 B9 B3 E7 7E 7A 2D 2E 0D E3 93 35 AF 24 3E
随机生成的IV: 68 B6 74 B4 BA A4 EF 30 14 D0 C4 FF 1B BD AD 8B
明文 (130 字节): 29 29 DB A4 7D 81 AE 46 85 40 5C A5 18 F7 7F CA 7C 55 36 FD F0 68 7F 24 72 78 48 BE AD FF 19 23 BD B2 C3 04 22 91 F0 B3 97 F0 E3 FB 48 FE EE 22 E2 45 CF B1 8B C3 C6 71 92 3A ED 1F D4 E2 83 49 8B DE 57 B0 AB AF DC 63 D3 27 92 D0 52 41 B7 E4 8D 09 34 35 77 9A 75 E1 B9 93 DB C4 96 7C 9B A0 39 91 28 AE 73 33 4D 61 E8 F4 89 3D E3 A4 0F 94 81 C5 EC C2 09 2F FF 95 DB 3C 9A 93 86 01 40 51 9C CE
密文 (144 字节): E7 A8 25 E9 8E 66 EB B1 F2 BA FE 66 BB 3D 18 59 C4 6A 6A FE 24 58 B4 38 29 A5 0B 0A 68 7C C5 93 4C D9 93 CD 10 9C A5 54 11 76 F6 08 BC 17 77 FF 76 62 7A C5 61 E8 C2 B9 DB 0D D0 15 ED 64 59 FB 4A 9B 5E DE D4 45 41 78 AE 97 E7 2D 80 7B 9F 95 87 7B 28 2D D5 BA C5 D4 92 DC 5A 9E 5E D2 29 B0 C9 FB 5A E4 BE 87 40 B1 0B 17 78 AC 40 BF D6 E7 63 08 1B 2F BA 42 33 97 5F CA FE BF 72 16 E6 0A E2 96 2A 4D 5D 75 CB 26 25 55 6A 3A F1 8C A1 DC
解密后的明文 (130 字节): 29 29 DB A4 7D 81 AE 46 85 40 5C A5 18 F7 7F CA 7C 55 36 FD F0 68 7F 24 72 78 48 BE AD FF 19 23 BD B2 C3 04 22 91 F0 B3 97 F0 E3 FB 48 FE EE 22 E2 45 CF B1 8B C3 C6 71 92 3A ED 1F D4 E2 83 49 8B DE 57 B0 AB AF DC 63 D3 27 92 D0 52 41 B7 E4 8D 09 34 35 77 9A 75 E1 B9 93 DB C4 96 7C 9B A0 39 91 28 AE 73 33 4D 61 E8 F4 89 3D E3 A4 0F 94 81 C5 EC C2 09 2F FF 95 DB 3C 9A 93 86 01 40 51 9C CE
解密验证成功:解密后的明文与原始明文一致。
[wzy@LAPTOP-PRC71A0C src]$ git add .
[wzy@LAPTOP-PRC71A0C src]$ git commit -m "sm4加密解密"
[master 2218d74] sm4加密解密7 files changed, 614 insertions(+)create mode 100644 src/sm3.ccreate mode 100644 src/sm3.hcreate mode 100755 src/sm4gmsslcreate mode 100644 src/sm4gmssl.ccreate mode 100644 src/test.ccreate mode 100755 src/testsm3create mode 100644 src/testsm3.c
3. 两人一组,在 Ubuntu或openEuler中(推荐 openEuler)中使用OpenSSL编程实现带签名的数字信封协议。使用OpenSSL库时,Alice发送,Bob接收。Ailice,Bob在实验中要替换为自己的8位学号+姓名。 使用Markdown记录详细记录实践过程,每完成一项git commit 一次。(5分)
- Alice,Bob生成自己的公私钥匙对,记作:(PKa,SKa),(PKb,SKb),Alice,Bob分别拥有:(PKa,SKa,PKb),(PKb,SKb,PKa),实验中把公钥文件拷贝给对方
- Alice发给Bob的明文plain.txt,内容为自己的姓名学号
- Alice:sm4 key使用gmssl rand 产生,16字节,记作k
- Alice:Sm4Enc(k,P) = C
- Alice:Sm2Enc(PKb,k) = KC
- Alice:Sm2Sign(SKa,C)= S1
- Alice: 数字信封 C||KC||S1 发给Bob
- Bob:Sm2Very(PKa,S1)
- Bob:Sm2Dec(SKb,KC)= k
- Bob:Sm4Dec(k,C)= P
4. 两人一组,在 Ubuntu或openEuler中(推荐 openEuler)中使用GmSSL编程实现带签名的数字信封协议。使用GmSSL库时,Bob发送,Alice接收。Ailice,Bob在实验中要替换为自己的8位学号+姓名。 使用Markdown记录详细记录实践过程,每完成一项git commit 一次。(5分)
生成公私钥对
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <gmssl/sm2.h>
#include <gmssl/sm3.h>
#include <gmssl/error.h>// 辅助函数:以十六进制格式打印字节数组
void print_hex(const char *label, const uint8_t *data, size_t len) {printf("%s: ", label);for(size_t i = 0; i < len; i++) {printf("%02X", data[i]);}printf("\n");
}int main() {int ret = 0;// 1. 生成 SM2 密钥对SM2_KEY key;printf("生成 SM2 密钥对...\n");ret = sm2_key_generate(&key);if (ret != 1) {fprintf(stderr, "SM2 密钥生成失败\n");return EXIT_FAILURE;}printf("SM2 密钥生成成功。\n");
[wzy@LAPTOP-PRC71A0C digital-envelope]$ vim generate_sm2_keys.c
[wzy@LAPTOP-PRC71A0C digital-envelope]$ gcc -o generate_sm2_keys generate_sm2_keys.c -lgmssl
[wzy@LAPTOP-PRC71A0C digital-envelope]$ ls
generate_sm2_keys generate_sm2_keys.c private_key.pem public_key.pem
[wzy@LAPTOP-PRC71A0C digital-envelope]$ vim generate_sm2_keys.c
[wzy@LAPTOP-PRC71A0C digital-envelope]$ git init
Reinitialized existing Git repository in /home/wzy/exp1/exp3/digital-envelope/.git/
[wzy@LAPTOP-PRC71A0C digital-envelope]$ git add .
[wzy@LAPTOP-PRC71A0C digital-envelope]$ git commit -m "生成sm2公私钥对"
[master (root-commit) fa1ca63] 生成sm2公私钥对4 files changed, 72 insertions(+)create mode 100755 generate_sm2_keyscreate mode 100644 generate_sm2_keys.ccreate mode 100644 private_key.pemcreate mode 100644 public_key.pem
明文文件
[wzy@LAPTOP-PRC71A0C digital-envelope]$ vim plain.txt
20221417wzy
读取明文加密
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include "gmssl/sm4.h"
#include "gmssl/rand.h"// 定义明文最小长度
#define PLAINTEXT_MIN_SIZE 130int main() {// 生成随机密钥uint8_t key[SM4_KEY_SIZE];if (rand_bytes(key, SM4_KEY_SIZE) != 1) {fprintf(stderr, "随机密钥生成失败。\n");return EXIT_FAILURE;}// 生成随机IVuint8_t iv[SM4_BLOCK_SIZE];if (rand_bytes(iv, SM4_BLOCK_SIZE) != 1) {fprintf(stderr, "随机IV生成失败。\n");return EXIT_FAILURE;}// 保存密钥和IV到文件FILE *key_file = fopen("sm4_key.bin", "wb");if (key_file == NULL) {fprintf(stderr, "无法打开密钥文件。\n");return EXIT_FAILURE;}fwrite(key, 1, SM4_KEY_SIZE, key_file);fwrite(iv, 1, SM4_BLOCK_SIZE, key_file);fclose(key_file);// 读取明文文件FILE *plain_file = fopen("plain.txt", "rb");if (plain_file == NULL) {fprintf(stderr, "无法打开明文文件。\n");return EXIT_FAILURE;}fseek(plain_file, 0, SEEK_END);size_t plaintext_len = ftell(plain_file);fseek(plain_file, 0, SEEK_SET);uint8_t *plaintext = malloc(plaintext_len);if (plaintext == NULL) {fprintf(stderr, "内存分配失败。\n");fclose(plain_file);return EXIT_FAILURE;}fread(plaintext, 1, plaintext_len, plain_file);fclose(plain_file);// 初始化 SM4 密钥结构SM4_KEY enc_key, dec_key;sm4_set_encrypt_key(&enc_key, key);sm4_set_decrypt_key(&dec_key, key);// 计算加密后可能的最大长度(明文长度 + 一个块的填充)size_t max_ciphertext_len = plaintext_len + SM4_BLOCK_SIZE;uint8_t *ciphertext = malloc(max_ciphertext_len);if (ciphertext == NULL) {fprintf(stderr, "内存分配失败。\n");free(plaintext);return EXIT_FAILURE;}size_t outlen;int ret;// 加密带填充的明文ret = sm4_cbc_padding_encrypt(&enc_key, iv, plaintext, plaintext_len, ciphertext, &outlen);if (ret != 1) {fprintf(stderr, "加密失败。\n");free(plaintext);free(ciphertext);return EXIT_FAILURE;}// 保存密文到文件FILE *cipher_file = fopen("cipher.bin", "wb");if (cipher_file == NULL) {fprintf(stderr, "无法打开密文文件。\n");free(plaintext);free(ciphertext);return EXIT_FAILURE;}fwrite(ciphertext, 1, outlen, cipher_file);fclose(cipher_file);// 释放分配的内存free(plaintext);free(ciphertext);return EXIT_SUCCESS;
}
[root@LAPTOP-PRC71A0C digital-envelope]# vim sm4enc.c
[root@LAPTOP-PRC71A0C digital-envelope]# gcc -o sm4enc sm4enc.c -lgmssl
[root@LAPTOP-PRC71A0C digital-envelope]# ./sm4enc
[root@LAPTOP-PRC71A0C digital-envelope]# cat cipher.bin
��q��V|[�SC��
对密文生成签名
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <gmssl/sm2.h>
#include <gmssl/sm3.h>
#include <gmssl/error.h>int main() {int ret = 0;// 1. 加载私钥SM2_KEY key;FILE *fp = fopen("private_key.pem", "r");if (!fp) {fprintf(stderr, "无法打开私钥文件\n");return EXIT_FAILURE;}ret = sm2_private_key_info_from_pem(&key, fp);fclose(fp);if (ret != 1) {fprintf(stderr, "加载私钥失败\n");return EXIT_FAILURE;}printf("私钥加载成功。\n");// 2. 读取密文FILE *cipher_fp = fopen("cipher.bin", "rb");if (!cipher_fp) {fprintf(stderr, "无法打开密文文件\n");return EXIT_FAILURE;}fseek(cipher_fp, 0, SEEK_END);size_t ciphertext_len = ftell(cipher_fp);fseek(cipher_fp, 0, SEEK_SET);uint8_t ciphertext[ciphertext_len];fread(ciphertext, 1, ciphertext_len, cipher_fp);fclose(cipher_fp);printf("密文读取成功。\n");// 3. 生成签名SM2_SIGN_CTX sign_ctx;sm2_sign_init(&sign_ctx, &key, SM2_DEFAULT_ID, SM2_DEFAULT_ID_LENGTH);ret = sm2_sign_update(&sign_ctx, ciphertext, ciphertext_len);if (ret != 1) {fprintf(stderr, "SM2 签名更新失败\n");return EXIT_FAILURE;}uint8_t signature[SM2_MAX_SIGNATURE_SIZE];size_t signature_len = sizeof(signature);ret = sm2_sign_finish(&sign_ctx, signature, &signature_len);if (ret != 1) {fprintf(stderr, "SM2 签名失败\n");return EXIT_FAILURE;}printf("签名成功。\n");// 4. 保存签名FILE *sig_fp = fopen("signature.bin", "wb");if (!sig_fp) {fprintf(stderr, "无法打开签名文件\n");return EXIT_FAILURE;}fwrite(signature, 1, signature_len, sig_fp);fclose(sig_fp);printf("签名保存成功。\n");return EXIT_SUCCESS;
}
[wzy@LAPTOP-PRC71A0C digital-envelope]$ vim sm2_sig.c
[wzy@LAPTOP-PRC71A0C digital-envelope]$ gcc -o sm2_sig sm2_sig.c -lgmssl
[root@LAPTOP-PRC71A0C digital-envelope]# ./sm2_sig
私钥加载成功。
密文读取成功。
签名成功。
签名保存成功。
[root@LAPTOP-PRC71A0C digital-envelope]# ls
cipher.bin generate_sm2_keys.c private_key.pem signature.bin sm2_sig.c sm4enc.c
generate_sm2_keys plain.txt public_key.pem sm2_sig sm4enc sm4_key.bin
[root@LAPTOP-PRC71A0C digital-envelope]# git add .
[root@LAPTOP-PRC71A0C digital-envelope]# git add signature.bin
[root@LAPTOP-PRC71A0C digital-envelope]# git commit -m "私钥对密文生成签名"
[master d63ffba] 私钥对密文生成签名Committer: Super User <root@LAPTOP-PRC71A0C>
Your name and email address were configured automatically based
on your username and hostname. Please check that they are accurate.
You can suppress this message by setting them explicitly. Run the
following command and follow the instructions in your editor to edit
your configuration file:git config --global --editAfter doing this, you may fix the identity used for this commit with:git commit --amend --reset-author7 files changed, 167 insertions(+)create mode 100644 cipher.bincreate mode 100644 signature.bincreate mode 100755 sm2_sigcreate mode 100644 sm2_sig.ccreate mode 100644 sm4_key.bincreate mode 100755 sm4enccreate mode 100644 sm4enc.c
使用Bob的公钥对sm4的密钥进行加密
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <gmssl/sm2.h>
#include <gmssl/error.h>
#include <gmssl/sm4.h>
int load_sm2_public_key(const char *filename, SM2_KEY *pub_key) {FILE *fp = fopen(filename, "r");if (!fp) {fprintf(stderr, "Error opening file %s\n", filename);return -1;}int ret = sm2_public_key_info_from_pem(pub_key, fp);fclose(fp);if (ret != 1) {fprintf(stderr, "Error loading SM2 public key\n");return -1;}return 0;
}int load_sm4_key(const char *filename, uint8_t *sm4_key, size_t *key_len) {FILE *fp = fopen(filename, "rb");if (!fp) {fprintf(stderr, "Error opening file %s\n", filename);return -1;}*key_len = fread(sm4_key, 1, SM4_KEY_SIZE, fp);fclose(fp);if (*key_len != SM4_KEY_SIZE) {fprintf(stderr, "Error reading SM4 key\n");return -1;}return 0;
}int encrypt_sm4_key_with_sm2(const SM2_KEY *pub_key, const uint8_t *sm4_key, size_t sm4_key_len, uint8_t *enc_key, size_t *enc_key_len) {int ret = sm2_encrypt(pub_key, sm4_key, sm4_key_len, enc_key, enc_key_len);if (ret != 1) {fprintf(stderr, "Error encrypting SM4 key with SM2\n");return -1;}return 0;
}int save_encrypted_key(const char *filename, const uint8_t *enc_key, size_t enc_key_len) {FILE *fp = fopen("enc_key.bin", "wb");if (!fp) {fprintf(stderr, "Error opening file %s\n", filename);return -1;}size_t written = fwrite(enc_key, 1, enc_key_len, fp);fclose(fp);if (written != enc_key_len) {fprintf(stderr, "Error writing encrypted key to file\n");return -1;}return 0;
}int main(int argc, char *argv[]) {if (argc != 4) {fprintf(stderr, "Usage: %s <sm2_public_key.pem> <sm4_key.bin> <output_encrypted_key.bin>\n", argv[0]);return -1;}SM2_KEY pub_key;uint8_t sm4_key[SM4_KEY_SIZE];size_t sm4_key_len;uint8_t enc_key[SM2_MAX_PLAINTEXT_SIZE];size_t enc_key_len;if (load_sm2_public_key(argv[1], &pub_key) != 0) {return -1;}if (load_sm4_key(argv[2], sm4_key, &sm4_key_len) != 0) {return -1;}if (encrypt_sm4_key_with_sm2(&pub_key, sm4_key, sm4_key_len, enc_key, &enc_key_len) != 0) {return -1;}if (save_encrypted_key(argv[3], enc_key, enc_key_len) != 0) {return -1;}printf("Encrypted key saved to %s\n", argv[3]);return 0;
}
[root@LAPTOP-PRC71A0C digital-envelope]# vim sm4_enc_key.c
[root@LAPTOP-PRC71A0C digital-envelope]# gcc -o sm4_enc_key sm4_enc_key.c -lgmssl
[root@LAPTOP-PRC71A0C digital-envelope]# ./sm4_enc_key public_key.pem sm4_key.bin enc_key.bin
[root@LAPTOP-PRC71A0C digital-envelope]# ./sm4_enc_key public_key.pem sm4_key.bin enc_key.bin
Encrypted key saved to enc_key.bin
之后将工作交给bob,将签名、密文、加密的密钥发送。
5. 使用Rust完成带签名的数字信封协议(选做,10分)
6. 实验记录中提交 gitee 课程项目链接,提交本次实验相关 git log运行结果
[root@LAPTOP-PRC71A0C digital-envelope]# git log
commit 911a498de579c3399f58a964e086a286c606cb6d (HEAD -> master)
Author: Super User <root@LAPTOP-PRC71A0C>
Date: Sun Oct 20 18:56:04 2024 +0800使用20221412公钥对sm4对称密钥进行加密commit d63ffba96f975885d17b5435e85baebb25f64c52
Author: Super User <root@LAPTOP-PRC71A0C>
Date: Sun Oct 20 17:07:55 2024 +0800私钥对密文生成签名commit a45290f34b70d821a054717dcc87c37deef98a26
Author: wei-zhengyi <https://gitee.com/wei-zhengyi/projects>
Date: Sun Oct 20 14:25:36 2024 +0800生成明文文件plain.txtcommit fa1ca63aa25854753e191f8bf460ceffe93093c3
Author: wei-zhengyi <https://gitee.com/wei-zhengyi/projects>
Date: Sun Oct 20 14:20:21 2024 +0800生成sm2公私钥对
7. 提交要求:
- 提交实践过程Markdown和转化的PDF文件
- 代码,文档托管到gitee或github等,推荐 gitclone
- 记录实验过程中遇到的问题,解决过程,反思等内容,用于后面实验报告