You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Golang实现AES256-CBC加密后,OpenSSL解密报块长度错误求助

AES256-CBC加密后OpenSSL解密报「最终块长度错误」问题解决

问题描述

我用Golang编写了一个函数,将载荷加密为AES256-CBC字符串后写入磁盘。编译后的程序功能正常,但使用OpenSSL命令解密时出现「最终块长度错误」。

密钥生成命令

export AES_KEY=$(python -c 'import uuid;print(uuid.uuid4().hex)')
export AES_IV=$(python -c 'import uuid;print(uuid.uuid4().hex[:16])')

Golang加密解密代码

import (
    "crypto/aes"
    "crypto/cipher"
    "encoding/base64"
    "errors"
    "bytes"
    "os"
)

func GetEnv(key, fallback string) string {
    if value, ok := os.LookupEnv(key); ok {
        return value
    }
    return fallback
}

func AesEncrypt(plaintext string) (string, error) {
    key := GetEnv("AES_KEY", "")
    iv := GetEnv("AES_IV", "")
    if key == "" || iv == "" {
        return "", errors.New("AES_KEY and AES_IV must be set")
    }
    bKey := []byte(key)
    bIV := []byte(iv)
    bPlaintext := PKCS5Padding([]byte(plaintext), aes.BlockSize)
    block, err := aes.NewCipher(bKey)
    if err != nil {
        return "", err
    }
    ciphertext := make([]byte, len(bPlaintext))
    mode := cipher.NewCBCEncrypter(block, bIV)
    mode.CryptBlocks(ciphertext, bPlaintext)
    return base64.StdEncoding.EncodeToString(ciphertext), nil
    // return hex.EncodeToString(ciphertext), nil
}

func PKCS5Padding(ciphertext []byte, blockSize int) []byte {
    padding := (blockSize - len(ciphertext)%blockSize)
    padtext := bytes.Repeat([]byte{byte(padding)}, padding)
    return append(ciphertext, padtext...)
}

func AesDecrypt(ciphertext string) (string, error) {
    key := GetEnv("AES_KEY", "")
    iv := GetEnv("AES_IV", "")
    if key == "" || iv == "" {
        return "", errors.New("AES_KEY and AES_IV must be set")
    }
    bKey := []byte(key)
    bIV := []byte(iv)
    // base64 decode
    cipherTextDecoded, err := base64.StdEncoding.DecodeString(ciphertext)
    if err != nil {
        return "", err
    }
    // cipherTextDecoded, err := hex.DecodeString(cipherText)

    block, err := aes.NewCipher(bKey)
    if err != nil {
        return "", err
    }

    mode := cipher.NewCBCDecrypter(block, bIV)
    mode.CryptBlocks([]byte(cipherTextDecoded), []byte(cipherTextDecoded))
    return string(cipherTextDecoded), nil
}

OpenSSL解密命令

openssl aes-256-cbc -d -a -v -nosalt \
    -K $AES_KEY \
    -iv $AES_IV \
    -in variables.enc -out variables.dec

报错信息

bad decrypt
8503165248:error:06FFF06D:digital envelope routines:CRYPTO_internal:wrong final block length:/AppleInternal/Library/BuildRoots/9e200cfa-7d96-11ed-886f-a23c4f261b56/Library/Caches/com.apple.xbs/Sources/libressl/libressl-3.3/crypto/evp/evp_enc.c:540:

问题根源与解决方案

1. 密钥/IV长度与解析不匹配

  • 问题:AES256需要32字节密钥,CBC模式需要16字节IV。当前生成的AES_KEY是32位十六进制字符(对应16字节),AES_IV是16位十六进制字符(对应8字节),长度不符合要求。同时Golang直接将十六进制字符串转为字节数组(每个字符占1字节),而OpenSSL的-K/-iv参数是将十六进制字符串解码为字节(每两个字符对应1字节),导致两边密钥/IV完全不一致。
  • 解决:
    • 修正密钥/IV生成命令,生成符合长度要求的十六进制字符串:
      # 生成32字节密钥(对应64位十六进制字符)
      export AES_KEY=$(python -c 'import os;print(os.urandom(32).hex())')
      # 生成16字节IV(对应32位十六进制字符)
      export AES_IV=$(python -c 'import os;print(os.urandom(16).hex())')
      
    • 修正Golang代码中密钥/IV的解析逻辑,使用hex.DecodeString将十六进制字符串解码为字节数组:
      import "encoding/hex"
      
      func AesEncrypt(plaintext string) (string, error) {
          key := GetEnv("AES_KEY", "")
          iv := GetEnv("AES_IV", "")
          if key == "" || iv == "" {
              return "", errors.New("AES_KEY and AES_IV must be set")
          }
          // 解析十六进制密钥为字节数组
          bKey, err := hex.DecodeString(key)
          if err != nil {
              return "", err
          }
          // 解析十六进制IV为字节数组
          bIV, err := hex.DecodeString(iv)
          if err != nil {
              return "", err
          }
          bPlaintext := PKCS5Padding([]byte(plaintext), aes.BlockSize)
          block, err := aes.NewCipher(bKey)
          if err != nil {
              return "", err
          }
          ciphertext := make([]byte, len(bPlaintext))
          mode := cipher.NewCBCEncrypter(block, bIV)
          mode.CryptBlocks(ciphertext, bPlaintext)
          // 将base64分割为每64字符一行,匹配OpenSSL默认格式
          encoded := base64.StdEncoding.EncodeToString(ciphertext)
          wrapped := ""
          for i := 0; i < len(encoded); i += 64 {
              end := i + 64
              if end > len(encoded) {
                  end = len(encoded)
              }
              wrapped += encoded[i:end] + "\n"
          }
          return wrapped, nil
      }
      

2. 解密函数未去除PKCS5填充

  • 问题:Golang解密函数未去除加密时添加的PKCS5填充,导致解密结果末尾存在多余字节,也可能引发OpenSSL的块长度错误。
  • 解决:添加PKCS5去填充函数,并在解密后调用:
    func PKCS5Unpadding(plaintext []byte) []byte {
        length := len(plaintext)
        if length == 0 {
            return plaintext
        }
        padding := int(plaintext[length-1])
        return plaintext[:length-padding]
    }
    
    func AesDecrypt(ciphertext string) (string, error) {
        key := GetEnv("AES_KEY", "")
        iv := GetEnv("AES_IV", "")
        if key == "" || iv == "" {
            return "", errors.New("AES_KEY and AES_IV must be set")
        }
        bKey, err := hex.DecodeString(key)
        if err != nil {
            return "", err
        }
        bIV, err := hex.DecodeString(iv)
        if err != nil {
            return "", err
        }
        cipherTextDecoded, err := base64.StdEncoding.DecodeString(ciphertext)
        if err != nil {
            return "", err
        }
    
        block, err := aes.NewCipher(bKey)
        if err != nil {
            return "", err
        }
    
        mode := cipher.NewCBCDecrypter(block, bIV)
        mode.CryptBlocks(cipherTextDecoded, cipherTextDecoded)
        // 去除PKCS5填充
        plaintext := PKCS5Unpadding(cipherTextDecoded)
        return string(plaintext), nil
    }
    

3. 验证修正后流程

  1. 重新生成密钥和IV
  2. 用修正后的Golang代码加密载荷并写入文件
  3. 使用原OpenSSL命令解密,即可正常得到明文

内容的提问来源于stack exchange,提问作者user1314147

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 07:35:20