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:

wct.vg/wt/droid/2.2.png

.. y de 2.3.3:

wct.vg/wt/droid/2.3.3.png

¿Fue útil?

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));
    }
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top