为了账号安全,请及时绑定邮箱和手机立即绑定

将 PHP AES 加密移植到 Golang

将 PHP AES 加密移植到 Golang

Go
BIG阳 2022-07-04 16:46:46
我的电子商务提供商在 PHP、Java、JavaScript、C# 和 Python 中有这个库来加密我的请求,因为我的 API 是用 Go 制作的,我自然想,为什么不用 Go 做呢?哦,男孩……我不知道我在做什么。这是原始的PHP代码:class AesCrypto {    /**    * Encrypt string with a given key    * @param strToEncrypt    * @param key    * @return String encrypted string    */    public static function encrypt($plaintext, $key128) {        $iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length('aes-128-cbc'));        $cipherText = openssl_encrypt($plaintext, 'AES-128-CBC', hex2bin($key128), 1, $iv);        return base64_encode($iv.$cipherText);    }}我用 Go 尝试了几种略有不同的方法,我想最低限度是这样的:func encrypt(text string, key string) string {    data := []byte(text)    block, _ := aes.NewCipher([]byte(key))    gcm, err := cipher.NewGCM(block)    if err != nil {        panic(err.Error())    }    nonce := make([]byte, gcm.NonceSize())    if _, err = io.ReadFull(rand.Reader, nonce); err != nil {        panic(err.Error())    }    ciphertext := gcm.Seal(nonce, nonce, data, nil)    encoded := base64.StdEncoding.EncodeToString([]byte(ciphertext))    return encoded}我创建了这个函数来加密和解密,它们工作正常,但是当我将它发送给我的提供商时它不起作用。由key电子商务提供商分配,长度为 32 个字节,我知道长度“告诉”newCipher选择 AES-256,对吗?那么它永远不会对应于 AES-128,如 PHP func 中所示。除了检查我的电子商务提供商的服务或尝试使用 PHP 代码解密之外,我该如何移植此 PHP 代码?这是另一个尝试(来自 Go 加密文档):func encrypt4(text string, keyString string) string {    key, _ := hex.DecodeString(keyString)    plaintext := []byte(text)    if len(plaintext)%aes.BlockSize != 0 {        panic("plaintext is not a multiple of the block size")    }    block, err := aes.NewCipher(key)    if err != nil {        panic(err)    }    ciphertext := make([]byte, aes.BlockSize+len(plaintext))    iv := ciphertext[:aes.BlockSize]    if _, err := io.ReadFull(rand.Reader, iv); err != nil {        panic(err)    }    mode := cipher.NewCBCEncrypter(block, iv)    mode.CryptBlocks(ciphertext[aes.BlockSize:], plaintext)    final := base64.StdEncoding.EncodeToString(ciphertext)    return final}
查看完整描述

1 回答

?
白板的微信

TA贡献1883条经验 获得超3个赞

GCM 与 CBC 模式不同。密钥是十六进制编码的,所以一个 32 字节的字符串代表一个 16 字节(或 128 位)的密钥。

在 CBC 模式下,明文必须被填充,以便它是块大小的倍数。PHP 的 openssl_encrypt 自动执行此操作(使用PKCS#5/7),但在 Go 中必须明确完成。

综上所述,我们最终得到了文档中 CBC 加密示例的轻微变化:

package main


import (

    "bytes"

    "crypto/aes"

    "crypto/cipher"

    "crypto/rand"

    "encoding/base64"

    "encoding/hex"

    "io"

)


func encrypt(plaintext, key16 string) string {

    padded := pkcs7pad([]byte(plaintext), aes.BlockSize)


    key, err := hex.DecodeString(key16)

    if err != nil {

        panic(err)

    }


    block, err := aes.NewCipher(key)

    if err != nil {

        panic(err)

    }


    buffer := make([]byte, aes.BlockSize+len(padded)) // IV followed by ciphertext

    iv, ciphertext := buffer[:aes.BlockSize], buffer[aes.BlockSize:]


    if _, err := io.ReadFull(rand.Reader, iv); err != nil {

        panic(err)

    }


    mode := cipher.NewCBCEncrypter(block, iv)

    mode.CryptBlocks(ciphertext, padded)


    return base64.StdEncoding.EncodeToString(buffer)

}


func pkcs7pad(plaintext []byte, blockSize int) []byte {

    padding := blockSize - len(plaintext)%blockSize


    return append(plaintext, bytes.Repeat([]byte{byte(padding)}, padding)...)

}


查看完整回答
反对 回复 2022-07-04
  • 1 回答
  • 0 关注
  • 229 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信