铜锁探“密”训练营实验手册
铜锁探“密”活动由开放原子开源基金会和铜锁社区共同举办,包含5次课程,以“抽丝剥茧,循序渐进,一起揭开商用密码的面纱”为主题,旨在让参与者更加深入地了解商用密码的原理和应用方法。 录播链接如下:
- 商用密码介绍和铜锁密码库入门,https://live.csdn.net/room/csdnnews/NJPxluNk
- 常用国密算法编程入门与实战,https://live.csdn.net/room/csdnnews/hZel8pLF
- 实战国密证书和国密传输协议,https://live.csdn.net/room/csdnnews/8fs5oVfT
- 实战铜锁国密应用和结营作业说明,https://live.csdn.net/room/csdnnews/BzeD1mGt
- 训练营结营&作品点评,https://live.csdn.net/room/csdnnews/fsZDshwb
实验环境说明
实验手册中大部分实验以docker环境为主,基于Ubuntu 20.04容器镜像。或者直接运行于Ubuntu 20.04操作系统或虚拟机也可以。比如电脑使用的Windows系统,可以通过安装Docker环境、Linux虚拟机、或者Linux子系统进行实验。其他Linux系统或者macOS系统可以参考本实验内容。
安装docker:
可以参考docker官方安装教程,详见https://docs.docker.com/engine/install/。
创建docker容器:
docker run -d -it --name tongsuolab ubuntu:20.04 bash
执行所有实验,都需要先进入docker容器。进入docker容器:
docker exec -it tongsuolab bash
其他软件,直接在操作系统上安装即可。
- 支持国密协议的浏览器,比如360企业安全浏览器。
- Wireshark,用于抓包分析国密协议。
构建铜锁密码库
基于铜锁密码库源代码进行构建和安装。代码下载地址:
AtomGit地址(推荐):https://atomgit.com/tongsuo/Tongsuo
GitHub地址:https://github.com/Tongsuo-Project/Tongsuo
下载代码需要使用git工具;构建铜锁密码库需要使用perl、gcc、make等基础开发工具。
安装依赖:
# 需要先更新软件包索引
apt update
apt install git gcc make -y
进入Shell终端执行以下命令:
git clone https://github.com/Tongsuo-Project/Tongsuo
cd Tongsuo
./config --prefix=/opt/tongsuo enable-ntls enable-ssl-trace -Wl,-rpath,/opt/tongsuo/lib64 --debug
make -j
make install
查看安装情况:
ls -l /opt/tongsuo
产看铜锁版本,执行如下命令:
/opt/tongsuo/bin/tongsuo version
SM2&SM3&SM4算法实战
实战SM4加解密算法
echo "hello tongsuo" > msg.bin
# SM4-CBC加密
/opt/tongsuo/bin/tongsuo enc -K "3f342e9d67d6ce7be701756af7bac8f2" -e -sm4-cbc -in msg.bin -iv "1fb2d42fb36e2e88a220b04f2e49aa13" -nosalt -out cipher.bin
# SM4-CBC解密
/opt/tongsuo/bin/tongsuo enc -K "3f342e9d67d6ce7be701756af7bac8f2" -d -sm4-cbc -in cipher.bin -iv "1fb2d42fb36e2e88a220b04f2e49aa13" -nosalt -out msg2.bin
# 比较解密的明文和原来的消息是否一样
diff msg.bin msg2.bin
实战SM3杂凑算法
echo -n "hello tongsuo" | /opt/tongsuo/bin/tongsuo dgst -sm3
结果如下:
实战SM2签名和验签
# 生成一个随机内容文件
dd if=/dev/urandom of=msg.bin bs=1024 count=1
# SM2私钥签名,签名算法为SM2withSM3,Tongsuo/test/certs/sm2.key来自Tongsuo源代码仓库
/opt/tongsuo/bin/tongsuo dgst -sm3 -sign Tongsuo/test/certs/sm2.key -out sigfile msg.bin
# SM2公钥验签,Tongsuo/test/certs/sm2pub.key来自Tongsuo源代码仓库
/opt/tongsuo/bin/tongsuo dgst -sm3 -verify Tongsuo/test/certs/sm2pub.key -signature sigfile msg.bin
签名正确时,验证成功可以看到:
SM2&SM3&SM4算法编程入门
SM4加解密算法编程入门
SM4加密
#include <openssl/evp.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
static int sm4_enc(const unsigned char *key, const unsigned char *iv,
unsigned char *in, int inlen, unsigned char *out,
int *outlen)
{
EVP_CIPHER_CTX *ctx = NULL;
int outl, tmplen;
if ((ctx = EVP_CIPHER_CTX_new()) == NULL
|| !EVP_EncryptInit_ex(ctx, EVP_sm4_cbc(), NULL, key, iv)
|| !EVP_EncryptUpdate(ctx, out, &outl, in, inlen)
|| !EVP_EncryptFinal_ex(ctx, out + outl, &tmplen)) {
EVP_CIPHER_CTX_free(ctx);
return 0;
}
EVP_CIPHER_CTX_free(ctx);
if (outlen)
*outlen = outl + tmplen;
return 1;
}
int main()
{
unsigned char key[] = {
0x3f, 0x34, 0x2e, 0x9d, 0x67, 0xd6, 0xce, 0x7b,
0xe7, 0x01, 0x75, 0x6a, 0xf7, 0xba, 0xc8, 0xf2,};
unsigned char iv[] = {
0x1f, 0xb2, 0xd4, 0x2f, 0xb3, 0x6e, 0x2e, 0x88,
0xa2, 0x20, 0xb0, 0x4f, 0x2e, 0x49, 0xaa, 0x13,};
unsigned char in[] = "hello tongsuo";
unsigned char *out = NULL;
int outlen;
int ret;
out = malloc(sizeof(in) + EVP_MAX_BLOCK_LENGTH);
assert(out != NULL);
ret = sm4_enc(key, iv, in, strlen(in), out, &outlen);
assert(ret == 1);
for (size_t i = 0; i < outlen; i++)
printf("%x", out[i]);
printf("\n");
free(out);
return 0;
}
将内容保存为sm4_enc.c,并编译运行:
gcc sm4_enc.c -I/opt/tongsuo/include -L/opt/tongsuo/lib64 -lcrypto -Wl,-rpath=/opt/tongsuo/lib64
./a.out
输出明文消息的密文如下:
SM4解密:
#include <openssl/evp.h>
#include <openssl/err.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
static int sm4_dec(const unsigned char *key, const unsigned char *iv,
unsigned char *in, int inlen, unsigned char *out,
int *outlen)
{
EVP_CIPHER_CTX *ctx = NULL;
int outl, tmplen;
if ((ctx = EVP_CIPHER_CTX_new()) == NULL
|| !EVP_DecryptInit_ex(ctx, EVP_sm4_cbc(), NULL, key, iv)
|| !EVP_DecryptUpdate(ctx, out, &outl, in, inlen)
|| !EVP_DecryptFinal_ex(ctx, out + outl, &tmplen)) {
ERR_print_errors_fp(stderr);
EVP_CIPHER_CTX_free(ctx);
return 0;
}
EVP_CIPHER_CTX_free(ctx);
if (outlen)
*outlen = outl + tmplen;
return 1;
}
int main()
{
unsigned char key[] = {
0x3f, 0x34, 0x2e, 0x9d, 0x67, 0xd6, 0xce, 0x7b,
0xe7, 0x01, 0x75, 0x6a, 0xf7, 0xba, 0xc8, 0xf2,};
unsigned char iv[] = {
0x1f, 0xb2, 0xd4, 0x2f, 0xb3, 0x6e, 0x2e, 0x88,
0xa2, 0x20, 0xb0, 0x4f, 0x2e, 0x49, 0xaa, 0x13,};
unsigned char in[] = {
0xe2, 0x44, 0xdb, 0xeb, 0x97, 0x58, 0x83, 0x1e,
0xa8, 0x7b, 0x7c, 0xeb, 0x27, 0x8e, 0x6e, 0x5d,};
unsigned char *out = NULL;
int outlen;
int ret;
out = malloc(sizeof(in));
assert(out != NULL);
ret = sm4_dec(key, iv, in, sizeof(in), out, &outlen);
assert(ret == 1);
printf("%*s\n", outlen, out);
free(out);
return 0;
}
// gcc sm4_dec.c -I/opt/tongsuo/include -L/opt/tongsuo/lib64 -lcrypto -Wl,-rpath=/opt/tongsuo/lib64
保存内容为sm4_dec.c,编译并执行:
gcc sm4_dec.c -I/opt/tongsuo/include -L/opt/tongsuo/lib64 -lcrypto -Wl,-rpath=/opt/tongsuo/lib64
./a.out
输出如下:
SM3杂凑算法编程入门
计算SM3杂凑:
#include <openssl/evp.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
static int sm3(const unsigned char *in, size_t inlen, unsigned char *out)
{
EVP_MD_CTX *mctx = NULL;
if ((mctx = EVP_MD_CTX_new()) == NULL
|| !EVP_DigestInit_ex(mctx, EVP_sm3(), NULL)
|| !EVP_DigestUpdate(mctx, in, inlen)
|| !EVP_DigestFinal_ex(mctx, out, NULL)) {
EVP_MD_CTX_free(mctx);
return 0;
}
EVP_MD_CTX_free(mctx);
return 1;
}
int main()
{
unsigned char in[] = "hello tongsuo";
unsigned char out[EVP_MAX_MD_SIZE];
int ret;
ret = sm3(in, strlen(in), out);
assert(ret == 1);
for (int i = 0; i < EVP_MD_size(EVP_sm3()); i++)
printf("%x", out[i]);
printf("\n");
return 0;
}
保存内容为sm3.c,编译运行如下:
gcc sm3.c -I/opt/tongsuo/include -L/opt/tongsuo/lib64 -lcrypto -Wl,-rpath=/opt/tongsuo/lib64
./a.out
运行结果如下:
SM2签名算法编程入门
SM2签名:
#include <openssl/evp.h>
#include <openssl/err.h>
#include <openssl/pem.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
static int sm2_sign(EVP_PKEY *pkey, const unsigned char *in, size_t inlen,
unsigned char *out, size_t *outlen)
{
EVP_MD_CTX *mctx = NULL;
if ((mctx = EVP_MD_CTX_new()) == NULL
|| !EVP_DigestSignInit(mctx, NULL, EVP_sm3(), NULL, pkey)
|| !EVP_DigestSign(mctx, out, outlen, in, inlen))
{
ERR_print_errors_fp(stderr);
EVP_MD_CTX_free(mctx);
return 0;
}
EVP_MD_CTX_free(mctx);
return 1;
}
int main()
{
unsigned char msg[] = "hello tongsuo";
unsigned char *sig = NULL;
EVP_PKEY *pkey = NULL;
BIO *bio = NULL;
size_t siglen;
unsigned char privkey[] =
"-----BEGIN PRIVATE KEY-----\n"
"MIGHAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBG0wawIBAQQgSKhk+4xGyDI+IS2H\n"
"WVfFPDxh1qv5+wtrddaIsGNXGZihRANCAAQwqeNkWp7fiu1KZnuDkAucpM8piEzE\n"
"TL1ymrcrOBvv8mhNNkeb20asbWgFQI2zOrSM99/sXGn9rM2/usM/Mlca\n"
"-----END PRIVATE KEY-----\n";
int ret;
bio = BIO_new_mem_buf(privkey, -1);
assert(bio != NULL);
pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
assert(pkey != NULL);
siglen = EVP_PKEY_size(pkey);
sig = malloc(siglen);
ret = sm2_sign(pkey, msg, strlen((const char *)msg), sig, &siglen);
assert(ret == 1);
for (size_t i = 0; i < siglen; i++)
printf("%02x", sig[i]);
printf("\n");
EVP_PKEY_free(pkey);
BIO_free(bio);
free(sig);
return 0;
}
保存代码到文件sm2_sign.c,编译并运行
gcc sm2_sign.c -I/opt/tongsuo/include -L/opt/tongsuo/lib64 -lcrypto -Wl,-rpath=/opt/tongsuo/lib64
./a.out
输出签名结果:
SM2验签:
#include <openssl/evp.h>
#include <openssl/err.h>
#include <openssl/pem.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
static int sm2_verify(EVP_PKEY *pkey, const unsigned char *msg, size_t msglen,
unsigned char *sig, size_t siglen)
{
EVP_MD_CTX *mctx = NULL;
if ((mctx = EVP_MD_CTX_new()) == NULL
|| EVP_DigestVerifyInit(mctx, NULL, EVP_sm3(), NULL, pkey) != 1
|| EVP_DigestVerify(mctx, sig, siglen, msg, msglen) != 1)
{
ERR_print_errors_fp(stderr);
EVP_MD_CTX_free(mctx);
return 0;
}
EVP_MD_CTX_free(mctx);
return 1;
}
int main()
{
unsigned char msg[] = "hello tongsuo";
unsigned char sig[] = {
0x30, 0x44, 0x02, 0x20, 0x64, 0x68, 0xab, 0xee, 0x05, 0xa0, 0x46, 0xef,
0xe2, 0xcd, 0x05, 0x79, 0xa2, 0xa2, 0xe8, 0x9a, 0xf2, 0x70, 0xc4, 0xa3,
0x36, 0x6b, 0xd3, 0x37, 0x2c, 0xee, 0x9a, 0x7f, 0x26, 0x2b, 0x61, 0x01,
0x02, 0x20, 0x73, 0x51, 0x81, 0x60, 0x40, 0xfc, 0x10, 0x32, 0xde, 0xd0,
0x57, 0x4b, 0x43, 0xbb, 0xe8, 0xf0, 0x92, 0x6d, 0x48, 0x24, 0x24, 0x32,
0x6d, 0x1a, 0x52, 0xb2, 0xb0, 0x4e, 0x8a, 0xb5, 0x55, 0x80,};
EVP_PKEY *pkey = NULL;
BIO *bio = NULL;
unsigned char pubkey[] =
"-----BEGIN PUBLIC KEY-----\n"
"MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAEMKnjZFqe34rtSmZ7g5ALnKTPKYhM\n"
"xEy9cpq3Kzgb7/JoTTZHm9tGrG1oBUCNszq0jPff7Fxp/azNv7rDPzJXGg==\n"
"-----END PUBLIC KEY-----\n";
int ret;
bio = BIO_new_mem_buf(pubkey, -1);
assert(bio != NULL);
pkey = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL);
assert(pkey != NULL);
ret = sm2_verify(pkey, msg, strlen((const char *)msg), sig, sizeof(sig));
assert(ret == 1);
EVP_PKEY_free(pkey);
BIO_free(bio);
return 0;
}
// gcc sm2_verify.c -I/opt/tongsuo/include -L/opt/tongsuo/lib64 -lcrypto -Wl,-rpath=/opt/tongsuo/lib64
保存代码为sm2_verify.c,编译并运行:
gcc sm2_verify.c -I/opt/tongsuo/include -L/opt/tongsuo/lib64 -lcrypto -Wl,-rpath=/opt/tongsuo/lib64
./a.out
运行没有报错,说明验证签名成功。