Javax.crypto работает по-разному в разных версиях ОС Android?

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

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

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

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

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

    Ответ в этом вопросе SO: ошибка BouncyCastle AES при обновлении до 1,45

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

    Вот что я в итоге привел в качестве примера того, как шифровать / дешифровать с помощью пароля, что кажется совместимым между 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)); } }