javax.crypto работает по-разному в разных версиях ОС Android? - PullRequest
6 голосов
/ 11 июня 2011

Я использую этот фрагмент кода для шифрования / дешифрования данных в базе данных моего приложения:

http://www.androidsnippets.com/encryptdecrypt-strings

Похоже, что операция javax.crypto.KeyGenerator.generateKey () работаетиначе в ОС Android 2.3.3, чем в других (предыдущих?) версиях.Естественно, это представляет большую проблему для моих пользователей, когда они обновляют свое устройство с 2.2 до 2.3.3, и приложение начинает выдавать ошибки при расшифровке базы данных.

Является ли это известной проблемой?Я использую криптографическую библиотеку неправильно?У любого есть какие-либо предложения о том, как решить эту проблему, чтобы данные, зашифрованные в 2.2, можно было расшифровать в 2.3.3?

Я создал тестовое приложение, которое передает значения через функцию шифрования.Когда я запускаю его на 2.2 AVD, я получаю один результат.Когда я запускаю его на 2.3.3 AVD, я получаю другой результат.

    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));
        }
    }

Мой макет main.xml выглядит так:

    <?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>

Я не могу публиковать ссылкиили изображения, так как я новый пользователь, но вы можете расшифровать URL-адреса для следующих двух изображений, если хотите увидеть результаты:

Что я получу от 2.2:

wct.vg/wt/droid/2.2.png

.. и от 2.3.3:

wct.vg/wt/droid/2.3.3.png

Ответы [ 3 ]

5 голосов
/ 24 июня 2011

Вы неправильно используете генератор псевдослучайных чисел и его начальное число в качестве функции выведения ключей - это действительно очень плохой стиль.Генератор псевдослучайных чисел "SHA1PRNG" не является стандартом, как AES - поэтому вы никогда не знаете, какую реализацию вы получите.См. Также Существует ли стандарт SHA1PRNG ?

Меня не удивляет, что вы получаете разные результаты.Получение детерминированного результата на основе заданного начального числа не является свойством, которое вы можете ожидать от функций псевдослучайных чисел.

Если вы хотите получить криптографический ключ из пароля, используйте Функция получения ключа как PKCS # 5 / PBKDF2.Реализация PBKDF2 - AFAIR, включенная в Bouncy Castle.

2 голосов
/ 24 июня 2011
1 голос
/ 17 июля 2011

Я хотел бы поблагодарить всех, кто внес свой вклад в этот вопрос.

Вот то, что я в конечном итоге придумал в качестве примера для того, как зашифровать / расшифровать с помощью пароля, который кажется совместимым между Android 2.2 и 2.3.3.

Основная деятельность:

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();
        }

    }
}

Основной макет:

<?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>

Криптообъект:

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));
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...