Para que diferentes plataformas troquem dados cifrados de forma consistente, é preciso alinhar modo de operação, tamanho de chave, vetor de inicialização, esquema de preenchimento e codificação dos bytes. O cenário mais comum é o AES-128 no modo CBC com Zero Padding (ou No Padding) e strings codificadas em UTF-8. A seguir, veja exemplos funcionais em cinco linguagens.
C#
No .NET, a classe RijndaelManaged permite configurar chave, IV, modo e preenchimento. O exemplo abaixo aplica o preenchimento manualmente e converte o resultado para Base64.
using System;
using System.Security.Cryptography;
using System.Text;
public class AesCipher
{
private static readonly int BlockSize = 16;
public static void Main()
{
string text = "Texto de exemplo";
string key = "chave12345678901";
string iv = "vetorinicial1234";
string encoded = Encrypt(text, key, iv);
Console.WriteLine(encoded);
string decoded = Decrypt(encoded, key, iv);
Console.WriteLine(decoded);
}
public static string Encrypt(string plain, string key, string iv)
{
byte[] keyBytes = Encoding.UTF8.GetBytes(key);
byte[] ivBytes = Encoding.UTF8.GetBytes(iv);
byte[] plainBytes = Pad(Encoding.UTF8.GetBytes(plain));
using (RijndaelManaged aes = new RijndaelManaged())
{
aes.Key = keyBytes;
aes.IV = ivBytes;
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.Zeros;
ICryptoTransform transform = aes.CreateEncryptor();
byte[] result = transform.TransformFinalBlock(plainBytes, 0, plainBytes.Length);
return Convert.ToBase64String(result);
}
}
public static string Decrypt(string encoded, string key, string iv)
{
byte[] keyBytes = Encoding.UTF8.GetBytes(key);
byte[] ivBytes = Encoding.UTF8.GetBytes(iv);
byte[] cipherBytes = Convert.FromBase64String(encoded);
using (RijndaelManaged aes = new RijndaelManaged())
{
aes.Key = keyBytes;
aes.IV = ivBytes;
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.Zeros;
ICryptoTransform transform = aes.CreateDecryptor();
byte[] result = transform.TransformFinalBlock(cipherBytes, 0, cipherBytes.Length);
return Encoding.UTF8.GetString(result).TrimEnd('\0');
}
}
private static byte[] Pad(byte[] data)
{
int remainder = data.Length % BlockSize;
if (remainder == 0) return data;
int newSize = data.Length + (BlockSize - remainder);
Array.Resize(ref data, newSize);
return data;
}
}
Java
Em Java, a transformação AES/CBC/NoPadding exige que o bloco já tenha tamanho múltiplo de 16 bytes. A decoddificação Base64 pode usar java.util.Base64.
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
public class AesExample {
public static void main(String[] args) throws Exception {
String text = "Texto de exemplo";
String key = "chave12345678901";
String iv = "vetorinicial1234";
String encrypted = encrypt(text, key, iv);
System.out.println(encrypted);
String decrypted = decrypt(encrypted, key, iv);
System.out.println(decrypted);
}
public static String encrypt(String plain, String key, String iv) throws Exception {
byte[] input = fillBlock(plain.getBytes("UTF-8"));
SecretKeySpec keySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
IvParameterSpec ivSpec = new IvParameterSpec(iv.getBytes("UTF-8"));
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
byte[] output = cipher.doFinal(input);
return Base64.getEncoder().encodeToString(output);
}
public static String decrypt(String encoded, String key, String iv) throws Exception {
byte[] input = Base64.getDecoder().decode(encoded);
SecretKeySpec keySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
IvParameterSpec ivSpec = new IvParameterSpec(iv.getBytes("UTF-8"));
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
byte[] output = cipher.doFinal(input);
return new String(output, "UTF-8").replaceAll("\\x00+$", "");
}
private static byte[] fillBlock(byte[] data) {
int block = 16;
int rest = data.length % block;
if (rest == 0) return data;
byte[] padded = new byte[data.length + (block - rest)];
System.arraycopy(data, 0, padded, 0, data.length);
return padded;
}
}
PHP
Como o OpenSSL do PHP não oferece preenchimento com zeros diretamente, o texto precisa ser completado antes da cifragem e ajustado após a decifragem.
<?php
function zeroPad($data) {
$block = 16;
$len = strlen($data);
$rest = $len % $block;
if ($rest === 0) return $data;
return $data . str_repeat("\0", $block - $rest);
}
$key = "chave12345678901";
$iv = "vetorinicial1234";
$text = "Texto de exemplo";
$encrypted = openssl_encrypt(
zeroPad($text),
"AES-128-CBC",
$key,
OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING,
$iv
);
$encoded = base64_encode($encrypted);
echo $encoded . PHP_EOL;
$decoded = base64_decode($encoded);
$decrypted = openssl_decrypt(
$decoded,
"AES-128-CBC",
$key,
OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING,
$iv
);
echo rtrim($decrypted, "\0") . PHP_EOL;
?>
JavaScript
A biblioteca crypto-js já inclui o preenchimento ZeroPadding, o que torna a operação mais direta no navegador ou no Node.js.
<script src="aes.js"></script>
<script src="pad-zeropadding.js"></script>
<script>
var text = "Texto de exemplo";
var key = CryptoJS.enc.Utf8.parse("chave12345678901");
var iv = CryptoJS.enc.Utf8.parse("vetorinicial1234");
var encrypted = CryptoJS.AES.encrypt(text, key, {
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.ZeroPadding
});
console.log(encrypted.toString());
var decrypted = CryptoJS.AES.decrypt(encrypted, key, {
iv: iv,
padding: CryptoJS.pad.ZeroPadding
});
console.log(decrypted.toString(CryptoJS.enc.Utf8));
</script>
Python
O exemplo usa a biblioteca pycryptodome. O objeto de cifrgaem deve ser recriado para a decifragem, e o preenchimento deve ser removido com rstrip.
from Crypto.Cipher import AES
import base64
BLOCK = 16
FILL = b'\0'
def pad(s):
return s + (BLOCK - len(s) % BLOCK) * FILL
def unpad(s):
return s.rstrip(FILL)
key = b'chave12345678901'
iv = b'vetorinicial1234'
source = b'Texto de exemplo'
engine = AES.new(key, AES.MODE_CBC, iv)
cipher = engine.encrypt(pad(source))
encoded = base64.b64encode(cipher)
print(encoded.decode('utf-8'))
engine = AES.new(key, AES.MODE_CBC, iv)
plain = unpad(engine.decrypt(cipher))
print(plain.decode('utf-8'))
Compatibilidade entre as linguagens
Para que todos os exemplos produzam o mesmo resultado, mantenha os seguintes parâmetros iguais em cada lado: algoritmo AES, modo CBC, chave e IV de 16 bytes, preenchimento com \0, codificação UTF-8 e representação Base64 para trensporte. Quando a API não suporta ZeroPadding explicitamente, use NoPadding e preencha o bloco manualmente.