Wie verschlüssele und entschlüssele ich Dateien in Android?

73

Ich möchte die Datei verschlüsseln und auf einer SD-Karte speichern. Ich möchte diese verschlüsselte Datei entschlüsseln und erneut auf der SD-Karte speichern. Ich habe versucht, eine Datei zu verschlüsseln, indem ich sie als Dateistream geöffnet und verschlüsselt habe, aber sie funktioniert nicht. Ich möchte eine Idee, wie das geht.

Pratik
quelle

Antworten:

68

Verwenden Sie ein CipherOutputStreamoder CipherInputStreammit einem Cipherund Ihrem FileInputStream/ FileOutputStream.

Ich würde so etwas wie Cipher.getInstance("AES/CBC/PKCS5Padding")zum Erstellen der CipherKlasse vorschlagen . Der CBC-Modus ist sicher und weist nicht die Schwachstellen des EZB-Modus für nicht zufällige Klartexte auf . Es sollte in jeder generischen kryptografischen Bibliothek vorhanden sein, um eine hohe Kompatibilität zu gewährleisten.

Vergessen Sie nicht, einen von einem sicheren Zufallsgenerator generierten Initialisierungsvektor (IV) zu verwenden, wenn Sie mehrere Dateien mit demselben Schlüssel verschlüsseln möchten. Sie können die einfache IV am Anfang des Chiffretextes voranstellen. Es ist immer genau ein Block (16 Bytes) groß.

Wenn Sie ein Kennwort verwenden möchten, stellen Sie bitte sicher, dass Sie einen guten Mechanismus zur Schlüsselableitung verwenden (suchen Sie nach kennwortbasierter Verschlüsselung oder kennwortbasierter Schlüsselableitung). PBKDF2 ist das am häufigsten verwendete Kennwortbasierte Schlüsselableitungsschema und ist in den meisten Java-Laufzeiten , einschließlich Android, vorhanden. Beachten Sie, dass SHA-1 eine etwas veraltete Hash-Funktion ist, in PBKDF2 jedoch in Ordnung sein sollte und derzeit die am besten kompatible Option darstellt.

Geben Sie beim Codieren / Decodieren von Zeichenfolgen immer die Zeichencodierung an. Andernfalls treten Probleme auf, wenn sich die Plattformcodierung von der vorherigen unterscheidet. Mit anderen Worten, nicht verwenden, String.getBytes()sondern verwenden String.getBytes(StandardCharsets.UTF_8).

Um die Sicherheit zu erhöhen, fügen Sie bitte kryptografische Integrität und Authentizität hinzu, indem Sie eine sichere Prüfsumme (MAC oder HMAC) über den Chiffretext und IV hinzufügen, vorzugsweise mit einem anderen Schlüssel. Ohne ein Authentifizierungs-Tag kann der Chiffretext so geändert werden, dass die Änderung nicht erkannt werden kann.

Seien Sie gewarnt, dass CipherInputStream möglicherweise keine Meldung BadPaddingExceptionerfolgt. Dies gilt auch BadPaddingExceptionfür authentifizierte Chiffren wie GCM. Dies würde die Streams für diese Art von authentifizierten Chiffren inkompatibel und unsicher machen.

Maarten Bodewes
quelle
6
+1 für "
Geben Sie
1
@abhy Ab Java 7 können Sie die StandardCharsetsFunktion für Ihre Bequemlichkeit verwenden. Offensichtlich kann auch ein static importvon StandardCharsetsnützlich sein.
Maarten Bodewes
Vielen Dank für die Aktualisierung. Ich schätze wirklich.
Abhy
1
Ich gehe davon aus, dass "IV" für "Initialisierungsvektor" steht. Sie sollten dies mindestens einmal sagen, vielleicht das erste Mal, wenn es erwähnt wird, für diejenigen, die es nicht wissen. Es wäre auch schön, Links zu haben, die erklären, warum diese spezielle Konfiguration von Cipher wünschenswert ist.
Hamid
@ Hamid OK, habe den Text ein wenig geändert, ich hatte nicht erwartet, dass dieser eher allgemeine Rat so populär wird :)
Maarten Bodewes
56

Ich hatte ein ähnliches Problem und für das Ver- / Entschlüsseln habe ich diese Lösung gefunden:

public static byte[] generateKey(String password) throws Exception
{
    byte[] keyStart = password.getBytes("UTF-8");

    KeyGenerator kgen = KeyGenerator.getInstance("AES");
    SecureRandom sr = SecureRandom.getInstance("SHA1PRNG", "Crypto");
    sr.setSeed(keyStart);
    kgen.init(128, sr);
    SecretKey skey = kgen.generateKey();
    return skey.getEncoded();
}

public static byte[] encodeFile(byte[] key, byte[] fileData) throws Exception
{

    SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");
    Cipher cipher = Cipher.getInstance("AES");
    cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
    byte[] encrypted = cipher.doFinal(fileData);

    return encrypted;
}

public static byte[] decodeFile(byte[] key, byte[] fileData) throws Exception
{
    SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");
    Cipher cipher = Cipher.getInstance("AES");
    cipher.init(Cipher.DECRYPT_MODE, skeySpec);

    byte[] decrypted = cipher.doFinal(fileData);

    return decrypted;
}

So speichern Sie eine verschlüsselte Datei auf SD:

File file = new File(Environment.getExternalStorageDirectory() + File.separator + "your_folder_on_sd", "file_name");
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file));
byte[] yourKey = generateKey("password");
byte[] filesBytes = encodeFile(yourKey, yourByteArrayContainigDataToEncrypt);
bos.write(fileBytes);
bos.flush();
bos.close();

So dekodieren Sie eine Datei:

byte[] yourKey = generateKey("password");
byte[] decodedData = decodeFile(yourKey, bytesOfYourFile);

Zum Einlesen einer Datei in ein Byte-Array gibt es einen anderen Ausweg. Ein Beispiel: http://examples.javacodegeeks.com/core-java/io/fileinputstream/read-file-in-byte-array-with-fileinputstream/

ZeroTek
quelle
Ich habe versucht, diese Lösung zu verwenden, aber wenn ich die Datei vom Android-Gerät verschlüssle, kann ich die Datei nicht von meinem Computer entschlüsseln. Es scheint, dass der generierte Schlüssel nicht konsistent ist. Kennen Sie eine Lösung dafür?
Minhaz1
4
Dies ist eine Kopie von Android-Snippets und verwendet dieselbe falsche Methode zum Ableiten des Schlüssels. Verwenden Sie dies und Sie können Ihre Daten verlieren.
Maarten Bodewes
2
@ MaartenBodewes, bitte argumentieren Sie Ihre Aussage, warum ist es Ihrer Meinung nach eine falsche Methode, den Schlüssel abzuleiten? Wie kann es verbessert werden?
Ayaz Alifov
4
Dies verwendet einen Zufallszahlengenerator, um Schlüssel abzuleiten. Dieses RNG muss sich nicht nur auf den Startwert verlassen, und der genaue Algorithmus ist nicht angegeben. Suchen Sie stattdessen nach der korrekten Verwendung von PBKDF2 oder ähnlichem.
Maarten Bodewes
3
Krypto wurde jetzt in Android N - android-developers.googleblog.com/2016/06/…
Jack
0

Sie können Java-Aes-Crypto oder Facebooks Conceal verwenden

Java-Aes-Krypto

Zitat aus dem Repo

Eine einfache Android-Klasse zum Ver- und Entschlüsseln von Zeichenfolgen, um die klassischen Fehler zu vermeiden, unter denen die meisten dieser Klassen leiden.

Facebook verbirgt sich

Zitat aus dem Repo

Conceal bietet einfache Android-APIs für die schnelle Verschlüsselung und Authentifizierung von Daten

user158
quelle