Backend Development
Python Tutorial
PHP implements AES-ECB file decryption: Python to PHP porting guide
PHP implements AES-ECB file decryption: Python to PHP porting guide

This article provides detailed guidance on how to transplant the AES-ECB file decryption logic based on the PyCryptodome library in Python to the PHP environment. It focuses on analyzing key derivation, chunked reading and writing, and deeply explores the most common pitfall in cross-language implementation-the difference in encryption padding modes. By comparing Python's `unpad` mechanism with the `OPENSSL_ZERO_PADDING` flag behavior of PHP `openssl_decrypt` function, an accurate PHP decryption implementation solution is provided to ensure that files can be decrypted correctly and avoid errors such as "wrong final block length".
In software development, it is a common requirement to implement the same encryption and decryption logic across languages. This article will take a specific case as an example to discuss how to accurately transplant the AES-ECB file decryption function implemented in Python using the PyCryptodome library to the PHP environment, and focus on solving the padding compatibility issues that may be encountered during the transplantation process.
1. Understand Python decryption logic
First, let's analyze the core decryption logic in the original Python code. The Python code snippet demonstrates two key parts: key derivation and file decryption process.
1.1 Key derivation (getv4key)
Python's getv4key function derives an AES key from a string deckey using the MD5 hash algorithm.
import hashlib
def getv4key(version, model, region):
# ... (Omit the logic of obtaining deckey)
deckey = "AU77D7K3SAU/D3UU" # Example value return hashlib.md5(deckey.encode()).digest()
Here decorkey.encode() converts the string into a bytes string and then hashlib.md5().digest() calculates its MD5 hash and returns the digest in raw bytes form.
1.2 File decryption (decrypt_progress)
The decrypt_progress function is responsible for reading the encrypted file in chunks, decrypting it using AES-ECB mode, and writing the decrypted data to the output file.
from Cryptodome.Cipher import AES
# from Cryptodome.Util.Padding import unpad # This unpad is actually used
def decrypt_progress(inf, outf, key, length):
cipher = AES.new(key, AES.MODE_ECB)
if length % 16 != 0:
raise Exception("invalid input block size")
chunks = length // 4096 1
for i in range(chunks):
block = inf.read(4096)
if not block:
break
decblock = cipher.decrypt(block)
# Note the padding here: only the last block is unpadd
if i == chunks - 1:
outf.write(unpad(decblock)) # Use PKCS#7 or other standard padding methods else:
outf.write(decblock)
The key point of this code is the processing of filling:
- It uses AES.MODE_ECB mode, which does not use initialization vectors (IVs).
- Data is read and decrypted in chunks of 4096 bytes.
- The unpad(decblock) function is only called when processing the last data block. This means that the intermediate data blocks are written directly after decryption without de-filling. This implies that the intermediate blocks may not have been padded during encryption, or if padding was done, it was not removed after decryption. The last block may contain padding bytes and needs to be depadded after decryption. Typically, the unpad function removes PKCS#7 or other standard padding.
2. Key derivation in PHP
Porting Python's MD5 key derivation logic to PHP is very straightforward.
Python: hashlib.md5(deckey.encode()).digest() PHP: hash('md5', $deckey, true)
The third parameter true in the hash('md5', $deckey, true) function means returning the hash value in the original binary format, which is consistent with the behavior of Python's digest() method.
<?php $deckey = "AU77D7K3SAU/D3UU"; // Example value, which should actually be obtained through other logic $key = hash('md5', $deckey, true); // $key is now a 16-byte binary string that can be used directly as an AES key?>
3. PHP file decryption implementation and filling mode challenges
In PHP, we usually use openssl_decrypt function for AES decryption. However, differences in handling of padding patterns with Python's PyCryptodome library are the most common pitfalls during porting.
3.1 Initial PHP decryption attempt and problems encountered
According to Python’s block decryption logic, a preliminary PHP implementation may be as follows:
<?php // ...key derivation...
$sourceFile = 'file.zip.enc4';
$destFile = 'file.zip';
$chunkSize = 4096;
$sourceHandle = fopen($sourceFile, 'rb');
$destHandle = fopen($destFile, 'wb');
while (!feof($sourceHandle)) {
$chunk = fread($sourceHandle, $chunkSize); // Note that chunkSize is read here
if (empty($chunk)) {
break;
}
// Try to decrypt, you may encounter problems $decryptedChunk = openssl_decrypt($chunk, 'aes-128-ecb', $key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING);
// var_dump(openssl_error_string()); // Used to debug errors fwrite($destHandle, $decryptedChunk);
}
fclose($sourceHandle);
fclose($destHandle);
?>
When decrypting using OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, PHP may report an error similar to error:1C80006B:Provider routines::wrong final block length. This error usually means that openssl_decrypt, while trying to pad, found that the length or content of the data block did not conform to the expected pad format.
3.2 Core Issue: Differences in Filling Modes
Python's PyCryptodome library does not automatically fill and defill each block by default in AES.MODE_ECB mode. Its unpad function is called explicitly and only takes effect on the last block. This means:
- When encrypting, only the last block may be padded (e.g. PKCS#7).
- When decrypting, the middle blocks do not need to be depadded, while the last block needs to be depadded according to how it was padded.
PHP's openssl_decrypt function behaves differently:
- By default, openssl_decrypt will attempt to de-pad the data with PKCS#7 padding.
- The OPENSSL_ZERO_PADDING flag disables the default PKCS#7 padding instead of enabling zero padding. When this flag is set, openssl_decrypt assumes that the input data has no padding, or that padding has already been handled by the caller. If the data block length is not an integral multiple of the AES block size (16 bytes), or data with padding is passed in when padding is disabled, a "wrong final block length" error will result.
Therefore, in order to simulate Python's behavior, we need to implement conditional filling processing in PHP:
- For intermediate data blocks (those not at the end of the file), we know that they are not unpadded in Python, so PHP should also disable padding (i.e. use OPENSSL_ZERO_PADDING).
- For the last chunk of data, the Python code calls unpad, which means it expects a chunk with standard padding (like PKCS#7). Therefore, PHP should allow openssl_decrypt to perform the default PKCS#7 depadding operation (i.e. not set OPENSSL_ZERO_PADDING) when decrypting the last block.
3.3 Revised PHP decryption implementation
In order to handle padding correctly, we need to keep track of the number of bytes that have been read and compare it to the total file size to tell if the currently processed block is the last one.
<?php $sourceFile = 'file.zip.enc4';
$destFile = 'file.zip';
// Key derivation $deckey = "AU77D7K3SAU/D3UU"; // Replace with actual deckey
$key = hash('md5', $deckey, true);
$chunkSize = 4096; //The chunk size of each read $fileSize = filesize($sourceFile); //Get the total size of encrypted files $sourceHandle = fopen($sourceFile, 'rb');
$destHandle = fopen($destFile, 'wb');
$totalBytesRead = 0; // Record the total number of bytes read while (!feof($sourceHandle)) {
$chunk = fread($sourceHandle, $chunkSize);
if (empty($chunk)) {
break;
}
$currentChunkSize = strlen($chunk);
$totalBytesRead = $currentChunkSize;
// Determine whether it is the last block // If the total number of bytes read is less than the total size of the file, it means that the current block is not the last block // At this time, padding should be disabled for processing (OPENSSL_ZERO_PADDING)
// Otherwise, the last block, allowing openssl_decrypt to use default PKCS#7 to pad $paddingFlag = ($totalBytesRead < $fileSize) ? OPENSSL_ZERO_PADDING : 0;
$decryptedChunk = openssl_decrypt($chunk, 'aes-128-ecb', $key, OPENSSL_RAW_DATA | $paddingFlag);
// Check whether decryption is successful, if it fails openssl_decrypt returns false
if ($decryptedChunk === false) {
// You can add error handling or logging as needed error_log("Decryption failed: " . openssl_error_string());
break;
}
fwrite($destHandle, $decryptedChunk);
}
fclose($sourceHandle);
fclose($destHandle);
echo "File decryption completed: " . $destFile . "\n";
?>
Code explanation:
- $fileSize = filesize($sourceFile);: Get the total number of bytes of the encrypted file, used to determine whether the current block is the last one.
- $totalBytesRead: Accumulates the actual number of bytes of the block read each time.
- $paddingFlag = ($totalBytesRead
- When $totalBytesRead
- When $totalBytesRead >= $fileSize, it means that the currently read block is the last block of the file (or includes the end of the file). At this point, the Python code calls unpad, so PHP should allow openssl_decrypt to perform its default PKCS#7 depadding operation. 0 means not to set any additional padding flags, i.e. use openssl_decrypt's default padding behavior.
4. Summary and precautions
With the above revised PHP code, we can successfully port Python's AES-ECB file decryption logic to PHP. This case highlights the importance of in-depth understanding of how each language library handles encryption padding patterns when performing cross-language encryption and decryption migrations.
Key notes:
- Key derivation consistency: Ensures that the MD5 hash result is in the original binary form.
- Chunked processing: Keep the same chunked reading and decryption logic as the original Python code.
- Fill mode: This is where things go wrong the most.
- Python PyCryptodome's unpad is called explicitly and only affects the last block.
- OPENSSL_ZERO_PADDING of PHP openssl_decrypt is to disable the default PKCS#7 padding.
- Be sure to conditionally enable or disable PHP's padding processing based on the total file size and the number of bytes read.
Correctly understanding and handling encryption padding patterns is the cornerstone of ensuring cross-language encryption and decryption interoperability. In actual projects, sufficient testing must be performed to verify the integrity and correctness of the decryption results.
The above is the detailed content of PHP implements AES-ECB file decryption: Python to PHP porting guide. For more information, please follow other related articles on the PHP Chinese website!
Hot AI Tools
Undress AI Tool
Undress images for free
AI Clothes Remover
Online AI tool for removing clothes from photos.
Undresser.AI Undress
AI-powered app for creating realistic nude photos
ArtGPT
AI image generator for creative art from text prompts.
Stock Market GPT
AI powered investment research for smarter decisions
Hot Article
Popular tool
Notepad++7.3.1
Easy-to-use and free code editor
SublimeText3 Chinese version
Chinese version, very easy to use
Zend Studio 13.0.1
Powerful PHP integrated development environment
Dreamweaver CS6
Visual web development tools
SublimeText3 Mac version
God-level code editing software (SublimeText3)
Hot Topics
20522
7
13634
4
Solve the error of multidict build failure when installing Python package
Mar 08, 2026 am 02:51 AM
When installing libraries that depend on multidict in Python, such as aiohttp or discord.py, users may encounter the error "ERROR: Could not build wheels for multidict". This is usually due to the lack of the necessary C/C compiler or build tools, preventing pip from successfully compiling multidict's C extension from source. This article will provide a series of solutions, including installing system build tools, managing Python versions, and using virtual environments, to help developers effectively solve this problem.
How to use the Python zip function_Parallel traversal of multiple sequences and dictionary construction
Mar 13, 2026 am 11:54 AM
The essence of zip is zipper pairing, which packs multiple iterable objects into tuples by position and does not automatically unpack the dictionary. When passing in a dictionary, its keys are traversed by default. You need to explicitly use the keys()/values()/items() view to correctly participate in parallel traversal.
How to draw a histogram in Python_Multi-dimensional classification data comparison and stacked histogram color mapping implementation
Mar 13, 2026 pm 12:18 PM
Multi-dimensional classification histograms need to manually calculate the x position and call plt.bar hierarchically; when stacking, bottom must be used to accumulate height, and xticks and ylim must be explicitly set (bottom=0); avoid mixing stacked=True and seaborn, and colors should be dynamically generated and strictly match the layer sequence.
How to find the sum of 5 numbers using Python's for loop
Mar 10, 2026 pm 12:48 PM
This article explains in detail how to use a for loop to read 5 integers from user input and add them up, provide a concise and readable standard writing method, and compare efficient alternatives to built-in functions.
How Python manages dependencies_Comparison between pip and poetry
Mar 12, 2026 pm 04:21 PM
pip is suitable for simple projects, which only install packages and do not isolate the environment; poetry is a modern tool that automatically manages dependencies, virtual environments and version locking. Use pip requirements.txt for small projects, and poetry is recommended for medium and large projects. The two cannot be mixed in the same project.
Python set intersection optimization_large data volume set operation skills
Mar 13, 2026 pm 12:36 PM
The key to optimizing Python set intersection performance is to use the minimum set as the left operand, avoid implicit conversion, block processing and cache incremental updates. Priority should be given to using min(...,key=len) to select the smallest set, disabling multi-parameter intersection(), using frozenset or bloom filters to reduce memory, and using lru_cache to cache results in high-frequency scenarios.
How to store sparse matrices in Python_Dictionary coordinate storage and use of scipy.sparse
Mar 12, 2026 pm 05:48 PM
Use scipy.sparse.coo_matrix instead of a dictionary because the bottom layer uses row/col/data three-array to efficiently support operations; the structure needs to be deduplicated, converted to csr/csc and then calculated; save_npz is preferred for saving; operations such as slicing must use csr/csc format.
How to run a Python script_Detailed explanation of various ways to run a Python script and command line operations
Apr 03, 2026 pm 01:51 PM
To run a Python script, make sure that Python is installed, the PATH configuration is correct, and the script has no syntax errors; confirm the interpreter path and version through which/where and --version; shebang only takes effect on Linux/macOS and requires chmod x; when reporting module errors, you need to check the working directory, sys.path, piplist, and running mode.





