首页 > 编程语言 >Go基于crypto库实现AES封装加密以及协同PHP8 使用openssl AES加密使用

Go基于crypto库实现AES封装加密以及协同PHP8 使用openssl AES加密使用

时间:2024-09-01 12:24:55浏览次数:10  
标签:ciphertext 加密 err AES crypto key IV

前言

要实现Go与PHP 8之间的AES加密协同工作,我们需要确保两端使用相同的加密模式、密钥长度、以及密钥和初始化向量(IV)。下面,我将提供一个详细的教程,说明如何在Go中使用crypto/aescrypto/cipher库来实现AES加密,并在PHP 8中使用OpenSSL来解密这些数据(反之亦然)。
在这里插入图片描述

Go基于基础Crypto库实现AES封装加密

在Go语言中,使用标准库crypto/aescrypto/cipher可以实现AES加密的封装。以下是一个使用AES-CBC模式进行加密和解密的简单示例。AES-CBC(Cipher Block Chaining)模式是一种常见的加密模式,它使用前一个密文块来加密下一个明文块,从而增强了加密的安全性。

首先,你需要安装Go环境。然后,你可以按照以下步骤创建一个AES加密和解密的封装:

  1. 生成AES密钥和IV(初始化向量):AES密钥的长度通常是16(AES-128)、24(AES-192)或32(AES-256)字节。IV也应该是与块大小相同(对于AES,块大小是16字节)。

  2. 加密过程:使用cipher.NewCBCEncrypter创建一个加密器,并使用它来加密数据。

  3. 解密过程:使用cipher.NewCBCDecrypter创建一个解密器,并使用它来解密数据。

以下是完整的示例代码:

package main

import (
	"bytes"
	"crypto/aes"
	"crypto/cipher"
	"crypto/rand"
	"encoding/hex"
	"fmt"
	"io"
)

// Encrypt 使用AES CBC模式加密数据
func Encrypt(plaintext, key []byte) (ciphertext, iv []byte, err error) {
	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, nil, err
	}

	// 生成随机的IV
	iv = make([]byte, aes.BlockSize)
	if _, err := io.ReadFull(rand.Reader, iv); err != nil {
		return nil, nil, err
	}

	// 加密
	ciphertext = make([]byte, aes.BlockSize+len(plaintext))
	mode := cipher.NewCBCEncrypter(block, iv)
	mode.CryptBlocks(ciphertext[aes.BlockSize:], plaintext)

	// 返回IV和密文
	return ciphertext, iv, nil
}

// Decrypt 使用AES CBC模式解密数据
func Decrypt(ciphertext, key, iv []byte) (plaintext []byte, err error) {
	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, err
	}

	if len(ciphertext) < aes.BlockSize {
		return nil, fmt.Errorf("ciphertext too short")
	}
	if len(ciphertext)%aes.BlockSize != 0 {
		return nil, fmt.Errorf("ciphertext is not a multiple of the block size")
	}

	// 解密
	plaintext = make([]byte, len(ciphertext)-aes.BlockSize)
	mode := cipher.NewCBCDecrypter(block, iv)
	mode.CryptBlocks(plaintext, ciphertext[aes.BlockSize:])

	// 去除填充(这里假设使用了PKCS#7填充)
	plaintext = PKCS7Unpad(plaintext)

	return plaintext, nil
}

// PKCS7Unpad removes the PKCS#7 padding from a slice of bytes.
func PKCS7Unpad(data []byte) []byte {
	length := len(data)
	unpadding := int(data[length-1])
	return data[:(length - unpadding)]
}

func main() {
	key := []byte("this is a key123") // 应使用更安全的密钥生成方法
	plaintext := []byte("hello world")

	ciphertext, iv, err := Encrypt(plaintext, key)
	if err != nil {
		panic(err)
	}

	fmt.Printf("IV: %s\nCiphertext: %s\n", hex.EncodeToString(iv), hex.EncodeToString(ciphertext))

	decryptedText, err := Decrypt(ciphertext, key, iv)
	if err != nil {
		panic(err)
	}

	fmt.Println("Decrypted text:", string(decryptedText))
}

注意

  • 密钥key应该是一个安全的随机字节序列,上面的示例中仅用于演示。
  • IV(初始化向量)每次加密时都应该是随机的,以确保加密的安全性。
  • 在实际应用中,你应当确保密文、密钥和IV都安全地存储和传输。
  • 解密函数中的PKCS7Unpad用于去除加密时可能添加的填充,以还原原始数据。这个例子假设使用了PKCS#7填充,这是AES加密中常用的填充方式。

其他事项

当然,我们可以继续讨论关于AES加密在Go语言中的实现,特别是关于如何更健壮地处理错误、如何管理密钥和IV的安全存储,以及如何在网络应用中安全地传输这些数据。

密钥和IV的管理
  • 密钥生成:在实际应用中,你应该使用安全的随机数生成器来生成密钥。Go的crypto/rand包提供了这样的功能。
  • 密钥存储:密钥应该安全地存储在硬件安全模块(HSM)或经过加密的数据库中,而不是硬编码在源代码中。
  • IV的传输:IV不需要保密,但它必须与密文一起安全地传输到解密方,以便解密过程能够正确进行。
传输&模式和性能
  • 加密数据的传输:当加密数据需要在网络上传输时,你应该使用TLS(传输层安全性协议)或类似的协议来保护数据的机密性和完整性。TLS会加密整个传输层的数据包,从而保护你的加密数据不被中间人攻击。

  • 加密模式的选择:除了CBC模式外,AES还支持其他几种模式,如ECB(电子密码本模式)、CFB(密码反馈模式)、OFB(输出反馈模式)和CTR(计数器模式)等。每种模式都有其特定的用途和安全性考虑。CBC模式是最常用的模式之一,因为它结合了加密和完整性校验的功能(尽管不是强校验)。然而,对于某些应用场景,你可能需要选择其他模式。

  • 填充和去填充:在上面的示例中,我们使用了PKCS#7填充来确保数据块的大小符合AES的要求。在解密时,我们需要去除这个填充。但是,如果密文在传输过程中被篡改,去填充过程可能会失败。因此,你应该在解密过程中检查并处理这种潜在的错误。

  • 性能考虑:AES加密是一个计算密集型的操作,特别是在处理大量数据时。因此,在性能敏感的应用中,你可能需要考虑使用硬件加速(如AES-NI指令集)或优化你的加密和解密逻辑。

示例扩展

下面是一个扩展的示例,它包括了更详细的错误处理和日志记录(假设你有一个日志库):

package main

import (
	// ... 导入必要的包
	"log"
)

func main() {
	// ... 生成密钥和IV

	plaintext := []byte("sensitive data")
	ciphertext, iv, err := Encrypt(plaintext, key)
	if err != nil {
		log.Fatalf("Failed to encrypt data: %v", err)
	}

	// ... 将ciphertext和iv安全地存储或传输

	// 假设我们稍后需要解密
	decryptedText, err := Decrypt(ciphertext, key, iv)
	if err != nil {
		log.Fatalf("Failed to decrypt data: %v", err)
	}

	log.Printf("Decrypted text: %s", decryptedText)
}

// ... Encrypt 和 Decrypt 函数的实现(与上面相同)

// LogError 是一个简单的错误日志记录函数(假设)
func LogError(err error) {
	// 使用你的日志库来记录错误
	log.Printf("Error: %v", err)
}

PHP端 OpenSSL 解密

在PHP端,我们使用OpenSSL函数来解密由Go生成的AES加密数据。
在这里插入图片描述

<?php

function decryptAES($ciphertext, $key) {
    // Base64解码
    $ciphertextDec = base64_decode($ciphertext);

    // 解密
    $decrypted = openssl_decrypt($ciphertextDec, 'AES-256-CBC', $key, OPENSSL_RAW_DATA, substr($ciphertextDec, 0, 16));

    // 去除PKCS#7填充
    $length = strlen($decrypted);
    $padding = ord($decrypted[$length - 1]);
    $decrypted = substr($decrypted, 0, -$padding);

    return $decrypted;
}

$key = "your-32-bytes-long-secret-key-here";
$ciphertext = "..."; // 这里是从Go端得到的Base64加密字符串

$decrypted = decryptAES($ciphertext, $key);
echo "Decrypted text: " . $decrypted . PHP_EOL;

注意事项

  1. 密钥和IV:确保Go和PHP端使用的密钥和IV(特别是IV)完全相同。在上面的Go示例中,IV是随机生成的,但在实际应用中,如果需要在两端解密相同的数据,IV必须一致。如果只是想简单地演示加密和解密,可以将IV作为加密函数的一部分返回给PHP端。

  2. 错误处理:在生产环境中,应该增加更多的错误处理逻辑来确保加密和解密操作的健壮性。

  3. 填充:这里使用了PKCS#7填充(Go中的PKCS7Padding和PHP中的手动去除填充)。确保两端使用相同的填充机制。

  4. Base64编码:加密后的数据通常是二进制的,不适合直接作为字符串传输或存储。Go示例中使用了Base64编码,PHP示例中相应地进行了Base64解码。

通过以上步骤,你应该能够在确保Go和PHP 8之间的AES加密协同工作无缝进行。我们将重点关注如何在需要时传递IV(初始化向量),因为IV在加密和解密过程中必须是相同的。

Go端:修改以返回IV

在Go的加密函数中,我们可以修改它以返回IV,这样PHP端就可以使用这个IV来解密数据。

package main

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

// ... (PKCS7Padding 和 PKCS7Unpadding 函数保持不变)

// EncryptAESWithIV 加密数据并返回IV
func EncryptAESWithIV(plaintext, key []byte) (string, []byte, error) {
    block, err := aes.NewCipher(key)
    if err != nil {
        return "", nil, err
    }

    // 生成IV
    iv := make([]byte, aes.BlockSize)
    if _, err := io.ReadFull(rand.Reader, iv); err != nil {
        return "", nil, err
    }

    // 加密
    mode := cipher.NewCBCEncrypter(block, iv)
    ciphertext := make([]byte, aes.BlockSize+len(plaintext))
    mode.CryptBlocks(ciphertext[aes.BlockSize:], PKCS7Padding(plaintext, aes.BlockSize))
    copy(ciphertext[:aes.BlockSize], iv)

    // 转换为Base64
    ciphertextBase64 := base64.StdEncoding.EncodeToString(ciphertext)

    return ciphertextBase64, iv, nil
}

func main() {
    key := []byte("your-32-bytes-long-secret-key-here")
    plaintext := []byte("Hello, world!")

    encrypted, iv, err := EncryptAESWithIV(plaintext, key)
    if err != nil {
        panic(err)
    }

    fmt.Println("Encrypted (Base64):", encrypted)
    fmt.Println("IV (Base64):", base64.StdEncoding.EncodeToString(iv)) // 可选:将IV也转换为Base64方便查看或传输

    // 在实际应用中,你需要将encrypted和iv(可能是Base64编码的)发送给PHP端
}

PHP端:使用提供的IV进行解密

在PHP端,你现在将接收到加密后的数据和IV(可能是Base64编码的),你需要先解码IV,然后使用它来解密数据。

<?php

function decryptAESWithIV($ciphertext, $key, $ivBase64) {
    // Base64解码IV
    $iv = base64_decode($ivBase64);

    // Base64解码加密数据
    $ciphertextDec = base64_decode($ciphertext);

    // 解密
    $decrypted = openssl_decrypt($ciphertextDec, 'AES-256-CBC', $key, OPENSSL_RAW_DATA, $iv);

    // 去除PKCS#7填充
    $length = strlen($decrypted);
    $padding = ord($decrypted[$length - 1]);
    $decrypted = substr($decrypted, 0, -$padding);

    return $decrypted;
}

$key = "your-32-bytes-long-secret-key-here";
$ciphertext = "..."; // 从Go端接收的Base64加密字符串
$ivBase64 = "...";   // 从Go端接收的Base64编码的IV

$decrypted = decryptAESWithIV($ciphertext, $key, $ivBase64);
echo "Decrypted text: " . $decrypted . PHP_EOL;

注意事项

  • IV的传输:IV不需要保密,但它必须与加密时使用的IV完全相同。因此,你可以安全地将IV与加密数据一起发送给解密方。
  • 安全性:确保你的密钥(key)是安全的,并且只在你信任的系统之间共享。
  • 错误处理:在PHP端,你可能还想添加一些错误处理逻辑来检查openssl_decrypt是否成功执行。
  • 测试:在生产环境中部署之前,请确保在多种情况下测试你的加密和解密逻辑,以确保其可靠性和安全性。

总结

我们在日常开发中,会经常遇到和其他平台数据做对接,接口与接口对接过程中,除了必要签名授权外,我们还需要对敏感数据进行加密,使用的比较多就是AES对称加密,但是每个系统之间的开发语言并非都是一致的,不同的业务系统使用的编程语言都是各异,所以在加解密的过程中,我们要协调双方的加密方式,密钥和IV以及填充方式等,以上就用Go和PHP做对比,使用AES CBC模式进行加密处理,确保双方的系统交互没有信息泄露的问题,当然Java、Python、C等其他语言,都是可以,这里就不一一举例了(重点我对这些语言也不是很熟,哈哈),但是在实际应用中,你可能还需要考虑其他安全因素,如使用HTTPS来保护数据传输过程中的安全等。好了,本篇内容到此结束。

标签:ciphertext,加密,err,AES,crypto,key,IV
From: https://blog.csdn.net/qq_35807303/article/details/141597923

相关文章

  • 学习爬虫day26-kanzhun请求加密与逆向解密
    找到入口之后,在主js文件中,模拟入口函数,生成需要的参数:functiontest(n){letM=window.loader(12210)//console.log(M._A())lets=M.A()//letn='{"cityCode":"","salaryId":"403","experienceId":"","pageNum......
  • python实现椭圆曲线加密算法(ECC)
    目录椭圆曲线加密算法(ECC)简介ECC的数学基础椭圆曲线的定义ECC的基本操作ECC加密和解密流程Python面向对象实现ECC加密和解密代码解释场景应用:安全通信总结椭圆曲线加密算法(ECC)简介椭圆曲线加密算法(EllipticCurveCryptography,ECC)是一种基于椭圆曲线数学结构的......
  • 终于有人说清了加密货币撸空投这些事
    相信很多对Web3感兴趣的朋友可能都被一些“行业内幕词汇(黑话)”难住了,别担心!今天我就来用简单的话解释一下Web3里关于‘空投’的专业术语,帮助大家更好地理解。如果你感兴趣,就继续往下看吧!什么是女巫其实就是一种叫做‘女巫攻击’的行为。这就好比一个人假装成很多不同......
  • [luoguP4051/JSOI2007] 字符加密
    题意给定字符串\(s\),输出将\(s\)的所有循环同构的字符串排序后,每个字符串的末尾的字符。sol因为要对循环同构的字符串排序,因此我们可以将\(s\)复制一遍,拼在后面,计算\(sa\),满足\(sa_i\len\)的所有元素的相对位置即为排序后字符串的相对位置,输出即可\(sa\)的计算详见......
  • U盘怎么加密保护?U盘加密软件哪个好?
    在工作和生活中,我们经常需要使用U盘来存储数据。而为了避免U盘数据泄露,我们需要加密保护U盘。那么,U盘加密软件哪个好呢?下面我们就一起来了解一下。BitLocker加密BitLocker是Windows系统提供的磁盘加密功能,可以加密保护U盘。但需要注意的是,该功能无法在家庭版系统中使用。......
  • 数据安全指南:电脑重要文件如何加密?
    在工作中,电脑是重要的办公工具,可以处理和保存大量数据。为了避免重要文件泄露,我们需要加密保护电脑重要文件。下面我们就来了解一下电脑重要文件的加密方法。EFS加密EFS是Windows系统提供的数据加密功能,基于公钥加密策略,采用透明加密方法。在加密文件时,不需要设置密码,通过......
  • RSA加密
    题目来源[BUUCTF]REVERSE——rsa打开文件夹有两个文件打开pub.key文件复制到解密网站对应RSA密钥指数E=65537,这一串模数可以转化为十进制后可以分离出p、qp=285960468890451637935629440372639283459,q=304008741604601924494328155975272418463所以已知E、p、q......
  • openGauss-透明数据加密
    openGauss-透明数据加密可获得性本特性自openGauss2.1.0版本开始引入。特性简介透明数据加密(TransparentDataEncryption),是数据库在将数据写入存储介质时对数据进行加密,从存储介质中读取数据时自动解密,防止攻击者绕过数据库认证机制直接读取数据文件中的数据,以解决静态数据......
  • nginx扩展之支持多个ssl加密虚拟主机
    nginx支持一台服务器唯一IP:PORT,根据server_name创建区分多个经过ssl加密的https虚拟主机,apache不支持 生成www.magedu.net域名证书:[[email protected]]#cd/etc/pki/tls/certs/[[email protected]]#vimMakefile%.key:umask77;\#/usr/bin/ope......
  • 两个月Crypto从入门到进阶专题第1天
    绪论:今天主要讲RSA的原理以及python的实现,RSA的历史这些就不讲了,RSA的历史你自己去搜视频看才有趣,三个大佬创造的RSA。1.RSA加密过程1.1选择p,q两个质数(为什么选质数,后面就知道了,这里说一下学习方法:有一些步骤不知道为什么的,先看下去,可能后面会给你解答,不要死板,后面闲聊就......