Android 4.2 hat meinen Verschlüsselungs- / Entschlüsselungscode zerstört und die bereitgestellten Lösungen funktionieren nicht

Zuallererst habe ich bereits gesehen, dass Android 4.2 meinen AES-Code zum Verschlüsseln / Entschlüsseln und den Verschlüsselungserrors auf Android 4.2 und die bereitgestellte Lösung zerstört hat:

SecureRandom sr = null; if (android.os.Build.VERSION.SDK_INT >= JELLY_BEAN_4_2) { sr = SecureRandom.getInstance("SHA1PRNG", "Crypto"); } else { sr = SecureRandom.getInstance("SHA1PRNG"); } 

funktioniert nicht für mich, denn beim Decodieren von Daten, die in Android <4.2 in Android 4.2 verschlüsselt sind, bekomme ich:

 javax.crypto.BadPaddingException: pad block corrupted at com.android.org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher.engineDoFinal(BaseBlockCipher.java:709) 

Mein Code ist ziemlich einfach und funktionierte bis Android 4.2:

 public static byte[] encrypt(byte[] data, String seed) throws Exception { KeyGenerator keygen = KeyGenerator.getInstance("AES"); SecureRandom secrand = SecureRandom.getInstance("SHA1PRNG"); secrand.setSeed(seed.getBytes()); keygen.init(128, secrand); SecretKey seckey = keygen.generateKey(); byte[] rawKey = seckey.getEncoded(); SecretKeySpec skeySpec = new SecretKeySpec(rawKey, "AES"); Cipher cipher = Cipher.getInstance("AES"); cipher.init(Cipher.ENCRYPT_MODE, skeySpec); return cipher.doFinal(data); } public static byte[] decrypt(byte[] data, String seed) throws Exception { KeyGenerator keygen = KeyGenerator.getInstance("AES"); SecureRandom secrand = SecureRandom.getInstance("SHA1PRNG"); secrand.setSeed(seed.getBytes()); keygen.init(128, secrand); SecretKey seckey = keygen.generateKey(); byte[] rawKey = seckey.getEncoded(); SecretKeySpec skeySpec = new SecretKeySpec(rawKey, "AES"); Cipher cipher = Cipher.getInstance("AES"); cipher.init(Cipher.DECRYPT_MODE, skeySpec); return cipher.doFinal(data); } 

Meine Vermutung ist, dass der Standardanbieter nicht das einzige war, was sich in Android 4.2 geändert hat, sonst würde mein Code mit der vorgeschlagenen Lösung funktionieren.

Mein Code basierte auf einem Post, den ich vor langer Zeit hier bei StackOverflow gefunden hatte. Ich sehe, dass es sich von den erwähnten Posts unterscheidet, da es nur Byte-Arrays entschlüsselt und entschlüsselt, während die anderen Lösungen Strings entschlüsseln und entschlüsseln (ich glaube, HEX Strings).

Hat es mit dem Samen zu tun? Hat es eine minimale / maximale Länge, Beschränkung von Zeichen usw.?

Irgendeine Idee / Lösung?

EDIT : Nach vielen Tests sehe ich, dass es 2 Probleme gibt:

  1. Der Anbieter wurde in Android 4.2 (API 17) geändert -> Dieser ist einfach zu beheben, wenden Sie einfach die oben erwähnte Lösung an

  2. BouncyCastle hat sich in Android 2.2 (API 8) -> Android2.3 (API 9) von 1.34 auf 1.45 geändert, so dass das oben beschriebene Entschlüsselungsproblem dasselbe ist wie hier beschrieben: BouncyCastle AES Fehler beim Upgrade auf 1.45

Jetzt also die Frage ist: Gibt es eine Möglichkeit, Daten in BouncyCastle 1.45+ in BouncyCastle 1.34 verschlüsselt wiederherzustellen?

Solutions Collecting From Web of "Android 4.2 hat meinen Verschlüsselungs- / Entschlüsselungscode zerstört und die bereitgestellten Lösungen funktionieren nicht"

Zuerst ein Disclaimer:

Verwenden Sie niemals “SecureRandom”, um einen Schlüssel abzuleiten! Das ist kaputt und macht keinen Sinn!

Wenn Sie einen AES-Schlüssel von der Festplatte lesen, speichern Sie einfach den eigentlichen Schlüssel und gehen Sie nicht durch diesen seltsamen Tanz. Sie können einen SecretKey für die AES-Nutzung aus den Bytes erhalten, indem Sie Folgendes tun:

  SecretKey key = new SecretKeySpec(keyBytes, "AES"); 

Wenn Sie ein Passwort verwenden, um einen Schlüssel abzuleiten, folgen Sie Nelenkovs exzellentem Tutorial mit der Einschränkung, dass eine gute Faustregel darin besteht, dass die Salzgröße dieselbe Größe wie die Schlüsselausgabe haben sollte. Es sieht aus wie das:

  /* User types in their password: */ String password = "password"; /* Store these things on disk used to derive key later: */ int iterationCount = 1000; int saltLength = 32; // bytes; should be the same size as the output (256 / 8 = 32) int keyLength = 256; // 256-bits for AES-256, 128-bits for AES-128, etc byte[] salt; // Should be of saltLength /* When first creating the key, obtain a salt with this: */ SecureRandom random = new SecureRandom(); byte[] salt = new byte[saltLength]; random.nextBytes(salt); /* Use this to derive the key from the password: */ KeySpec keySpec = new PBEKeySpec(password.toCharArray(), salt, iterationCount, keyLength); SecretKeyFactory keyFactory = SecretKeyFactory .getInstance("PBKDF2WithHmacSHA1"); byte[] keyBytes = keyFactory.generateSecret(keySpec).getEncoded(); SecretKey key = new SecretKeySpec(keyBytes, "AES"); 

Das ist es. Alles andere solltest du nicht benutzen.

Das Problem ist, dass mit dem neuen Anbieter der folgende Codeschnipsel

 KeyGenerator keygen = KeyGenerator.getInstance("AES"); SecureRandom secrand = SecureRandom.getInstance("SHA1PRNG"); secrand.setSeed(seed.getBytes()); keygen.init(128, secrand); SecretKey seckey = keygen.generateKey(); byte[] rawKey = seckey.getEncoded(); 

erzeugt rawKey jeder Ausführung ein anderes, wirklich zufälliges rawKey . Sie versuchen also, mit einem Schlüssel zu entschlüsseln, der sich von dem zum Verschlüsseln von Daten unterscheidet, und Sie erhalten die Ausnahme. Sie können Ihren Schlüssel oder Ihre Daten nicht wiederherstellen, wenn sie auf diese Weise generiert wurden und nur der Seed gespeichert wurde .

Was es für mich repariert hat (wie @Giorgio vorgeschlagen hat ) war nur das Ersetzen :

 SecureRandom secrand = SecureRandom.getInstance("SHA1PRNG"); 

mit diesem :

 SecureRandom secrand = SecureRandom.getInstance("SHA1PRNG", "Crypto"); 
 private static final int ITERATION_COUNT = 1000; private static final int KEY_LENGTH = 256; private static final String PBKDF2_DERIVATION_ALGORITHM = "PBKDF2WithHmacSHA1"; private static final String CIPHER_ALGORITHM = "AES/CBC/PKCS5Padding"; private static final int PKCS5_SALT_LENGTH = 32; private static final String DELIMITER = "]"; private static final SecureRandom random = new SecureRandom(); public static String encrypt(String plaintext, String password) { byte[] salt = generateSalt(); SecretKey key = deriveKey(password, salt); try { Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM); byte[] iv = generateIv(cipher.getBlockSize()); IvParameterSpec ivParams = new IvParameterSpec(iv); cipher.init(Cipher.ENCRYPT_MODE, key, ivParams); byte[] cipherText = cipher.doFinal(plaintext.getBytes("UTF-8")); if(salt != null) { return String.format("%s%s%s%s%s", toBase64(salt), DELIMITER, toBase64(iv), DELIMITER, toBase64(cipherText)); } return String.format("%s%s%s", toBase64(iv), DELIMITER, toBase64(cipherText)); } catch (GeneralSecurityException e) { throw new RuntimeException(e); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } } public static String decrypt(String ciphertext, String password) { String[] fields = ciphertext.split(DELIMITER); if(fields.length != 3) { throw new IllegalArgumentException("Invalid encypted text format"); } byte[] salt = fromBase64(fields[0]); byte[] iv = fromBase64(fields[1]); byte[] cipherBytes = fromBase64(fields[2]); SecretKey key = deriveKey(password, salt); try { Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM); IvParameterSpec ivParams = new IvParameterSpec(iv); cipher.init(Cipher.DECRYPT_MODE, key, ivParams); byte[] plaintext = cipher.doFinal(cipherBytes); return new String(plaintext, "UTF-8"); } catch (GeneralSecurityException e) { throw new RuntimeException(e); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } } private static byte[] generateSalt() { byte[] b = new byte[PKCS5_SALT_LENGTH]; random.nextBytes(b); return b; } private static byte[] generateIv(int length) { byte[] b = new byte[length]; random.nextBytes(b); return b; } private static SecretKey deriveKey(String password, byte[] salt) { try { KeySpec keySpec = new PBEKeySpec(password.toCharArray(), salt, ITERATION_COUNT, KEY_LENGTH); SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(PBKDF2_DERIVATION_ALGORITHM); byte[] keyBytes = keyFactory.generateSecret(keySpec).getEncoded(); return new SecretKeySpec(keyBytes, "AES"); } catch (GeneralSecurityException e) { throw new RuntimeException(e); } } private static String toBase64(byte[] bytes) { return Base64.encodeToString(bytes, Base64.NO_WRAP); } private static byte[] fromBase64(String base64) { return Base64.decode(base64, Base64.NO_WRAP); } 

Quelle

Ich bin nicht in der Lage, Ihnen eine Antwort auf Ihre Frage zu geben, aber ich würde einfach versuchen, dies zu umgehen> – Wenn Sie Probleme mit Bouncycastle über Geräte / Betriebssystemversion haben, sollten Sie integrierte Versionen komplett ablegen und stattdessen Bouncycastle als Glas hinzufügen zu deinem Projekt, ändere deinen import , um auf diesen Krug zu zeigen, neu zu bauen und angenommen, dass alles funktioniert, wärst du immun gegen Android eingebaute Versionsänderungen von jetzt an.

Da mir das alles nicht half, ein verschlüsseltes Passwort zu generieren, das für alle Android-Geräte deterministisch war (> = 2.1), suchte ich nach einer anderen AES-Implementierung. Ich habe einen gefunden, der auf allen Geräten für mich funktioniert. Ich bin kein Sicherheitsspezialist, daher bitte nicht meine Antwort ablehnen, wenn die Technik nicht so sicher ist, wie es sein könnte. Ich poste nur den Code für Leute, die in demselben Problem gelaufen sind, mit dem ich vorher konfrontiert worden war.

 import java.security.GeneralSecurityException; import javax.crypto.Cipher; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import android.util.Log; public class EncodeDecodeAES { private static final String TAG_DEBUG = "TAG"; private IvParameterSpec ivspec; private SecretKeySpec keyspec; private Cipher cipher; private String iv = "fedcba9876543210";//Dummy iv (CHANGE IT!) private String SecretKey = "0123456789abcdef";//Dummy secretKey (CHANGE IT!) public EncodeDecodeAES() { ivspec = new IvParameterSpec(iv.getBytes()); keyspec = new SecretKeySpec(SecretKey.getBytes(), "AES"); try { cipher = Cipher.getInstance("AES/CBC/NoPadding"); } catch (GeneralSecurityException e) { Log.d(TAG_DEBUG, e.getMessage()); } } public byte[] encrypt(String text) throws Exception { if (text == null || text.length() == 0) throw new Exception("Empty string"); byte[] encrypted = null; try { cipher.init(Cipher.ENCRYPT_MODE, keyspec, ivspec); encrypted = cipher.doFinal(padString(text).getBytes()); } catch (Exception e) { Log.d(TAG_DEBUG, e.getMessage()); throw new Exception("[encrypt] " + e.getMessage()); } return encrypted; } public byte[] decrypt(String code) throws Exception { if (code == null || code.length() == 0) throw new Exception("Empty string"); byte[] decrypted = null; try { cipher.init(Cipher.DECRYPT_MODE, keyspec, ivspec); decrypted = cipher.doFinal(hexToBytes(code)); } catch (Exception e) { Log.d(TAG_DEBUG, e.getMessage()); throw new Exception("[decrypt] " + e.getMessage()); } return decrypted; } public static String bytesToHex(byte[] data) { if (data == null) { return null; } int len = data.length; String str = ""; for (int i = 0; i < len; i++) { if ((data[i] & 0xFF) < 16) str = str + "0" + java.lang.Integer.toHexString(data[i] & 0xFF); else str = str + java.lang.Integer.toHexString(data[i] & 0xFF); } return str; } public static byte[] hexToBytes(String str) { if (str == null) { return null; } else if (str.length() < 2) { return null; } else { int len = str.length() / 2; byte[] buffer = new byte[len]; for (int i = 0; i < len; i++) { buffer[i] = (byte) Integer.parseInt(str.substring(i * 2, i * 2 + 2), 16); } return buffer; } } private static String padString(String source) { char paddingChar = ' '; int size = 16; int x = source.length() % size; int padLength = size - x; for (int i = 0; i < padLength; i++) { source += paddingChar; } return source; } } 

Du kannst es benutzen wie:

 EncodeDecodeAES aes = new EncodeDecodeAES (); /* Encrypt */ String encrypted = EncodeDecodeAES.bytesToHex(aes.encrypt("Text to Encrypt")); /* Decrypt */ String decrypted = new String(aes.decrypt(encrypted)); 

Quelle: HIER

Es hat tatsächlich mit dem Seed zu tun und es sollte auch ein Vielfaches von 8 (wie 8, 16, 24 oder 32) sein, versuch den Seed mit A’s und B’s oder 1’s und 0’s zu vervollständigen (muss sowas sein wie ABAB .. ., weil AAA .. oder BBB .. wird auch nicht funktionieren.) bis zu einem Vielfachen von 8 Nummer zu erreichen. Es gibt noch eine andere Sache, wenn Sie nur Bytes lesen und verschlüsseln (nicht wie bei Char64 in Char64 konvertieren), dann benötigen Sie ein passendes PKCS5 oder PKCS7 Padding, aber in Ihrem Fall (nur 128bits und es wurde mit älteren erstellt Versionen von Android) PKCS5 wäre genug, aber Sie sollten es auch in Ihre SecreteKeySpec etwas wie “AES / CBC / PKCS5Padding” oder “AES / ECB / PKCS5Padding” anstatt nur “AES”, weil Android 4.2 es PKCS7Padding als Standard verwendet und wenn es nur Bytes sind, brauchen Sie wirklich den gleichen Algorithmus, der zuvor der Standard war. Versuchen Sie, ein Gerät mit einem Android früher als 4.2 zu erhalten, überprüfen Sie den Objektbaum auf Ihrem ” keygen.init (128, secrand); “, wenn ich mich nicht irre, dass es die Label- Chiffre hat , dann benutze es. Versuche es.