2010-01-19 26 views
19

He encontrado muchos ejemplos de cómo hacer el cifrado en C#, y un par para Android, pero estoy buscando especialmente una forma de manejar el cifrado (usando algo como AES, TripleDES, etc. .) de Android, y finalmente terminan siendo descifrados en C#. Encontré un example for encoding AES en Android y encoding/decoding AES en C# pero no estoy seguro si estos son compatibles (C# requiere un IV, no se especifica nada para esto en el ejemplo de Android). Además, una recomendación sobre una buena forma de codificar la cadena encriptada para la transmisión a través de HTTP (Base64?) Sería útil. Gracias.Cifrado compatible entre Android y C#

+0

El enlace está muerto. ¿Puedes agregar el fragmento de código que usaste para Android? –

Respuesta

38

Recibí ayuda del http://oogifu.blogspot.com/2009/01/aes-in-java-and-c.html.

Aquí es mi clase Java:

package com.neocodenetworks.smsfwd; 

import java.security.*; 
import javax.crypto.*; 
import javax.crypto.spec.*; 
import android.util.Log; 

public class Crypto { 
    public static final String TAG = "smsfwd"; 

    private static Cipher aesCipher; 
    private static SecretKey secretKey; 
    private static IvParameterSpec ivParameterSpec; 

    private static String CIPHER_TRANSFORMATION = "AES/CBC/PKCS5Padding"; 
    private static String CIPHER_ALGORITHM = "AES"; 
    // Replace me with a 16-byte key, share between Java and C# 
    private static byte[] rawSecretKey = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
              0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; 

    private static String MESSAGEDIGEST_ALGORITHM = "MD5"; 

    public Crypto(String passphrase) { 
     byte[] passwordKey = encodeDigest(passphrase); 

     try { 
      aesCipher = Cipher.getInstance(CIPHER_TRANSFORMATION); 
     } catch (NoSuchAlgorithmException e) { 
      Log.e(TAG, "No such algorithm " + CIPHER_ALGORITHM, e); 
     } catch (NoSuchPaddingException e) { 
      Log.e(TAG, "No such padding PKCS5", e); 
     } 

     secretKey = new SecretKeySpec(passwordKey, CIPHER_ALGORITHM); 
     ivParameterSpec = new IvParameterSpec(rawSecretKey); 
    } 

    public String encryptAsBase64(byte[] clearData) { 
     byte[] encryptedData = encrypt(clearData); 
     return net.iharder.base64.Base64.encodeBytes(encryptedData); 
    } 

    public byte[] encrypt(byte[] clearData) { 
     try { 
      aesCipher.init(Cipher.ENCRYPT_MODE, secretKey, ivParameterSpec); 
     } catch (InvalidKeyException e) { 
      Log.e(TAG, "Invalid key", e); 
      return null; 
     } catch (InvalidAlgorithmParameterException e) { 
      Log.e(TAG, "Invalid algorithm " + CIPHER_ALGORITHM, e); 
      return null; 
     } 

     byte[] encryptedData; 
     try { 
      encryptedData = aesCipher.doFinal(clearData); 
     } catch (IllegalBlockSizeException e) { 
      Log.e(TAG, "Illegal block size", e); 
      return null; 
     } catch (BadPaddingException e) { 
      Log.e(TAG, "Bad padding", e); 
      return null; 
     } 
     return encryptedData; 
    } 

    private byte[] encodeDigest(String text) { 
     MessageDigest digest; 
     try { 
      digest = MessageDigest.getInstance(MESSAGEDIGEST_ALGORITHM); 
      return digest.digest(text.getBytes()); 
     } catch (NoSuchAlgorithmException e) { 
      Log.e(TAG, "No such algorithm " + MESSAGEDIGEST_ALGORITHM, e); 
     } 

     return null; 
    } 
} 

que utilizan http://iharder.sourceforge.net/current/java/base64/ para la codificación base64.

Aquí es mi clase C#:

using System; 
using System.Text; 
using System.Security.Cryptography; 

namespace smsfwdClient 
{ 
    public class Crypto 
    { 
     private ICryptoTransform rijndaelDecryptor; 
     // Replace me with a 16-byte key, share between Java and C# 
     private static byte[] rawSecretKey = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; 

     public Crypto(string passphrase) 
     { 
      byte[] passwordKey = encodeDigest(passphrase); 
      RijndaelManaged rijndael = new RijndaelManaged(); 
      rijndaelDecryptor = rijndael.CreateDecryptor(passwordKey, rawSecretKey); 
     } 

     public string Decrypt(byte[] encryptedData) 
     { 
      byte[] newClearData = rijndaelDecryptor.TransformFinalBlock(encryptedData, 0, encryptedData.Length); 
      return Encoding.ASCII.GetString(newClearData); 
     } 

     public string DecryptFromBase64(string encryptedBase64) 
     { 
      return Decrypt(Convert.FromBase64String(encryptedBase64)); 
     } 

     private byte[] encodeDigest(string text) 
     { 
      MD5CryptoServiceProvider x = new System.Security.Cryptography.MD5CryptoServiceProvider(); 
      byte[] data = Encoding.ASCII.GetBytes(text); 
      return x.ComputeHash(data); 
     } 
    } 
} 

Realmente espero que esto ayude a alguien más!

+7

Sí ... Me encanta cuando las personas responden mi pregunta técnica con un montón de "Oh, deberías hacer eso" o "Oh, deberías hacer esto". Gracias por ser práctico y por dejar la implementación de su clase. – kape123

+0

He cifrado el contenido de mi archivo con su código Java y he guardado el contenido cifrado en encrypted.txt. Ahora me gustaría descifrarlo con la herramienta openssl usando este comando: openssl enc -d -a -p -aes-256-cbc -salt -in encrypted.txt -out descrypted.txt -pass pass: myPassword pero me da error: mal número mágico ¿Alguna idea de lo que está mal? Incluso si elimino el parámetro -salt del comando, sigo recibiendo el mismo error. – uerceg

+0

@uerceg: Vas a querer comenzar una nueva pregunta para eso. Esta solución no usa OpenSSL, así que no espero que funcione. – Jess

1

Sí, debería estar bien, siempre que el tamaño de la clave sea el mismo: AES de 128 bits y el modo de cifrado de bloque correcto (CBC). Es posible que tenga problemas con el relleno, pero eso debería ser bastante fácil de resolver. Me encontré con estos problemas con Java y Python recientemente, pero al final todo funcionó. Base64 para la codificación debería estar bien sobre HTTP. ¡Buena suerte!

1

Si implementa correctamente el mismo cifrado (como AES) y el modo (como CTR, CFB, CCM, etc.) en ambos extremos, el texto cifrado de un extremo se puede descifrar por el otro extremo independientemente de la plataforma.

El ejemplo de Android al que ha vinculado parece utilizar el modo ECB, y por lo tanto no es seguro para sus propósitos. Es sumamente importante que comprenda las implicaciones del modo de bloque que selecciona. Es muy fácil equivocarse en este nivel, lo que da como resultado un sistema que no es tan seguro como crees.

EDITAR: Retiro esto, no está utilizando ECB, pero la forma en que genera la IV no es práctica. En cualquier caso, mi punto de entender las implicaciones de los modos de bloque se mantiene.

Puede comenzar con este wikipedia article. El libro de Bruce Schneier 'Practical Cryptography' también es muy valioso para cualquiera que implemente seguridad criptográfica.

En cuanto a la codificación de la cadena, si debe convertir la cadena en texto ASCII, Base64 es una buena manera, pero le sugiero que investigue el uso de HTTP PUT o POST para evitar este paso adicional.

4

En la muestra C# código fuente proporcionado, cuidado con esta línea:

Encoding.ASCII.GetString(newClearData); 

UTF-8 es la codificación predeterminada para Android, cuerda de manera encriptada (especialmente los caracteres no ASCII como el chino) serán pasados a C# asumiendo UTF-8. El texto se convierte codificado si se decodifica de nuevo en cadena usando codificación ASCII. Aquí está uno mejor,

Encoding.UTF8.GetString(newClearData); 

Gracias!

1

La mayoría de los ejemplos en Internet son una implementación débil de AES. Para que una implementación sea fuerte, se debe usar IV aleatoria todo el tiempo y la clave debe ser hash.

Para más segura (IV + aleatoria clave hash) multiplataforma (Android, iOS, C#) la implementación de AES ver mi respuesta aquí - https://stackoverflow.com/a/24561148/2480840