Encriptación AES en Java y descifrado en C #

Hola, he cifrado cadena hexadecimal y clave que se ha cifrado utilizando el algoritmo AES estándar. Código:

final String key = "=abcd!#Axd*G!pxP"; final javax.crypto.spec.SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), "AES"); final javax.crypto.Cipher cipher = Cipher.getInstance("AES"); cipher.init(Cipher.ENCRYPT_MODE, keySpec); byte [] encryptedValue = cipher.doFinal(input.getBytes()); return new String(org.apache.commons.codec.binary.Hex.encodeHex(encryptedValue)); 

Ahora bash descifrarlo usando C # Code:

  RijndaelManaged rijndaelCipher = new RijndaelManaged(); // Assumed Mode and padding values. rijndaelCipher.Mode = CipherMode.ECB; rijndaelCipher.Padding = PaddingMode.None; // AssumedKeySize and BlockSize values. rijndaelCipher.KeySize = 0x80; rijndaelCipher.BlockSize = 0x80; // Convert Hex keys to byte Array. byte[] encryptedData = hexStringToByteArray(textToDecrypt); byte[] pwdBytes = Encoding.Unicode.GetBytes(key); byte[] keyBytes = new byte[0x10]; int len = pwdBytes.Length; if (len > keyBytes.Length) { len = keyBytes.Length; } Array.Copy(pwdBytes, keyBytes, len); rijndaelCipher.Key = keyBytes; rijndaelCipher.IV = keyBytes; // Decrypt data byte[] plainText = rijndaelCipher.CreateDecryptor().TransformFinalBlock(encryptedData, 0, encryptedData.Length); str = Encoding.UTF8.GetString(plainText); 

y

  static private byte[] HexToBytes(string str) { if (str.Length == 0 || str.Length % 2 != 0) return new byte[0]; byte[] buffer = new byte[str.Length / 2]; char c; for (int bx = 0, sx = 0; bx  '9' ? (c > 'Z' ? (c - 'a' + 10) : (c - 'A' + 10)) : (c - '0')) < '9' ? (c > 'Z' ? (c - 'a' + 10) : (c - 'A' + 10)) : (c - '0')); } return buffer; } 

pero el resultado no es el esperado. Por favor, señala dónde me estoy equivocando?

Tu código tiene un gran problema: ¡está mezclando las codificaciones de los personajes!

En Java está llamando a key.getBytes() , sin argumentos. Este método devuelve los datos codificados UTF-8 o CP1252 / ISO 8859-1 dependiendo de su sistema operativo y el juego de caracteres predeterminado en Java.

En el lado C # está utilizando Encoding.Unicode.GetBytes(key) – “Unicode” en .Net es un sinónimo de caracteres de doble byte alias UTF-16 (Little-Endian) . Por lo tanto, está usando una clave diferente en C #.

Debería poder ver la diferencia comparando el número de bytes en Java y C #:

Java: "=abcd!#Axd*G!pxP".getBytes().length = 16

C #: Encoding.Unicode.GetBytes("=abcd!#Axd*G!pxP").Length = 32

Recomiendo encarecidamente que use matrices de bytes en lugar de cadenas para definir una clave criptográfica.

Actualización: Otra diferencia es que está configurando un vector de inicialización (IV) en C # que no hace en Java. Como está utilizando ECB, la IV no debe usarse, pero si cambia a CBC, por ejemplo, esto hace una gran diferencia.

Necesitaba algo no solo para C #, sino también para Silverlight y Windows Phone 7. Y definitivamente estoy harto de la falta de ejemplos completos de algo aceptable tanto en Java como en C # (y basado en Base64).

El código no es nada sofisticado, pero funciona. Por favor, siéntete libre de mejorarlo, ya que marqué esto como wiki de la comunidad, pero asegúrate de probarlo antes de enviar cualquier cambio.

Aquí está el código C #:

 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Security.Cryptography; using System.IO; //Author: Doguhan Uluca //Website: www.deceivingarts.com namespace DeceivingArts.Common { public class Encryptor { private string _seed = ""; public Encryptor(string seed) { _seed = seed; } public string Encrypt(string input) where TSymmetricAlgorithm : SymmetricAlgorithm, new() { var pwdBytes = Encoding.UTF8.GetBytes(_seed); using(TSymmetricAlgorithm sa = new TSymmetricAlgorithm()) { ICryptoTransform saEnc = sa.CreateEncryptor(pwdBytes, pwdBytes); var encBytes = Encoding.UTF8.GetBytes(input); var resultBytes = saEnc.TransformFinalBlock(encBytes, 0, encBytes.Length); return Convert.ToBase64String(resultBytes); } } public string Decrypt(string input) where TSymmetricAlgorithm : SymmetricAlgorithm, new() { var pwdBytes = Encoding.UTF8.GetBytes(_seed); using(TSymmetricAlgorithm sa = new TSymmetricAlgorithm()) { ICryptoTransform saDec = sa.CreateDecryptor(pwdBytes, pwdBytes); var encBytes = Convert.FromBase64String(input); var resultBytes = saDec.TransformFinalBlock(encBytes, 0, encBytes.Length); return Encoding.UTF8.GetString(resultBytes); } } } } 

Aquí está el código de Java compatible con Android:

 import javax.crypto.Cipher; import javax.crypto.SecretKey; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; /** * Usage: * 
 * String crypto = SimpleCrypto.encrypt(masterpassword, cleartext) * ... * String cleartext = SimpleCrypto.decrypt(masterpassword, crypto) * 

* @author ferenc.hechler * @author Doguhan Uluca */ public class Encryptor { public static String encrypt(String seed, String cleartext) throws Exception { byte[] rawKey = getRawKey(seed.getBytes()); byte[] result = encrypt(rawKey, cleartext.getBytes()); return toBase64(result); } public static String decrypt(String seed, String encrypted) throws Exception { byte[] rawKey = getRawKey(seed.getBytes()); byte[] enc = fromBase64(encrypted); byte[] result = decrypt(rawKey, enc); return new String(result); } private static byte[] getRawKey(byte[] seed) throws Exception { SecretKey skey = new SecretKeySpec(seed, "AES"); byte[] raw = skey.getEncoded(); return raw; } private static byte[] encrypt(byte[] raw, byte[] clear) throws Exception { SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES"); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); IvParameterSpec ivParameterSpec = new IvParameterSpec(raw); cipher.init(Cipher.ENCRYPT_MODE, skeySpec, ivParameterSpec); byte[] encrypted = cipher.doFinal(clear); return encrypted; } private static byte[] decrypt(byte[] raw, byte[] encrypted) throws Exception { SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES"); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); IvParameterSpec ivParameterSpec = new IvParameterSpec(raw); cipher.init(Cipher.DECRYPT_MODE, skeySpec, ivParameterSpec); byte[] decrypted = cipher.doFinal(encrypted); return decrypted; } public static String toBase64(byte[] buf) { return Base64.encodeBytes(buf); } public static byte[] fromBase64(String str) throws Exception { return Base64.decode(str); } }

Para la conversión de Base64, consulte la excelente implementación en http://iharder.net/base64 .

Espero que esto ahorre horas a la gente.

Prueba esta combinación:

 aesAlg.Mode = CipherMode.ECB; aesAlg.Padding = PaddingMode.PKCS7; //aesAlg.IV; - use default (not assign) 

Sospecho que el error es que no está especificando un relleno o modo de operación en el lado de Java de esta ecuación. No estoy seguro de cuál es la implementación AES de Java de forma predeterminada, pero comenzaría especificando ambos cuando obtenga el cifrado. Por ejemplo:

 Cipher.getInstance("//"); 

Necesitará buscar los esquemas de amortiguación admitidos y los modos de operación para AES en Java y luego asegurarse de configurar su código C # para usar exactamente la misma configuración.