javax.crypto trabajando de manera diferente en diferentes versiones de Android OS?
-
26-10-2019 - |
Pregunta
Estoy usando este fragmento de código para cifrar/descifrar datos en la base de datos de mi aplicación:
http://www.androidsnippets.com/encryptdecrypt-strings
Parece que la operación Javax.crypto.KeyGenerator.GenerateKey () funciona de manera diferente en Android 2.3.3 OS que en otras versiones (¿Anterior?). Naturalmente, esto presenta un problema importante para mis usuarios cuando actualizan su dispositivo de 2.2 a 2.3.3 y la aplicación comienza a lanzar errores que descifran la base de datos.
¿Es este un problema conocido? ¿Estoy usando la biblioteca criptográfica incorrectamente? ¿Alguien tiene alguna sugerencia sobre cómo abordar esto para que los datos encriptados en 2.2 puedan descifrarse en 2.3.3?
Construí una aplicación de prueba que alimenta los valores a través de la función Cicrypt. Cuando lo ejecuto en un AVD 2.2, obtengo un resultado. Cuando lo ejecuto en un 2.3.3 AVD, obtengo un resultado diferente.
import java.security.SecureRandom;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;
public class main extends Activity {
TextView tvOutput;
static String out;
String TEST_STRING = "abcdefghijklmnopqrstuvwxyz";
String PASSKEY = "ThePasswordIsPassord";
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
tvOutput = (TextView) findViewById(R.id.tvOutput);
}
@Override
public void onResume() {
super.onResume();
out = "";
runTest();
tvOutput.setText(out);
}
private void runTest() {
out = "Test string: " + TEST_STRING + "\n";
out += "Passkey: " + PASSKEY + "\n";
try {
out += "Encrypted: " + encrypt(PASSKEY, TEST_STRING) + "\n";
} catch (Exception e) {
out += "Error: " + e.getMessage() + "\n";
e.printStackTrace();
}
}
public static String encrypt(String seed, String cleartext)
throws Exception {
byte[] rawKey = getRawKey(seed.getBytes());
byte[] result = encrypt(rawKey, cleartext.getBytes());
return toHex(result) + "\n" + "Raw Key: " + String.valueOf(rawKey)
+ "\n";
}
private static byte[] getRawKey(byte[] seed) throws Exception {
KeyGenerator kgen = KeyGenerator.getInstance("AES");
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
sr.setSeed(seed);
kgen.init(128, sr); // 192 and 256 bits may not be available
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;
}
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 final static String HEX = "0123456789ABCDEF";
private static void appendHex(StringBuffer sb, byte b) {
sb.append(HEX.charAt((b >> 4) & 0x0f)).append(HEX.charAt(b & 0x0f));
}
}
Mi diseño principal.xml se ve así:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TextView android:layout_width="fill_parent"
android:layout_height="wrap_content" android:id="@+id/tvOutput" />
</LinearLayout>
No puedo publicar enlaces o imágenes ya que soy un nuevo usuarios, pero puede descifrar las URL para las siguientes dos imágenes si desea ver los resultados:
Lo que obtengo de 2.2:
.. y de 2.3.3:
Solución
Usted está utilizando mal un generador de números pseudo aleatorios y es una semilla como una función de derivación clave: este es realmente un estilo realmente malo. El generador de números pseudo aleatorios "SHA1PRNG" no es un estándar como AES, por lo tanto, nunca se sabe qué implementación obtiene. Ver también ¿Hay un estándar SHA1PRNG??
No es de extrañar que obtengas resultados diferentes. Obtener un resultado determinista basado en una semilla determinada no es una propiedad que puede esperar de un número pseudo aleatorio funciones.
Si desea obtener una clave criptográfica de una contraseña, utilice un Función de derivación de clave como PKCS #5 / PBKDF2. Una implementación de PBKDF2 es AFAIR incluida en Bouncy Castle.
Otros consejos
La respuesta está en esta pregunta: Error AES de Bouncycastle al actualizar a 1.45
Me gustaría agradecer a todos los que contribuyeron a esta pregunta.
Esto es lo que finalmente se me ocurrió como ejemplo de cómo cifrar/descifrar usando una contraseña, que parece consistente entre Android 2.2 y 2.3.3.
Actividad principal:
package cc.ndl.testencryption;
import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;
public class main extends Activity {
TextView tvOutput;
static String out;
String TEST_STRING = "abcdefghijklmnopqrstuvwxyz";
static String PASSKEY = "ThePasswordIsPassord";
static byte[] SALT = { 1, 2, 4, 5 };
static int ITERATIONS = 1979;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
tvOutput = (TextView) findViewById(R.id.tvOutput);
}
@Override
public void onResume() {
super.onResume();
out = "";
runTest();
tvOutput.setText(out);
}
private void runTest() {
out = "Test string: " + TEST_STRING + "\n";
out += "Passkey: " + PASSKEY + "\n";
try {
Crypto crypto = new Crypto(PASSKEY);
String encryptedData = crypto.encrypt(TEST_STRING);
out += "Encrypted: " + encryptedData + "\n";
out += "Decrypted: " + crypto.decrypt(encryptedData);
} catch (Exception e) {
out += "Error: " + e.getMessage() + "\n";
e.printStackTrace();
}
}
}
Diseño principal:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TextView android:layout_width="fill_parent"
android:layout_height="wrap_content" android:id="@+id/tvOutput" />
</LinearLayout>
Objeto criptográfico:
package cc.ndl.testencryption;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.KeySpec;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;
public class Crypto {
Cipher ecipher;
Cipher dcipher;
// 8-byte Salt
byte[] salt = { 1, 2, 4, 5, 7, 8, 3, 6 };
// Iteration count
int iterationCount = 1979;
Crypto(String passPhrase) {
try {
// Create the key
KeySpec keySpec = new PBEKeySpec(passPhrase.toCharArray(), salt,
iterationCount);
SecretKey key = SecretKeyFactory.getInstance(
"PBEWITHSHA256AND128BITAES-CBC-BC").generateSecret(keySpec);
ecipher = Cipher.getInstance(key.getAlgorithm());
dcipher = Cipher.getInstance(key.getAlgorithm());
// Prepare the parameter to the ciphers
AlgorithmParameterSpec paramSpec = new PBEParameterSpec(salt,
iterationCount);
// Create the ciphers
ecipher.init(Cipher.ENCRYPT_MODE, key, paramSpec);
dcipher.init(Cipher.DECRYPT_MODE, key, paramSpec);
} catch (Exception e) {
}
}
public String encrypt(String str) {
String rVal;
try {
// Encode the string into bytes using utf-8
byte[] utf8 = str.getBytes("UTF8");
// Encrypt
byte[] enc = ecipher.doFinal(utf8);
// Encode bytes to base64 to get a string
rVal = toHex(enc);
} catch (Exception e) {
rVal = "Error encrypting: " + e.getMessage();
}
return rVal;
}
public String decrypt(String str) {
String rVal;
try {
// Decode base64 to get bytes
byte[] dec = toByte(str);
// Decrypt
byte[] utf8 = dcipher.doFinal(dec);
// Decode using utf-8
rVal = new String(utf8, "UTF8");
} catch (Exception e) {
rVal = "Error encrypting: " + e.getMessage();
}
return rVal;
}
private 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;
}
private 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 final static String HEX = "0123456789ABCDEF";
private static void appendHex(StringBuffer sb, byte b) {
sb.append(HEX.charAt((b >> 4) & 0x0f)).append(HEX.charAt(b & 0x0f));
}
}