2011-03-14 84 views
5

Hola He codificado cadena hexadecimal y clave que se ha cifrado utilizando el algoritmo AES estándar. Código:Encriptación AES en Java y descifrado en C#

 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 trato de descifrarlo usando C# Código:

  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 < buffer.Length; ++bx, ++sx) 
     { 
      // Convert first half of byte 
      c = str[sx]; 
      buffer[bx] = (byte)((c > '9' ? (c > 'Z' ? (c - 'a' + 10) : (c - 'A' + 10)) : (c - '0')) << 4); 
      // Convert second half of byte  
      c = str[++sx]; 
      buffer[bx] |= (byte)(c > '9' ? (c > 'Z' ? (c - 'a' + 10) : (c - 'A' + 10)) : (c - '0')); 
     } 
     return buffer; 
    } 

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

Respuesta

0

Sospecho que el error es que no está especificando un relleno o modo de operación en el lado 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("<algorithm>/<mode of operation>/<padding>"); 

Tendrá que mirar hacia arriba los esquemas de relleno compatibles y los modos de funcionamiento para AES en Java y luego asegúrese de configurar su código C# para utilizar la misma configuración exacta.

+0

He intentado utilizar todas las combinaciones posibles de modo y el relleno. pero no funciona También intenté llamar a Security.getProvider ("SunJCE"), para conocer el modo predeterminado y el relleno, no estoy seguro de en qué grado obtendré esta información relacionada con el modo predeterminado y el relleno utilizados en Java AES. Sin embargo, la lógica de cifrado y descifrado en Java funciona con esta encriptación también. Pero cuando trato de descifrar en C# con el código proporcionado, no puedo descifrar la cadena. – Test

8

Su código tiene un gran problema: ¡está mezclando las codificaciones de caracteres!

En Java está llamando al 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" in .Net is a synonym for double byte characters alias UTF-16 (Little-Endian). Por lo tanto, está usando una clave diferente en C#.

Usted debe ser capaz de ver la diferencia al comparar el número de bytes de Java y C#:

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

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

recomiendo encarecidamente que utilice las 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.

+0

Llegué a saber que la codificación utilizada es: "ISO-8859-1" Así que cambié el valor clave en lógica de descifrado a: byte [] pwdBytes = System.Text.Encoding.GetEncoding ("ISO-8859-1"). GetBytes (clave); pero aún así no funcionó. Obtengo que "Padding no es válido y no puede ser eliminada "excepción. – Test

+0

¿Ha cambiado su código Java a' Cipher cipher = Cipher.getInstance ("AES/ECB/NoPadding"); 'como propuso Chris Thompson? – Robert

+2

ECB solo debe usarse para cifrar claves y otros datos que pueden ser considerado aleatorio, por favor aconseje usar CBC y una IV diferente para cada parte de datos encriptados d con la misma clave. Probablemente sea más útil especificar el relleno PKCS # 5 también; sin relleno significa que la longitud de los datos codificados debe conocerse implícitamente. –

4

Necesitaba algo no solo para C# sino también para Silverlight y Windows Phone 7 compatibles. 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<TSymmetricAlgorithm>(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<TSymmetricAlgorithm>(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 compatibles Android:

import javax.crypto.Cipher; 
import javax.crypto.SecretKey; 
import javax.crypto.spec.IvParameterSpec; 
import javax.crypto.spec.SecretKeySpec; 

/** 
* Usage: 
* <pre> 
* String crypto = SimpleCrypto.encrypt(masterpassword, cleartext) 
* ... 
* String cleartext = SimpleCrypto.decrypt(masterpassword, crypto) 
* </pre> 
* @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 Base64 consulte la excelente aplicación a http://iharder.net/base64.

Espero que esto ahorre horas a las personas.

+0

-1 por ser mala crypto 1) Está usando una terminología extraña, 'seed' para password 2) usa la contraseña como IV. No hagas eso. Necesita una nueva IV aleatoria por encriptación. 3) Utiliza una contraseña directamente como clave, sin KDF apropiado 4) Carece de autenticación => vulnerable a oráculos de relleno – CodesInChaos

+0

@CodesInChaos Gracias por los buenos comentarios. No soy un experto en criptografía Lo invitamos a editar la respuesta y actualizar el código para que se ajuste a los estándares de la buena criptografía. Estoy seguro de que eso ayudaría mucho a la comunidad. –

+0

@CodesInChaos ¿Cómo utilizó/invocó exactamente el método 'Encrypt (string input) .....' en C#. Intenté usar 'Decrypt5 (" myEncryptedStringxyzabcblahblah ");' pero me dio un error. Diciendo que no puedo usar una clase abstracta. ¿Lo estoy llamando mal? ¿Qué es una clase no abstracta que puedo usar? Gracias. –

2

probar esta combinación:

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