首页 > 其他分享 >Go使用crypto实现AES和RSA加密处理

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

时间:2024-08-25 12:53:02浏览次数:14  
标签:AES return err nil RSA crypto 加密 ciphertext

前言

在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

相关文章

  • BaseCTF2024-week1-Crypto部分题目wp
    先放一下官方的wp(我这里只放我出的题):Docs(feishu.cn)babypackfromCrypto.Util.numberimport*importrandomflag=b'BaseCTF{}'m=bytes_to_long(flag)bin_m=bin(m)[2:]length=len(bin_m)a=[1]sum=1foriinrange(length-1):temp=random.randint(2*sum+1,4*su......
  • golang RSA 解密前端jsencrypt发送的数据时异常 crypto/rsa: decryption error 解决方
    golang中RSA解密前端(jsencrypt)发来的密文后出现 "crypto/rsa:decryptionerror" ,这个问题首先需要确认你的私匙和公匙是否匹配,如果匹配那检查入参数据类型,前端发送来的rsa加密后的数据一般都是经过base64编码后的,在后端进行RSA解码时需要对前端发送的数据进行base64......
  • 全新Versal HBM 系列自适应 SoC:XCVH1542-1MSEVSVA3697、XCVH1542-2MLELSVA4737、XCVH1
    系列概述VersalHBM系列具有快速内存、安全连接和自适应计算的异构集成,可消除内存绑定的计算密集型工作负载(如机器学习、数据库加速、下一代防火墙和高级网络测试仪)的处理和内存瓶颈。它从底层开始构建,以适应不断发展的算法、协议和数据速率。与VersalPremium系列*相比,通过集......
  • 【JS逆向】探索文章链接地址AES加密后如何再次替换变形
    一、加密后的数据变形二、加密函数的定义defencrypt_message(message):cipher=AES.new(key,AES.MODE_ECB)padded_message=pad(message.encode(),AES.block_size,style='pkcs7')cliphertext=cipher.encrypt(padded_message)returncliphert......
  • The 3rd Universal Cup. Stage 1: St. Petersburg Finalized Standings
    C.CherryPicking这道题用了一个类似ODT的题思路。首先我们可以想到是,如果删除某些选手,只有可能会导致区间的合并,不会导致区间的分裂。所以我们可以枚举一下$x$的值,然后找到需要删除的点。用set​维护相同且相邻区间,找到删除点所在的区间后,给区间长度减一。如果区间长度为空......
  • 密码学之RSA算法
    文章目录1.RSA算法介绍1.2算法历史与发展1.3算法应用场景2.RSA密钥生成2.1选择素数2.2计算公钥和私钥2.3密钥长度与安全性3算法原理3.1加密原理3.2加密方法3.3加密示例3.4代码实现4.总结1.RSA算法介绍1.2算法历史与发展RSA算法由RonRivest、Adi......
  • NSSCTF [SWPUCTF 2021 新生赛]crypto8
    开启环境,什么东西?只有一个文件???那就先下载下来看看73E-30U1&>V-H965S95]I<U]P;W=E<GT`这样一串字符,没有网页,只有文件,那肯定是用某种加密方式加密之后的结果。但是这个形式的加密是真没见过,才疏学浅,只能先上网取经了。看了这篇大佬的文章大佬说是UUencode编码,先去找个解码器试......
  • delphi加密C#解密(AES-256)
    因为公司内部业务需要,用delphi加密的内容(流和字符串)要用C#解密,因为不懂delphi,我这里只是问同事要了代码,贴上delphi加密:共两个文件(AES.pas和ElAES.pas)AES.pas:(**************************************************************)(*......
  • AES常用的代码示例
    AESAES是对称加密。对称加密是指加密和解密使用相同的密钥的加密算法。非对称加密是指加密和解密使用不同的密钥的加密算法。AES加密解密加密模式,有ECB模式和CBC模式等等,ECB不需要iv偏移量,而CBC需要。密钥,可以自定义。填充方式,有PKCS5、PKCS7、NoPadding。......
  • [GUET-CTF2019]虚假的压缩包1附送RSA解密&CRC破解png宽高&异或python脚本
    解压得到下图两个zip文件,虚假的压缩包、真实的压缩包使用010editor打开,发现是伪加密,把09改为00即可打开打开以后,发现是是一个rsa解密题上python脚本importgmpy2deffind_pq(n):forpinrange(2,int(n**0.5)+1):ifn%p==0:q=......