求助:使用Go语言解密PHP openssl_encrypt加密文件失败
Hey there! Let's break down why your Go decryption is spitting out gibberish even when the keys and IV seem correct—encoding mismatches and subtle crypto parameter differences are the usual suspects here. Let's walk through the most likely issues and fixes step by step:
1. Base64 Encoding Mismatches
PHP often outputs raw binary data from encryption functions (like openssl_public_encrypt or openssl_encrypt) and then Base64-encodes it for safe transmission. If you're not decoding that Base64 string in Go before attempting decryption, you're trying to work with garbled data right out the gate.
- Double-check: Did the PHP code run
base64_encode()on the encrypted AES key, ciphertext, or IV? If yes, usebase64.StdEncoding.DecodeString()(orbase64.URLEncodingif PHP usedbase64url_encode()) in Go to convert those strings back to bytes first.
2. RSA Padding Mismatch
PHP's openssl_public_encrypt defaults to PKCS#1 v1.5 padding. If your Go code is using OAEP padding by accident, decryption will fail silently and produce garbage.
- Fix: Use
rsa.DecryptPKCS1v15()in Go instead ofrsa.DecryptOAEP(). Example snippet for RSA decryption:encryptedAESKeyBytes, err := base64.StdEncoding.DecodeString(encryptedAESKeyStr) if err != nil { // handle decoding error } aesKey, err := rsa.DecryptPKCS1v15(rand.Reader, privateKey, encryptedAESKeyBytes) if err != nil { // handle decryption error (bad key, wrong padding, etc.) }
3. AES Key/IV Length & Format Issues
- AES-256 requires a 32-byte key. After decrypting the AES key with RSA in Go, print its length with
fmt.Println(len(aesKey))—if it's not 32 bytes, your RSA decryption is failing, or the PHP code is generating/processing the key incorrectly (e.g., truncating or padding it). - AES-CBC needs a 16-byte IV. Ensure you're decoding the IV from Base64 if PHP encoded it, and that you're using the exact same IV as the PHP encryption step (don't generate a new IV in Go!).
4. PKCS#7 Padding Handling
PHP's openssl_encrypt automatically adds PKCS#7 padding to the plaintext before encryption. Go's crypto/cipher package doesn't handle unpadding by default, so you need to implement it manually. Add this helper function to your Go code:
func pkcs7Unpad(data []byte) []byte { if len(data) == 0 { return nil } padLen := int(data[len(data)-1]) if padLen > len(data) || padLen == 0 { return nil } return data[:len(data)-padLen] }
Call this function on the decrypted bytes from AES to get the clean plaintext.
5. Text Encoding Differences
If the original plaintext in PHP was encoded in something other than UTF-8 (like ISO-8859-1), converting the decrypted Go bytes directly to a string with string(plaintext) might produce gibberish. Try writing the decrypted bytes to a file first to check if it's actual binary garbage or just an encoding mismatch.
Here's a consolidated example tying all these fixes together:
package main import ( "crypto/aes" "crypto/cipher" "crypto/rand" "crypto/rsa" "crypto/x509" "encoding/base64" "encoding/pem" "fmt" ) func main() { // Load your RSA private key (replace with your actual key) privateKeyPEM := []byte(`-----BEGIN RSA PRIVATE KEY----- YOUR_PRIVATE_KEY_HERE -----END RSA PRIVATE KEY-----`) block, _ := pem.Decode(privateKeyPEM) privateKey, err := x509.ParsePKCS1PrivateKey(block.Bytes) if err != nil { panic(err) } // Data received from PHP (Base64-encoded) encryptedAESKeyBase64 := "ENCRYPTED_AES_KEY_FROM_PHP" encryptedDataBase64 := "ENCRYPTED_DATA_FROM_PHP" ivBase64 := "IV_FROM_PHP" // Decode all Base64 strings to bytes encryptedAESKey, err := base64.StdEncoding.DecodeString(encryptedAESKeyBase64) if err != nil { panic(err) } encryptedData, err := base64.StdEncoding.DecodeString(encryptedDataBase64) if err != nil { panic(err) } iv, err := base64.StdEncoding.DecodeString(ivBase64) if err != nil { panic(err) } // Decrypt AES key with RSA PKCS#1 v1.5 aesKey, err := rsa.DecryptPKCS1v15(rand.Reader, privateKey, encryptedAESKey) if err != nil { panic(err) } fmt.Printf("Decrypted AES key length: %d bytes (should be 32)\n", len(aesKey)) // Decrypt data with AES-256-CBC blockCipher, err := aes.NewCipher(aesKey) if err != nil { panic(err) } if len(iv) != aes.BlockSize { panic(fmt.Sprintf("IV must be %d bytes, got %d", aes.BlockSize, len(iv))) } mode := cipher.NewCBCDecrypter(blockCipher, iv) mode.CryptBlocks(encryptedData, encryptedData) // Remove PKCS#7 padding plaintext := pkcs7Unpad(encryptedData) if plaintext == nil { panic("Failed to unpad plaintext") } fmt.Println("Decrypted plaintext:", string(plaintext)) } func pkcs7Unpad(data []byte) []byte { if len(data) == 0 { return nil } padLen := int(data[len(data)-1]) if padLen > len(data) || padLen == 0 { return nil } return data[:len(data)-padLen] }
内容的提问来源于stack exchange,提问作者l3dx

