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:
- Android enviar archivos grandes vía socket
- ¿Cuáles son las maneras de generar programáticamente conjuntos de colores de Material Design?
- ¿Es posible acceder a las funcionalidades NFC desde una aplicación React
- Escritura de audio en el servidor a través de un socket TCP
- Cómo eliminar elementos de una lista con gesto de desplazamiento como en gmail
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
- Cómo obtener mi certificación de la huella digital android en Android Studio
- Convertir el valor de color hexadecimal (#ffffff) en valor entero
- El método getDrawingCache () de Android 2.1 View devuelve siempre null
- Gráficos de Android para XXHDPI
- Cómo escribir un Android SocketServer para escuchar en wifi
- ¿Cómo obtener energía eléctrica de Jack Head Phone?
- Emoticonos en EditText
- Reaccionar proceso de fondo nativo
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").
- Java.lang.VerifyError IllformedLocaleException
- Adición de carpetas de java al proyecto de estudio de Android