跳到主要内容

3 篇博文 含有标签「加密」

查看所有标签

go 语言加密

· 阅读需 9 分钟
ahKevinXy
作者

Go 语言 实现 AES、RSA、国密算法(SM2/SM4) 的完整可运行代码,包含加密、解密、签名、验签核心功能,注释详细,新手也能直接复制使用。


一、前置说明

  1. 依赖:Go 标准库已内置 AES/RSA,国密算法需引入第三方成熟库(推荐 github.com/tjfoc/gmsm,国内主流);
  2. 安全规范:密钥/私钥需妥善保管,传输时用安全通道,示例中为简化用固定密钥,实际需动态生成/读取;
  3. 编码:加密后的数据通常用 base64 编码,方便传输/存储。

安装国密依赖

go get github.com/tjfoc/gmsm

二、AES 加密实现(ECB/CBC 模式)

AES 是对称加密(加密解密用同一密钥),推荐用 CBC 模式(比 ECB 更安全,需初始化向量 IV)。

完整代码

package main

import (
"bytes"
"crypto/aes"
"crypto/cipher"
"encoding/base64"
"fmt"
)

// PKCS7 补码(AES 要求明文长度为 16 字节倍数)
func pkcs7Padding(data []byte, blockSize int) []byte {
padding := blockSize - len(data)%blockSize
padText := bytes.Repeat([]byte{byte(padding)}, padding)
return append(data, padText...)
}

// PKCS7 解补码
func pkcs7UnPadding(data []byte) ([]byte, error) {
length := len(data)
if length == 0 {
return nil, fmt.Errorf("加密数据为空")
}
// 取出最后一个字节的值,即为补码长度
padding := int(data[length-1])
return data[:length-padding], nil
}

// AESCBCEncrypt AES-CBC 加密
// key: 密钥(16/24/32 字节,对应 AES-128/AES-192/AES-256)
// iv: 初始化向量(必须 16 字节)
func AESCBCEncrypt(plainText, key, iv []byte) (string, error) {
// 1. 创建 AES 密码块
block, err := aes.NewCipher(key)
if err != nil {
return "", err
}
// 2. 补码
plainText = pkcs7Padding(plainText, block.BlockSize())
// 3. 创建 CBC 模式
mode := cipher.NewCBCEncrypter(block, iv)
// 4. 加密
cipherText := make([]byte, len(plainText))
mode.CryptBlocks(cipherText, plainText)
// 5. base64 编码返回
return base64.StdEncoding.EncodeToString(cipherText), nil
}

// AESCBCDecrypt AES-CBC 解密
func AESCBCDecrypt(cipherTextBase64 string, key, iv []byte) (string, error) {
// 1. base64 解码
cipherText, err := base64.StdEncoding.DecodeString(cipherTextBase64)
if err != nil {
return "", err
}
// 2. 创建 AES 密码块
block, err := aes.NewCipher(key)
if err != nil {
return "", err
}
// 3. 创建 CBC 解密模式
mode := cipher.NewCBCDecrypter(block, iv)
// 4. 解密
plainText := make([]byte, len(cipherText))
mode.CryptBlocks(plainText, cipherText)
// 5. 解补码
plainText, err = pkcs7UnPadding(plainText)
if err != nil {
return "", err
}
return string(plainText), nil
}

func main() {
// 测试 AES-CBC
plainText := []byte("Hello, AES Encryption!")
// 密钥(16 字节 = AES-128)
key := []byte("1234567890123456")
// IV(必须 16 字节)
iv := []byte("1234567890123456")

// 加密
cipherText, err := AESCBCEncrypt(plainText, key, iv)
if err != nil {
fmt.Printf("AES 加密失败:%v\n", err)
return
}
fmt.Printf("AES 加密结果:%s\n", cipherText)

// 解密
decryptedText, err := AESCBCDecrypt(cipherText, key, iv)
if err != nil {
fmt.Printf("AES 解密失败:%v\n", err)
return
}
fmt.Printf("AES 解密结果:%s\n", decryptedText)
}

关键说明

  • 密钥长度:AES-128(16字节)、AES-192(24字节)、AES-256(32字节),国内常用 AES-128;
  • IV 要求:CBC 模式必须用 16 字节 IV,且每次加密建议用随机 IV(示例中为固定值,实际需生成随机数);
  • 补码方式:PKCS7 是通用补码方式,Go 标准库无内置,需手动实现。

三、RSA 加密实现(非对称加密)

RSA 是非对称加密(公钥加密、私钥解密;私钥签名、公钥验签),适合小数据加密(如密钥传输)。

完整代码

package main

import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/base64"
"encoding/pem"
"fmt"
"os"
)

// GenerateRSAKey 生成 RSA 密钥对(2048 位)
func GenerateRSAKey() (pubKey []byte, priKey []byte, err error) {
// 1. 生成私钥
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
return nil, nil, err
}
// 2. 编码私钥(PEM 格式)
privateBytes := x509.MarshalPKCS1PrivateKey(privateKey)
privateBlock := &pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: privateBytes,
}
priKey = pem.EncodeToMemory(privateBlock)

// 3. 提取公钥
publicKey := &privateKey.PublicKey
// 4. 编码公钥(PEM 格式)
publicBytes, err := x509.MarshalPKIXPublicKey(publicKey)
if err != nil {
return nil, nil, err
}
publicBlock := &pem.Block{
Type: "RSA PUBLIC KEY",
Bytes: publicBytes,
}
pubKey = pem.EncodeToMemory(publicBlock)

return pubKey, priKey, nil
}

// RSAEncrypt 公钥加密
func RSAEncrypt(plainText []byte, pubKey []byte) (string, error) {
// 1. 解析公钥
block, _ := pem.Decode(pubKey)
if block == nil {
return "", fmt.Errorf("公钥解析失败")
}
pubInterface, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
return "", err
}
publicKey, ok := pubInterface.(*rsa.PublicKey)
if !ok {
return "", fmt.Errorf("公钥类型错误")
}

// 2. 加密(OAEP 填充更安全,PKCS1v15 兼容老系统)
cipherText, err := rsa.EncryptOAEP(
rand.Reader,
publicKey,
nil,
plainText,
nil,
)
if err != nil {
return "", err
}

// 3. base64 编码
return base64.StdEncoding.EncodeToString(cipherText), nil
}

// RSADecrypt 私钥解密
func RSADecrypt(cipherTextBase64 string, priKey []byte) (string, error) {
// 1. base64 解码
cipherText, err := base64.StdEncoding.DecodeString(cipherTextBase64)
if err != nil {
return "", err
}

// 2. 解析私钥
block, _ := pem.Decode(priKey)
if block == nil {
return "", fmt.Errorf("私钥解析失败")
}
privateKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
return "", err
}

// 3. 解密
plainText, err := rsa.DecryptOAEP(
rand.Reader,
privateKey,
nil,
cipherText,
nil,
)
if err != nil {
return "", err
}

return string(plainText), nil
}

func main() {
// 生成 RSA 密钥对
pubKey, priKey, err := GenerateRSAKey()
if err != nil {
fmt.Printf("生成 RSA 密钥失败:%v\n", err)
return
}
fmt.Printf("RSA 公钥:\n%s\n", string(pubKey))
fmt.Printf("RSA 私钥:\n%s\n", string(priKey))

// 测试加密解密
plainText := []byte("Hello, RSA Encryption!")
// 公钥加密
cipherText, err := RSAEncrypt(plainText, pubKey)
if err != nil {
fmt.Printf("RSA 加密失败:%v\n", err)
return
}
fmt.Printf("RSA 加密结果:%s\n", cipherText)

// 私钥解密
decryptedText, err := RSADecrypt(cipherText, priKey)
if err != nil {
fmt.Printf("RSA 解密失败:%v\n", err)
return
}
fmt.Printf("RSA 解密结果:%s\n", decryptedText)
}

关键说明

  • 密钥长度:推荐 2048 位(1024 位已不安全),4096 位性能较差;
  • 填充方式:OAEP 比 PKCS1v15 更安全,优先使用;
  • 适用场景:RSA 加密速度慢,仅用于加密小数据(如 AES 密钥),不适合加密大文本。

四、国密算法实现(SM2/SM4)

国密算法是国家密码局制定的标准,包含:

  • SM2:非对称加密(替代 RSA),用于签名/加密;
  • SM4:对称加密(替代 AES),128 位密钥,分组加密。

完整代码

package main

import (
"encoding/base64"
"fmt"

"github.com/tjfoc/gmsm/sm2"
"github.com/tjfoc/gmsm/sm4"
"github.com/tjfoc/gmsm/x509"
)

// ===================== SM4 对称加密 =====================
// SM4ECBEncrypt SM4-ECB 加密(ECB 模式,无需 IV)
func SM4ECBEncrypt(plainText, key []byte) (string, error) {
// 1. 创建 SM4 密码块
block, err := sm4.NewCipher(key)
if err != nil {
return "", err
}
// 2. 补码(PKCS7,与 AES 通用)
plainText = pkcs7Padding(plainText, block.BlockSize())
// 3. 加密(ECB 模式)
cipherText := make([]byte, len(plainText))
for i := 0; i < len(plainText); i += block.BlockSize() {
block.Encrypt(cipherText[i:i+block.BlockSize()], plainText[i:i+block.BlockSize()])
}
// 4. base64 编码
return base64.StdEncoding.EncodeToString(cipherText), nil
}

// SM4ECBDecrypt SM4-ECB 解密
func SM4ECBDecrypt(cipherTextBase64 string, key []byte) (string, error) {
// 1. base64 解码
cipherText, err := base64.StdEncoding.DecodeString(cipherTextBase64)
if err != nil {
return "", err
}
// 2. 创建 SM4 密码块
block, err := sm4.NewCipher(key)
if err != nil {
return "", err
}
// 3. 解密
plainText := make([]byte, len(cipherText))
for i := 0; i < len(cipherText); i += block.BlockSize() {
block.Decrypt(plainText[i:i+block.BlockSize()], cipherText[i:i+block.BlockSize()])
}
// 4. 解补码
plainText, err = pkcs7UnPadding(plainText)
if err != nil {
return "", err
}
return string(plainText), nil
}

// ===================== SM2 非对称加密 =====================
// GenerateSM2Key 生成 SM2 密钥对
func GenerateSM2Key() (pubKey, priKey []byte, err error) {
// 1. 生成 SM2 私钥
privateKey, err := sm2.GenerateKey(rand.Reader)
if err != nil {
return nil, nil, err
}
// 2. 编码私钥(PEM 格式)
privateBytes, err := x509.WritePrivateKeyToPem(privateKey, nil)
if err != nil {
return nil, nil, err
}
// 3. 编码公钥(PEM 格式)
publicBytes, err := x509.WritePublicKeyToPem(&privateKey.PublicKey)
if err != nil {
return nil, nil, err
}
return publicBytes, privateBytes, nil
}

// SM2Encrypt SM2 公钥加密
func SM2Encrypt(plainText []byte, pubKey []byte) (string, error) {
// 1. 解析公钥
publicKey, err := x509.ReadPublicKeyFromPem(pubKey)
if err != nil {
return "", err
}
// 2. 加密(SM2 推荐 C1C3C2 格式)
cipherText, err := sm2.Encrypt(publicKey, plainText, rand.Reader, sm2.C1C3C2)
if err != nil {
return "", err
}
// 3. base64 编码
return base64.StdEncoding.EncodeToString(cipherText), nil
}

// SM2Decrypt SM2 私钥解密
func SM2Decrypt(cipherTextBase64 string, priKey []byte) (string, error) {
// 1. base64 解码
cipherText, err := base64.StdEncoding.DecodeString(cipherTextBase64)
if err != nil {
return "", err
}
// 2. 解析私钥
privateKey, err := x509.ReadPrivateKeyFromPem(priKey, nil)
if err != nil {
return "", err
}
// 3. 解密
plainText, err := sm2.Decrypt(privateKey, cipherText, sm2.C1C3C2)
if err != nil {
return "", err
}
return string(plainText), nil
}

func main() {
// ===================== 测试 SM4 =====================
sm4Key := []byte("1234567890123456") // SM4 密钥必须 16 字节
sm4PlainText := []byte("Hello, SM4 Encryption!")
// SM4 加密
sm4CipherText, err := SM4ECBEncrypt(sm4PlainText, sm4Key)
if err != nil {
fmt.Printf("SM4 加密失败:%v\n", err)
return
}
fmt.Printf("SM4 加密结果:%s\n", sm4CipherText)
// SM4 解密
sm4DecryptedText, err := SM4ECBDecrypt(sm4CipherText, sm4Key)
if err != nil {
fmt.Printf("SM4 解密失败:%v\n", err)
return
}
fmt.Printf("SM4 解密结果:%s\n", sm4DecryptedText)

// ===================== 测试 SM2 =====================
// 生成 SM2 密钥对
sm2PubKey, sm2PriKey, err := GenerateSM2Key()
if err != nil {
fmt.Printf("生成 SM2 密钥失败:%v\n", err)
return
}
sm2PlainText := []byte("Hello, SM2 Encryption!")
// SM2 加密
sm2CipherText, err := SM2Encrypt(sm2PlainText, sm2PubKey)
if err != nil {
fmt.Printf("SM2 加密失败:%v\n", err)
return
}
fmt.Printf("SM2 加密结果:%s\n", sm2CipherText)
// SM2 解密
sm2DecryptedText, err := SM2Decrypt(sm2CipherText, sm2PriKey)
if err != nil {
fmt.Printf("SM2 解密失败:%v\n", err)
return
}
fmt.Printf("SM2 解密结果:%s\n", sm2DecryptedText)
}

关键说明

  • SM4 密钥:固定 16 字节,模式支持 ECB/CBC/CTR 等,推荐 CBC 模式;
  • SM2 特点:密钥长度 256 位,加密效率高于 RSA,是国内金融/政务系统的首选;
  • 依赖库tjfoc/gmsm 是国内维护最活跃的国密库,支持 SM2/SM3/SM4/SM9。

五、核心注意事项

  1. 密钥安全
    • 对称密钥(AES/SM4):避免硬编码,建议从配置中心/环境变量读取;
    • 私钥(RSA/SM2):加密存储,仅授权服务可访问;
  2. 性能优化
    • 大文本加密:用 AES/SM4(对称加密),RSA/SM2 仅加密对称密钥;
    • 批量加密:复用密码块对象,减少重复创建;
  3. 兼容性
    • 国密算法需对接方也支持,否则优先用 AES/RSA;
    • 编码统一用 base64,避免二进制传输乱码。

总结

  1. AES:对称加密,16/24/32 字节密钥,CBC 模式需 IV,适合大文本加密;
  2. RSA:非对称加密,2048 位密钥,OAEP 填充更安全,适合小数据/密钥传输;
  3. 国密算法:SM4 替代 AES,SM2 替代 RSA,符合国内合规要求,金融/政务场景优先使用;
  4. 所有加密后的数据建议用 base64 编码,补码统一用 PKCS7。