Android 4.2 rompió mi código AES de cifrado / descifrado

Es mi primera vez que pido ayuda aquí, mi departamento (un gobierno), han publicado alguna aplicación en el mercado (Google Play), y la encriptación y descripción funcionaba muy bien hasta ayer cuando recibí el Jelly Bean 4.2 en mi Nexo. El cifrado funciona bien, es de hecho cifrar la información que se almacena. Aunque al desencriptarlo, estoy recibiendo una excepción exactamente igual a esto: pad block corrompido . He comprobado la cadena y es coherente con ella en otros dispositivos (utilizando la misma clave para fines de prueba), lo que significa que es exactamente lo mismo. El problema es que necesitamos mantener la compatibilidad con versiones anteriores, lo que significa que si cambio algo en el código, debería ser capaz de leer la vieja información cifrada. La información cifrada que se almacena en SQLite, debido a que necesito codificar a Base64. La excepción ocurre en esta línea byte [] decrypted = cipher.doFinal (cifrado);

Aquí está mi clase:

import java.security.SecureRandom; import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; import android.util.Base64; public class EncodeDecodeAES { private final static String HEX = "0123456789ABCDEF"; public static String encrypt(String seed, String cleartext) throws Exception { byte[] rawKey = getRawKey(seed.getBytes()); byte[] result = encrypt(rawKey, cleartext.getBytes()); String fromHex = toHex(result); String base64 = new String(Base64.encodeToString(fromHex.getBytes(), 0)); return base64; } public static String decrypt(String seed, String encrypted) throws Exception { String base64 = new String(Base64.decode(encrypted, 0)); byte[] rawKey = getRawKey(seed.getBytes()); byte[] enc = toByte(base64); byte[] result = decrypt(rawKey, enc); return new String(result); } public static byte[] encryptBytes(String seed, byte[] cleartext) throws Exception { byte[] rawKey = getRawKey(seed.getBytes()); byte[] result = encrypt(rawKey, cleartext); return result; } public static byte[] decryptBytes(String seed, byte[] encrypted) throws Exception { byte[] rawKey = getRawKey(seed.getBytes()); byte[] result = decrypt(rawKey, encrypted); return result; } private static byte[] getRawKey(byte[] seed) throws Exception { KeyGenerator kgen = KeyGenerator.getInstance("AES"); SecureRandom sr = SecureRandom.getInstance("SHA1PRNG"); sr.setSeed(seed); try { kgen.init(256, sr); } catch (Exception e) { // Log.w(LOG, "This device doesn't suppor 256bits, trying 192bits."); try { kgen.init(192, sr); } catch (Exception e1) { // Log.w(LOG, "This device doesn't suppor 192bits, trying 128bits."); kgen.init(128, sr); } } SecretKey skey = kgen.generateKey(); 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"); cipher.init(Cipher.ENCRYPT_MODE, skeySpec); 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"); cipher.init(Cipher.DECRYPT_MODE, skeySpec); byte[] decrypted = cipher.doFinal(encrypted); return decrypted; } public static String toHex(String txt) { return toHex(txt.getBytes()); } public static String fromHex(String hex) { return new String(toByte(hex)); } public static byte[] toByte(String hexString) { int len = hexString.length() / 2; byte[] result = new byte[len]; for (int i = 0; i < len; i++) result[i] = Integer.valueOf(hexString.substring(2 * i, 2 * i + 2), 16).byteValue(); return result; } public static String toHex(byte[] buf) { if (buf == null) return ""; StringBuffer result = new StringBuffer(2 * buf.length); for (int i = 0; i < buf.length; i++) { appendHex(result, buf[i]); } return result.toString(); } private static void appendHex(StringBuffer sb, byte b) { sb.append(HEX.charAt((b >> 4) & 0x0f)).append(HEX.charAt(b & 0x0f)); } } 

Me gustaría saber (si alguien me ayuda), ¿qué estoy haciendo mal con este código, o si es un problema con Android 4.2 y si es un problema con 4.2 si tiene alguna solución?

Gracias

ADVERTENCIA Esta respuesta utiliza SecureRandom para la derivación clave, lo cual es contrario a su propósito. SecureRandom es un generador de números aleatorios y no está garantizado para producir salida consistente entre plataformas (que es lo que causó el problema en la pregunta). El mecanismo apropiado para la derivación clave es SecretKeyFactory . Esta entrada de blog de nelenkov tiene una buena reseña sobre este tema. Esta respuesta proporciona una solución para los casos en los que está restringido por el requisito de compatibilidad con versiones anteriores; Sin embargo, debe migrar a una implementación correcta tan pronto como sea posible.


Ok, hoy con un poco más de tiempo para hacer algunas investigaciones (y eliminar mi antigua publicación, que en realidad no funcionaba, lo siento) Tengo una respuesta que funciona bien, en realidad lo probé en Android 2.3.6, 2.3.7 (Que es básicamente el mismo), 4.0.4 y 4.2 y ha funcionado. Hice algunas investigaciones sobre esos vínculos:

Error de cifrado en Android 4.2 ,

BouncyCastle AES error al actualizar a 1.45 ,

http://en.wikipedia.org/wiki/Padding_(cryptography)

Entonces conseguí en esta solución gracias al contenido en esos acoplamientos arriba. Aquí está mi clase (y ahora funciona bien):

  package au.gov.dhsJobSeeker.main.readwriteprefssettings.util; import java.security.SecureRandom; import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; import android.util.Base64; public class EncodeDecodeAES { private final static String HEX = "0123456789ABCDEF"; private final static int JELLY_BEAN_4_2 = 17; private final static byte[] key = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; // static { // Security.addProvider(new BouncyCastleProvider()); // } public static String encrypt(String seed, String cleartext) throws Exception { byte[] rawKey = getRawKey(seed.getBytes()); byte[] result = encrypt(rawKey, cleartext.getBytes()); String fromHex = toHex(result); String base64 = new String(Base64.encodeToString(fromHex.getBytes(), 0)); return base64; } public static String decrypt(String seed, String encrypted) throws Exception { byte[] seedByte = seed.getBytes(); System.arraycopy(seedByte, 0, key, 0, ((seedByte.length < 16) ? seedByte.length : 16)); String base64 = new String(Base64.decode(encrypted, 0)); byte[] rawKey = getRawKey(seedByte); byte[] enc = toByte(base64); byte[] result = decrypt(rawKey, enc); return new String(result); } public static byte[] encryptBytes(String seed, byte[] cleartext) throws Exception { byte[] rawKey = getRawKey(seed.getBytes()); byte[] result = encrypt(rawKey, cleartext); return result; } public static byte[] decryptBytes(String seed, byte[] encrypted) throws Exception { byte[] rawKey = getRawKey(seed.getBytes()); byte[] result = decrypt(rawKey, encrypted); return result; } private static byte[] getRawKey(byte[] seed) throws Exception { KeyGenerator kgen = KeyGenerator.getInstance("AES"); // , "SC"); SecureRandom sr = null; if (android.os.Build.VERSION.SDK_INT >= JELLY_BEAN_4_2) { sr = SecureRandom.getInstance("SHA1PRNG", "Crypto"); } else { sr = SecureRandom.getInstance("SHA1PRNG"); } sr.setSeed(seed); try { kgen.init(256, sr); // kgen.init(128, sr); } catch (Exception e) { // Log.w(LOG, "This device doesn't suppor 256bits, trying 192bits."); try { kgen.init(192, sr); } catch (Exception e1) { // Log.w(LOG, "This device doesn't suppor 192bits, trying 128bits."); kgen.init(128, sr); } } SecretKey skey = kgen.generateKey(); 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"); // /ECB/PKCS7Padding", "SC"); cipher.init(Cipher.ENCRYPT_MODE, skeySpec); 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"); // /ECB/PKCS7Padding", "SC"); cipher.init(Cipher.DECRYPT_MODE, skeySpec); byte[] decrypted = cipher.doFinal(encrypted); return decrypted; } public static String toHex(String txt) { return toHex(txt.getBytes()); } public static String fromHex(String hex) { return new String(toByte(hex)); } public static byte[] toByte(String hexString) { int len = hexString.length() / 2; byte[] result = new byte[len]; for (int i = 0; i < len; i++) result[i] = Integer.valueOf(hexString.substring(2 * i, 2 * i + 2), 16).byteValue(); return result; } public static String toHex(byte[] buf) { if (buf == null) return ""; StringBuffer result = new StringBuffer(2 * buf.length); for (int i = 0; i < buf.length; i++) { appendHex(result, buf[i]); } return result.toString(); } private static void appendHex(StringBuffer sb, byte b) { sb.append(HEX.charAt((b >> 4) & 0x0f)).append(HEX.charAt(b & 0x0f)); } } 

Sin embargo, la respuesta PBrando (por encima, también funciona, debido a que lo marcó como solución.), Aunque como estaba buscando una manera de mantener un tamaño de archivo de aplicación similar con es ahora, he optado por utilizar este enfoque. Porque no necesito importar jarras externas. Hice poner la clase entera para apenas en caso de que cualquiera de usted esté teniendo el mismo problema, y ​​quiera apenas copiar y pegarlo.

Puede intentar utilizar la biblioteca SpongyCastle. Es el BouncyCastle parcheado para compilar en Android.

Dado que es compatible con BouncyCastle (sólo el nombre del paquete y el proveedor de servicios son diferentes, "SC" en lugar de "BC"), y Android utiliza un subconjunto de BouncyCastle, integrar SpongyCastle en su código debe ser una tarea trivial.

Usted puede encontrar SpongyCastle aquí: http://rtyley.github.com/spongycastle/

Tenga cuidado de registrar SpongyCastle como se explica en su página web:

 static { Security.addProvider(new org.spongycastle.jce.provider.BouncyCastleProvider()); } 

Cuando obtenga instancias de objetos criptográficos, especifique también el proveedor ("SC").

  • Android: cómo ampliar una plaza específica en los mapas de Skobbler
  • ¿Cómo crear un paquete de idioma personalizado para Android?
  • Cómo utilizar muestras de color de Android
  • ¿Cuál es el uso de 0xff -colors en android?
  • La prueba CTS de Android puede ejecutarse en varios dispositivos en paralelo
  • Cómo convertir el código Color.RED en valores enteros de color en Android Java?
  • Posición de Android, Longitud (min, máximo), Latitud (min, max)
  • Invertir colores de Android dibujable
  • Android trata con dispositivos IoT sin conexión a Internet
  • ¿Para qué se utiliza Android TextUtils?
  • Compruebe si la cadena es número en android
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.