PyCrypto AES无法加密含č/š/ž等特殊字符的字符串问题求助
Hey there! I’ve run into this exact issue before—special characters like č, š, ž break AES encryption in PyCrypto because of how string encoding works under the hood. Let’s break down the problem and fix it step by step.
The Root Cause
AES encryption (and most crypto libraries) operates on byte data, not raw strings. When you try to encrypt a string with non-ASCII characters using the default string-to-bytes conversion (like ASCII), those special characters can’t be represented in ASCII, leading to encoding errors or corrupted data.
The fix is straightforward: we need to use a Unicode-compatible encoding (like UTF-8) to convert our strings to bytes before encryption, then reverse the process when decrypting.
Step-by-Step Solution
1. Standardize on UTF-8 for String ↔ Byte Conversion
Always encode your name string to UTF-8 bytes before passing it to AES, and decode the decrypted bytes back to a UTF-8 string afterward. This ensures all special characters are preserved correctly across systems.
2. Handle AES Block Size Padding
AES requires input data to be a multiple of its block size (16 bytes for AES-128). We’ll use PKCS7 padding to handle this—PyCrypto provides helper functions for this in Crypto.Util.Padding.
3. Full Working Example
Here’s a complete, tested code snippet that handles special characters without errors:
from Crypto.Cipher import AES from Crypto.Util.Padding import pad, unpad from Crypto.Random import get_random_bytes def encrypt_name(name: str, key: bytes) -> bytes: # Encode the name to UTF-8 bytes first name_bytes = name.encode('utf-8') # Generate a random initialization vector (IV) - required for CBC mode iv = get_random_bytes(AES.block_size) # Initialize AES cipher in CBC mode cipher = AES.new(key, AES.MODE_CBC, iv) # Pad the data to match AES block size, then encrypt encrypted_data = cipher.encrypt(pad(name_bytes, AES.block_size)) # Return IV + encrypted data (we need the IV to decrypt later) return iv + encrypted_data def decrypt_name(encrypted_data: bytes, key: bytes) -> str: # Split the IV from the encrypted content iv = encrypted_data[:AES.block_size] ciphertext = encrypted_data[AES.block_size:] # Initialize the cipher with the same key and IV cipher = AES.new(key, AES.MODE_CBC, iv) # Decrypt and remove padding, then decode back to a string decrypted_bytes = unpad(cipher.decrypt(ciphertext), AES.block_size) return decrypted_bytes.decode('utf-8') # Test with a name containing special characters if __name__ == "__main__": # Generate a secure 256-bit (32-byte) key secret_key = get_random_bytes(32) test_name = "Štrah" encrypted = encrypt_name(test_name, secret_key) decrypted = decrypt_name(encrypted, secret_key) print(f"Original name: {test_name}") print(f"Encrypted data (hex): {encrypted.hex()}") print(f"Decrypted name: {decrypted}") print(f"Names match: {test_name == decrypted}") # Should print True
Key Tips
- Never skip the IV: The initialization vector ensures identical plaintexts encrypt to different ciphertexts. Always generate a new random IV for each encryption, and store it alongside the ciphertext (we prepend it here for convenience).
- Use a secure key: Generate your key with
get_random_bytes()instead of hardcoding a string—this avoids weak keys that are easy to guess. - Stick to UTF-8: It’s the universal standard for Unicode characters, so you won’t run into issues with other special characters down the line.
This should resolve the errors you’re seeing with names containing č, š, ž, or any other Unicode character.
内容的提问来源于stack exchange,提问作者Razmooo

