国密算法

1. 概述

国密算法是我国自主研发创新的一套数据加密处理系列算法,从sm1到sm4分别实现了对称、非对称、摘要、对称算法功能,普遍适用于加解密,签名,摘要等场景。国密算法中,无论是sm4和经典的aes比较,或是sm2和经典的rsa比较,抑或是sm3和md5比较,都要更加的快速且更加的安全。

2. 介绍

SM1

sm1算法为对称加密,其加密强度与AES相当,该算法并未开源,调用该算法时,需要通过加密芯片接口进行调用。

SM2

sm2为非对称加密,基于ECC(椭圆加密算法),签名速度与密钥生成速度都比RSA要快。sm2采用了ECC 256bit的一种,安全强度比RSA 2048bit都要高,并且运算速度也要快于RSA。

SM3

sm3为摘要算法,摘要结果的长度为256bit

SM4

sm4为对称加密算法,密钥长度和分组长度均为128bit,该算法为无线局域网产品可以使用的加密算法。

3. 使用

在这里我使用的语言是golang,使用到的开源库为gmsm​,其为苏州同济区块链研究院有限公司版权所有的开源库,并且本次使用到的案例也大部分取自官方的教程。

Github仓库地址:https://github.com/tjfoc/gmsm

该仓库的官方介绍如下:

GM SM2/3/4 library based on Golang-Feature

由于sm1算法是非开源的算法,因此自然是不会有该算法的使用教程。

SM2

生成sm2公私钥对并使用公钥进行加密,使用私钥进行解密

这里需要主要的一点是,GenerateKey()​函数的返回值是私钥类,同时,私钥类为公钥类的继承类,也可以理解为,公钥类对象包含在私钥类之中为私钥类的成员。

func testSM2_1() {
	priv, err := sm2.GenerateKey(rand.Reader) // 生成密钥对
	if err != nil {
		log.Fatal(err)
	}
	msg := []byte("Hello World")
	pub := &priv.PublicKey
  
	ciphertxt, err := pub.EncryptAsn1(msg, rand.Reader) // sm2公钥加密
	if err != nil {
		log.Fatalf("公钥加密错误: %v", err)
	}
	log.Printf("加密结果: %x", ciphertxt)
	plaintxt, err := priv.DecryptAsn1(ciphertxt)
	if err != nil {
		log.Fatalf("私钥解密错误: %v", err)
	}
	if !bytes.Equal(msg, plaintxt) {
		log.Fatalf("原文与密文解密后的内容不匹配")
	}
}

使用私钥进行签名,公钥进行验签:

func testSM2_2() {
	priv, err := sm2.GenerateKey(rand.Reader) // 生成密钥对
	if err != nil {
		log.Fatal(err)
	}
	msg := []byte("Hello World")
	pub := &priv.PublicKey
  
	sign, err := priv.Sign(rand.Reader, msg, nil) // sm2私钥签名
	if err != nil {
		log.Fatalf("私钥签名错误: %v", err)
	}
	ok := pub.Verify(msg, sign) // sm2公钥验签
	log.Printf("验签结果: %v", ok)
}

另外还有一个特殊的应用场景是需要对公私钥进行保存或传输,我使用过的方法是将公私钥转为byte数组再使用base64进行编码,随后进行传输。

将公私钥转为byte数组并使用base64进行编码:

func testSM2_3() {
	priv, err := sm2.GenerateKey(rand.Reader) // 生成密钥对
	if err != nil {
		log.Fatal(err)
	}

	pubBytes, _ := x509.MarshalSm2PublicKey(&priv.PublicKey) // 将公钥转为byte数组导出
	log.Printf("将公钥转为[]byte: %s", base64.StdEncoding.EncodeToString(pubBytes)) // 以base64编码输出

	privBytes, _ := x509.MarshalSm2PrivateKey(priv, nil) // 将私钥转为byte数组导出
	log.Printf("将私钥转为[]byte: %s", base64.StdEncoding.EncodeToString(privBytes)) // 以base64编码输出
}

将base64编码后的公私钥转为公私钥对象并使用(这里使用到的base64编码的字符串为上例中输出的结果):

func testSM2_4() {
	// 将base64编码后的结果转回byte数组
	pubBytes, err := base64.StdEncoding.DecodeString("MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAEdmRUpvzg23H1LBgVM4GC0SpbOSFZ1HUYqlAnAOzKR3TKhAbYmjLw7TJ96IWLHQ9hPvY4dIlmBJ3LFd3CpupcWw==")
	if err != nil {
		log.Fatalf("base64解码错误: %v", err)
	}
	privBytes, err := base64.StdEncoding.DecodeString("MIGTAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBHkwdwIBAQQgl9cZTl8l/Ct+DhILxvSbA1+df7CPD1qccu4eUiFtatWgCgYIKoEcz1UBgi2hRANCAAR2ZFSm/ODbcfUsGBUzgYLRKls5IVnUdRiqUCcA7MpHdMqEBtiaMvDtMn3ohYsdD2E+9jh0iWYEncsV3cKm6lxb")
	if err != nil {
		log.Fatalf("base64解码错误: %v", err)
	}

	// 将公钥byte数组转为公钥对象
	pub, err := x509.ParseSm2PublicKey(pubBytes)
	if err != nil {
		log.Fatalf("转化公钥发生错误: %v", err)
	}
	// 将私钥byte数组转为私钥对象
	priv, err := x509.ParsePKCS8UnecryptedPrivateKey(privBytes)
	if err != nil {
		log.Fatalf("转化私钥发生错误: %v", err)
	}

	msg := []byte("Hello World")
	// 加密测试
	encrypt, err := pub.EncryptAsn1(msg, rand.Reader)
	if err != nil {
		log.Fatalf("公钥加密错误: %v", err)
	}
	log.Printf("加密后内容: %x", encrypt)
	// 解密测试
	decrypt, err := priv.DecryptAsn1(encrypt)
	if err != nil {
		log.Fatalf("私钥解密错误: %v", err)
	}
	log.Printf("解密后内容: %s", string(decrypt))
}

特别注意!

在将公私钥对象转为byte数组的时候,可以看到使用的函数是x509​包中提供的MarshalSm2PublicKey()​和MarshalSm2PrivateKey()​,但是在将byte数组转回到公私钥对象的时候,使用到的函数分别是x509​包中的ParseSm2PublicKey()​和ParsePKCS8UnecryptedPrivateKey()​,需要特别注意的就是私钥的转换!

SM3

func testSM3() {
	data := "Hello World"
	h := sm3.New() // 生成一个sm3摘要对象
	h.Write([]byte(data)) // 向对象中写入内容
	sum := h.Sum(nil) // 计算摘要结果
	log.Printf("摘要结果: %x", sum)
}

SM4

func testSM4() {
	data := "Hello World"
	log.Printf("data: %v", data)

	key := []byte("1234567890abcdef") // sm4算法加密密钥
	iv := []byte("0000000000000000") // sm4算法的初始向量

	err := sm4.SetIV(iv) // 设置sm4算法实现的初始向量, 不设置则使用默认值
	if err != nil {
		log.Fatalf("设置初始向量错误: %v", err)
	}

	encrypt, err := sm4.Sm4Ecb(key, []byte(data), true) // 使用sm4Ecb模式, pksc7填充加密, mode为true代表加密
	if err != nil {
		log.Fatalf("加密错误: %v", err)
	}
	decrypt, err := sm4.Sm4Ecb(key, encrypt, false) // 使用相同的方式进行解密, mode为false代表解密
	if err != nil {
		log.Fatalf("解密错误: %v", err)
	}
	log.Printf("解密后的数据为: %v", string(decrypt))
	log.Printf("解密结果: %v", bytes.Equal(decrypt, []byte(data)))
}

4. 总结

加密算法是保障信息安全的核心技术,尤其是最关键的银行业核心领域,它们长期以来都是沿用3DES、SHA-1、RSA、AES等国际通用的加密算法体系,而国密算法的推出,正式让我国摆脱了对国外技术和产品的过度依赖,建设行业网络安全环境,增强了我国行业信息系统的安全可控能力!

0%