前言
在Go语言中,实现数据加密可以通过使用标准库中的crypto
包以及encoding/base64
等包来完成。这里,我们将重点阐述如何在Go语言中使用这些库来实现对称加密和非对称加密的详细逻辑。
一、对称加密
对称加密是指加密和解密使用同一密钥的加密方式。常用的对称加密算法有AES、DES等。这里以AES为例,说明其实现逻辑。
1. AES加密实现
AES加密过程中,需要选择一个密钥长度(AES-128、AES-192或AES-256),并使用这个密钥来创建密码块(cipher.Block)。加密过程可能还需要对明文进行填充,以确保其长度是块大小的整数倍。
package main
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"encoding/base64"
"fmt"
"io"
)
// 加密函数
func AesEncrypt(plaintext, key []byte) (string, error) {
block, err := aes.NewCipher(key)
if err != nil {
return "", err
}
// PKCS7Padding填充
plaintext = PKCS7Padding(plaintext, block.BlockSize())
// 初始化向量(IV)
ciphertext := make([]byte, aes.BlockSize+len(plaintext))
iv := ciphertext[:aes.BlockSize]
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
return "", err
}
// 加密模式(这里使用CBC模式)
mode := cipher.NewCBCEncrypter(block, iv)
mode.CryptBlocks(ciphertext[aes.BlockSize:], plaintext)
// Base64编码
return base64.StdEncoding.EncodeToString(ciphertext), nil
}
// PKCS7Padding填充
func PKCS7Padding(ciphertext []byte, blockSize int) []byte {
padding := blockSize - len(ciphertext)%blockSize
padtext := bytes.Repeat([]byte{byte(padding)}, padding)
return append(ciphertext, padtext...)
}
// 主函数
func main() {
key := []byte("1234567890123456") // AES-128
plaintext := []byte("hello world")
encrypted, err := AesEncrypt(plaintext, key)
if err != nil {
fmt.Println("Encrypt failed:", err)
return
}
fmt.Println("Encrypted:", encrypted)
}
2. AES解密实现
解密过程与加密相反,需要使用相同的密钥和初始化向量(IV),并通过解密模式来恢复明文。
// 解密函数
func AesDecrypt(ciphertext string, key []byte) ([]byte, error) {
data, err := base64.StdEncoding.DecodeString(ciphertext)
if err != nil {
return nil, err
}
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
if len(data) < aes.BlockSize {
return nil, fmt.Errorf("ciphertext too short")
}
iv := data[:aes.BlockSize]
data = data[aes.BlockSize:]
mode := cipher.NewCBCDecrypter(block, iv)
mode.CryptBlocks(data, data)
// 去除PKCS7Padding
data = PKCS7UnPadding(data, block.BlockSize())
return data, nil
}
// PKCS7UnPadding去除填充
func PKCS7UnPadding(data []byte, blockSize int) []byte {
length := len(data)
unpadding := int(data[length-1])
return data[:(length - unpadding)]
}
二、非对称加密
非对称加密使用一对密钥,一个公钥用于加密,一个私钥用于解密。常用的非对称加密算法有RSA、ECDSA等。这里以RSA为例,说明其实现逻辑。
1. RSA加密实现
RSA加密首先需要生成RSA密钥对,然后使用公钥对明文进行加密。以下是一个完整的示例,包括生成RSA密钥对、使用公钥加密数据和使用私钥解密数据的步骤。
package main
import (
"crypto/rand"
"crypto/rsa"
"crypto/rsa/pkcs1v15"
"crypto/x509"
"encoding/pem"
"fmt"
"os"
)
// 生成RSA密钥对
func GenerateRSAKeyPair(bits int) (*rsa.PrivateKey, error) {
privateKey, err := rsa.GenerateKey(rand.Reader, bits)
if err != nil {
return nil, err
}
return privateKey, nil
}
// 保存RSA私钥到文件
func SavePrivateKey(filename string, privateKey *rsa.PrivateKey) error {
outFile, err := os.Create(filename)
if err != nil {
return err
}
defer outFile.Close()
var privateKeyBytes = x509.MarshalPKCS1PrivateKey(privateKey)
pem.Encode(outFile, &pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: privateKeyBytes,
})
return nil
}
// 从文件加载RSA私钥
func LoadPrivateKey(filename string) (*rsa.PrivateKey, error) {
inFile, err := os.Open(filename)
if err != nil {
return nil, err
}
defer inFile.Close()
pemData, err := ioutil.ReadAll(inFile)
if err != nil {
return nil, err
}
block, _ := pem.Decode(pemData)
if block == nil {
return nil, fmt.Errorf("failed to parse PEM block containing the key")
}
priv, err := x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
return nil, err
}
return priv, nil
}
// RSA公钥加密
func RSAEncrypt(publicKey *rsa.PublicKey, plaintext []byte) ([]byte, error) {
ciphertext, err := rsa.EncryptPKCS1v15(rand.Reader, publicKey, plaintext)
if err != nil {
return nil, err
}
return ciphertext, nil
}
// RSA私钥解密
func RSADecrypt(privateKey *rsa.PrivateKey, ciphertext []byte) ([]byte, error) {
plaintext, err := rsa.DecryptPKCS1v15(rand.Reader, privateKey, ciphertext)
if err != nil {
return nil, err
}
return plaintext, nil
}
func main() {
// 生成RSA密钥对
privateKey, err := GenerateRSAKeyPair(2048)
if err != nil {
fmt.Println("Error generating key:", err)
return
}
// 保存私钥到文件
err = SavePrivateKey("private.pem", privateKey)
if err != nil {
fmt.Println("Error saving private key:", err)
return
}
// 提取公钥
publicKey := &privateKey.PublicKey
// 加密
plaintext := []byte("hello world")
ciphertext, err := RSAEncrypt(publicKey, plaintext)
if err != nil {
fmt.Println("Error encrypting:", err)
return
}
fmt.Printf("Encrypted: %x\n", ciphertext)
// 加载私钥进行解密
loadedPrivateKey, err := LoadPrivateKey("private.pem")
if err != nil {
fmt.Println("Error loading private key:", err)
return
}
decrypted, err := RSADecrypt(loadedPrivateKey, ciphertext)
if err != nil {
fmt.Println("Error decrypting:", err)
return
}
fmt.Printf("Decrypted: %s\n", decrypted)
}
// 注意:由于示例中未包含ioutil包,你需要使用io/ioutil或改用其他方式读取文件
// 例如,使用os.ReadFile代替ioutil.ReadAll
注意:
- 示例中的
ioutil.ReadAll
在Go 1.16及以后的版本中已被弃用,你可以使用os.ReadFile
来替代。 - RSA加密通常用于加密较小的数据块(如密钥、哈希值等),因为RSA加密的计算成本较高。对于大量数据的加密,通常使用
三、结合RSA和AES加密完整示例
- 业务逻辑: 对于大量数据的加密,RSA由于其计算成本较高,并不适合直接用来加密整个数据集。相反,RSA通常用于加密一个对称密钥(如AES密钥),然后使用这个对称密钥来加密实际的数据。这样做可以显著提高加密和解密的速度,因为对称加密(如AES)比非对称加密(如RSA)要快得多。
- 示例:下面是一个修改后的示例,展示了如何使用RSA加密一个AES密钥,然后使用AES密钥来加密实际的数据。
package main
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"crypto/rsa"
"crypto/rsa/pkcs1v15"
"crypto/x509"
"encoding/hex"
"encoding/pem"
"fmt"
"io/ioutil"
"os"
)
func main() {
// 生成RSA密钥对
privateKey, err := GenerateRSAKeyPair(2048)
if err != nil {
fmt.Println("Error generating key:", err)
return
}
// 保存私钥到文件
err = SavePrivateKey("private.pem", privateKey)
if err != nil {
fmt.Println("Error saving private key:", err)
return
}
// 提取公钥
publicKey := &privateKey.PublicKey
// 加密
plaintext := []byte("hello world")
ciphertext, err := RSAEncrypt(publicKey, plaintext)
if err != nil {
fmt.Println("Error encrypting:", err)
return
}
fmt.Printf("Encrypted: %x\n", ciphertext)
// 加载私钥进行解密
loadedPrivateKey, err := LoadPrivateKey("private.pem")
if err != nil {
fmt.Println("Error loading private key:", err)
return
}
decrypted, err := RSADecrypt(loadedPrivateKey, ciphertext)
if err != nil {
fmt.Println("Error decrypting:", err)
return
}
fmt.Printf("Decrypted: %s\n", decrypted)
}
// 生成RSA密钥对
func GenerateRSAKeyPair(bits int) (*rsa.PrivateKey, error) {
privateKey, err := rsa.GenerateKey(rand.Reader, bits)
if err != nil {
return nil, err
}
return privateKey, nil
}
// 保存RSA私钥到文件
func SavePrivateKey(filename string, privateKey *rsa.PrivateKey) error {
outFile, err := os.Create(filename)
if err != nil {
return err
}
defer outFile.Close()
var privateKeyBytes = x509.MarshalPKCS1PrivateKey(privateKey)
pem.Encode(outFile, &pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: privateKeyBytes,
})
return nil
}
// 从文件加载RSA私钥
func LoadPrivateKey(filename string) (*rsa.PrivateKey, error) {
inFile, err := os.Open(filename)
if err != nil {
return nil, err
}
defer inFile.Close()
pemData, err := ioutil.ReadAll(inFile)
if err != nil {
return nil, err
}
block, _ := pem.Decode(pemData)
if block == nil {
return nil, fmt.Errorf("failed to parse PEM block containing the key")
}
priv, err := x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
return nil, err
}
return priv, nil
}
// RSA公钥加密
func RSAEncrypt(publicKey *rsa.PublicKey, plaintext []byte) ([]byte, error) {
ciphertext, err := rsa.EncryptPKCS1v15(rand.Reader, publicKey, plaintext)
if err != nil {
return nil, err
}
return ciphertext, nil
}
// RSA私钥解密
func RSADecrypt(privateKey *rsa.PrivateKey, ciphertext []byte) ([]byte, error) {
plaintext, err := rsa.DecryptPKCS1v15(rand.Reader, privateKey, ciphertext)
if err != nil {
return nil, err
}
return plaintext, nil
}
// AES加密
func AESEncrypt(key []byte, plaintext []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
// 填充使明文长度为块大小的整数倍
plaintext = PKCS7Padding(plaintext, block.BlockSize())
// 初始化向量IV(对于CBC模式)
ciphertext := make([]byte, aes.BlockSize+len(plaintext))
iv := ciphertext[:aes.BlockSize]
if _, err := rand.Read(iv); err != nil {
return nil, err
}
mode := cipher.NewCBCEncrypter(block, iv)
mode.CryptBlocks(ciphertext[aes.BlockSize:], plaintext)
return ciphertext, nil
}
// AES解密
func AESDecrypt(key []byte, ciphertext []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
if len(ciphertext) < aes.BlockSize {
return nil, fmt.Errorf("ciphertext too short")
}
iv := ciphertext[:aes.BlockSize]
ciphertext = ciphertext[aes.BlockSize:]
mode := cipher.NewCBCDecrypter(block, iv)
mode.CryptBlocks(ciphertext, ciphertext)
// 去除填充
return PKCS7Unpadding(ciphertext), nil
}
// PKCS7填充
func PKCS7Padding(ciphertext []byte, blockSize int) []byte {
padding := blockSize - len(ciphertext)%blockSize
padtext := bytes.Repeat([]byte{byte(padding)}, padding)
return append(ciphertext, padtext...)
}
// PKCS7去填充
func PKCS7Unpadding(origData []byte) []byte {
length := len(origData)
unpadding := int(origData[length-1])
return origData[:(length - unpadding)]
}
func main() {
// ... (省略RSA密钥生成和保存的步骤)
// 假设我们已经有了一个RSA公钥和私钥
// publicKey 和 privateKey 应该是从某处加载的
// 生成一个AES密钥
aesKey := make([]byte, 16) // AES-128位密钥
if _, err := rand.Read(aesKey); err != nil {
fmt.Println("Error generating AES key:", err)
return
}
// 使用RSA公钥加密AES密钥
encryptedAESKey, err := RSAEncrypt(publicKey, aesKey)
if err != nil {
fmt.Println("Error encrypting AES key with RSA:", err)
return
}
// 假设我们要加密的数据
plaintext := []byte("This is a secret message!")
// 使用AES密钥加密数据
encryptedData, err := AESEncrypt(aesKey, plaintext)
if err != nil {
fmt.Println("Error encrypting data with AES:", err)
return
}
// ... (保存encryptedAESKey和encryptedData)
// 解密过程(通常发生在另一台机器上,拥有RSA私钥)
// 加载RSA私钥
loadedPrivateKey, err := LoadPrivateKey("private.pem")
if err != nil {
fmt.Println("Error loading private key:", err)
return
}
// 使用RSA私钥解密AES密钥
decryptedAESKey, err := RSADecrypt(loadedPrivateKey, encryptedAESKey)
if err != nil {
fmt.Println("Error decrypting AES key with RSA:", err)
return
}
// 使用解密后的AES密钥来解密数据
decryptedData, err := AESDecrypt(decryptedAESKey, encryptedData)
if err != nil {
fmt.Println("Error decrypting data with AES:", err)
return
}
fmt.Println("Decrypted Message:", string(decryptedData))
// 在这个示例中,我们首先生成了一个AES密钥,并使用RSA公钥来加密这个AES密钥。
// 然后,我们使用AES密钥来加密一段实际的明文数据。
//
// 在解密过程中,我们首先使用RSA私钥来解密AES密钥,然后再使用解密后的AES密钥来解密之前加密的数据。
//
// 这种方法结合了RSA的安全性(用于密钥交换)和AES的高效性(用于数据加密),是实现大规模数据加密的常用技术。
注意:
- 在实际应用中,你应该将加密的AES密钥(encryptedAESKey)和加密的数据(encryptedData)安全地存储或传输。
- 对于PKCS7Padding和PKCS7Unpadding函数,我提供了一个简单的实现,但在生产环境中,你可能需要使用更健壮的库函数或实现来处理边缘情况。
- AES的CBC模式需要一个初始化向量(IV),它在加密时随机生成,并在解密时用于恢复原始数据。IV不需要保密,但应与密文一起传输或存储。
- 当你使用RSA进行密钥交换时,通常选择RSA密钥的位数要足够大(如2048位或更长),以确保其安全性。
- 最后,加密和解密操作应始终在安全的上下文中执行,以防止密钥泄露或数据篡改。
总结
通过将RSA用于密钥交换和AES用于数据加密,我们可以构建一个既安全又高效的加密系统。这种混合加密方法结合了RSA的强安全性和AES的高效性,是处理大规模数据加密的常用且有效的策略。在实际应用中,还需要注意密钥管理、加密库的选择、协议安全性以及合规性等方面的挑战。
标签:AES,return,err,nil,RSA,crypto,加密,ciphertext From: https://blog.csdn.net/qq_35807303/article/details/141527963