国密算法
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
该仓库的官方介绍如下:
由于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等国际通用的加密算法体系,而国密算法的推出,正式让我国摆脱了对国外技术和产品的过度依赖,建设行业网络安全环境,增强了我国行业信息系统的安全可控能力!