Go使用crypto实现AES和RSA加密处理

前言

在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的高效性,是处理大规模数据加密的常用且有效的策略。在实际应用中,还需要注意密钥管理、加密库的选择、协议安全性以及合规性等方面的挑战。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

bobo-rs

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值