【问题标题】:Porting PHP AES encryption to Golang将 PHP AES 加密移植到 Golang
【发布时间】:2021-01-15 15:34:13
【问题描述】:

我的电子商务提供商在 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
}

【问题讨论】:

  • 密钥是十六进制的,所以32个字符就是16个字节,也就是aes-128。在您的第一个示例中,您使用的是 GCM,这永远行不通。第二个示例看起来更好,但如果没有完整示例,则很难判断textkeyString 和生成与预期的ivciphertext

标签: php go encryption aes


【解决方案1】:

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

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

综上所述,我们最终得到了CBC encryption example in the docs 的细微变化:

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)...)
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-03-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多