Was sind Best Practices für die Verwendung der AES-Verschlüsselung in Android?

89

Warum ich diese Frage stelle:

Ich weiß, dass es viele Fragen zur AES-Verschlüsselung gab, auch für Android. Und es gibt viele Codefragmente, wenn Sie im Web suchen. Aber auf jeder einzelnen Seite, in jeder Frage zum Stapelüberlauf, finde ich eine andere Implementierung mit großen Unterschieden.

Also habe ich diese Frage erstellt, um eine "Best Practice" zu finden. Ich hoffe, wir können eine Liste der wichtigsten Anforderungen zusammenstellen und eine Implementierung einrichten, die wirklich sicher ist!

Ich habe über Initialisierungsvektoren und Salze gelesen. Nicht alle Implementierungen, die ich gefunden habe, hatten diese Funktionen. Also brauchst du es Erhöht es die Sicherheit erheblich? Wie setzen Sie es um? Sollte der Algorithmus Ausnahmen auslösen, wenn die verschlüsselten Daten nicht entschlüsselt werden können? Oder ist das unsicher und sollte nur eine unlesbare Zeichenfolge zurückgeben? Kann der Algorithmus Bcrypt anstelle von SHA verwenden?

Was ist mit diesen beiden Implementierungen, die ich gefunden habe? Sind sie in Ordnung? Perfekt oder fehlen einige wichtige Dinge? Was davon ist sicher?

Der Algorithmus sollte eine Zeichenfolge und ein "Passwort" zur Verschlüsselung verwenden und die Zeichenfolge dann mit diesem Passwort verschlüsseln. Die Ausgabe sollte wieder eine Zeichenfolge (hex oder base64?) Sein. Natürlich sollte auch eine Entschlüsselung möglich sein.

Was ist die perfekte AES-Implementierung für Android?

Implementierung Nr. 1:

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SecureRandom;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;

public class AdvancedCrypto implements ICrypto {

        public static final String PROVIDER = "BC";
        public static final int SALT_LENGTH = 20;
        public static final int IV_LENGTH = 16;
        public static final int PBE_ITERATION_COUNT = 100;

        private static final String RANDOM_ALGORITHM = "SHA1PRNG";
        private static final String HASH_ALGORITHM = "SHA-512";
        private static final String PBE_ALGORITHM = "PBEWithSHA256And256BitAES-CBC-BC";
        private static final String CIPHER_ALGORITHM = "AES/CBC/PKCS5Padding";
        private static final String SECRET_KEY_ALGORITHM = "AES";

        public String encrypt(SecretKey secret, String cleartext) throws CryptoException {
                try {

                        byte[] iv = generateIv();
                        String ivHex = HexEncoder.toHex(iv);
                        IvParameterSpec ivspec = new IvParameterSpec(iv);

                        Cipher encryptionCipher = Cipher.getInstance(CIPHER_ALGORITHM, PROVIDER);
                        encryptionCipher.init(Cipher.ENCRYPT_MODE, secret, ivspec);
                        byte[] encryptedText = encryptionCipher.doFinal(cleartext.getBytes("UTF-8"));
                        String encryptedHex = HexEncoder.toHex(encryptedText);

                        return ivHex + encryptedHex;

                } catch (Exception e) {
                        throw new CryptoException("Unable to encrypt", e);
                }
        }

        public String decrypt(SecretKey secret, String encrypted) throws CryptoException {
                try {
                        Cipher decryptionCipher = Cipher.getInstance(CIPHER_ALGORITHM, PROVIDER);
                        String ivHex = encrypted.substring(0, IV_LENGTH * 2);
                        String encryptedHex = encrypted.substring(IV_LENGTH * 2);
                        IvParameterSpec ivspec = new IvParameterSpec(HexEncoder.toByte(ivHex));
                        decryptionCipher.init(Cipher.DECRYPT_MODE, secret, ivspec);
                        byte[] decryptedText = decryptionCipher.doFinal(HexEncoder.toByte(encryptedHex));
                        String decrypted = new String(decryptedText, "UTF-8");
                        return decrypted;
                } catch (Exception e) {
                        throw new CryptoException("Unable to decrypt", e);
                }
        }

        public SecretKey getSecretKey(String password, String salt) throws CryptoException {
                try {
                        PBEKeySpec pbeKeySpec = new PBEKeySpec(password.toCharArray(), HexEncoder.toByte(salt), PBE_ITERATION_COUNT, 256);
                        SecretKeyFactory factory = SecretKeyFactory.getInstance(PBE_ALGORITHM, PROVIDER);
                        SecretKey tmp = factory.generateSecret(pbeKeySpec);
                        SecretKey secret = new SecretKeySpec(tmp.getEncoded(), SECRET_KEY_ALGORITHM);
                        return secret;
                } catch (Exception e) {
                        throw new CryptoException("Unable to get secret key", e);
                }
        }

        public String getHash(String password, String salt) throws CryptoException {
                try {
                        String input = password + salt;
                        MessageDigest md = MessageDigest.getInstance(HASH_ALGORITHM, PROVIDER);
                        byte[] out = md.digest(input.getBytes("UTF-8"));
                        return HexEncoder.toHex(out);
                } catch (Exception e) {
                        throw new CryptoException("Unable to get hash", e);
                }
        }

        public String generateSalt() throws CryptoException {
                try {
                        SecureRandom random = SecureRandom.getInstance(RANDOM_ALGORITHM);
                        byte[] salt = new byte[SALT_LENGTH];
                        random.nextBytes(salt);
                        String saltHex = HexEncoder.toHex(salt);
                        return saltHex;
                } catch (Exception e) {
                        throw new CryptoException("Unable to generate salt", e);
                }
        }

        private byte[] generateIv() throws NoSuchAlgorithmException, NoSuchProviderException {
                SecureRandom random = SecureRandom.getInstance(RANDOM_ALGORITHM);
                byte[] iv = new byte[IV_LENGTH];
                random.nextBytes(iv);
                return iv;
        }

}

Quelle: http://pocket-for-android.1047292.n5.nabble.com/Encryption-method-and-reading-the-Dropbox-backup-td4344194.html

Implementierung Nr. 2:

import java.security.SecureRandom;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

/**
 * Usage:
 * <pre>
 * String crypto = SimpleCrypto.encrypt(masterpassword, cleartext)
 * ...
 * String cleartext = SimpleCrypto.decrypt(masterpassword, crypto)
 * </pre>
 * @author ferenc.hechler
 */
public class SimpleCrypto {

    public static String encrypt(String seed, String cleartext) throws Exception {
        byte[] rawKey = getRawKey(seed.getBytes());
        byte[] result = encrypt(rawKey, cleartext.getBytes());
        return toHex(result);
    }

    public static String decrypt(String seed, String encrypted) throws Exception {
        byte[] rawKey = getRawKey(seed.getBytes());
        byte[] enc = toByte(encrypted);
        byte[] result = decrypt(rawKey, enc);
        return new String(result);
    }

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

    private static byte[] decrypt(byte[] raw, byte[] encrypted) throws Exception {
        SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
        Cipher cipher = Cipher.getInstance("AES");
        cipher.init(Cipher.DECRYPT_MODE, skeySpec);
        byte[] decrypted = cipher.doFinal(encrypted);
        return decrypted;
    }

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

}

Quelle: http://www.tutorials-android.com/learn/How_to_encrypt_and_decrypt_strings.rhtml

krächzen
quelle
Ich versuche, die Lösung 1 zu implementieren, aber es wurden einige Klassen benötigt. Haben Sie den vollständigen Quellcode?
Albanx
1
Nein, habe ich nicht, sorry. Aber ich habe es zum Laufen gebracht, indem ich es einfach gelöscht implements ICryptound geändert throws CryptoExceptionhabe throws Exceptionund so weiter. Sie brauchen diese Klassen also nicht mehr.
Caw
Aber auch die HexEncoder-Klasse fehlt? Wo kann ich es finden?
Albanx
Ich denke, HexEncoder ist Teil der BouncyCastle-Bibliothek. Sie können es einfach herunterladen. Oder Sie können in Java nach "byte [] to hex" googeln und umgekehrt.
Caw
Danke Marco. Ich stelle jedoch fest, dass es in der ersten Implementierung drei Methoden getSecretKeygibt getHash, generateSaltdie nicht verwendet werden. Vielleicht irre ich mich, aber wie könnte diese Klasse verwendet werden, um eine Zeichenfolge in der Praxis zu verschlüsseln?
Albanx

Antworten:

37

Keine der von Ihnen in Ihrer Frage angegebenen Implementierungen ist völlig korrekt, und keine der von Ihnen angegebenen Implementierungen sollte unverändert verwendet werden. Im Folgenden werde ich Aspekte der kennwortbasierten Verschlüsselung in Android diskutieren.

Schlüssel und Hashes

Ich werde anfangen, das passwortbasierte System mit Salzen zu diskutieren. Das Salz ist eine zufällig erzeugte Zahl. Es wird nicht "abgeleitet". Implementierung 1 enthält eine generateSalt()Methode, die eine kryptografisch starke Zufallszahl generiert. Da das Salz für die Sicherheit wichtig ist, sollte es nach seiner Erzeugung geheim gehalten werden, obwohl es nur einmal erzeugt werden muss. Wenn es sich um eine Website handelt, ist es relativ einfach, das Salz geheim zu halten, aber für installierte Anwendungen (für Desktop- und Mobilgeräte) ist dies viel schwieriger.

Die Methode getHash()gibt einen Hash des angegebenen Kennworts und Salt zurück, der zu einer einzelnen Zeichenfolge verkettet ist. Der verwendete Algorithmus ist SHA-512, der einen 512-Bit-Hash zurückgibt. Diese Methode gibt einen Hash zurück, der zum Überprüfen der Integrität einer Zeichenfolge nützlich ist. Sie kann daher auch verwendet werden, indem getHash()nur ein Kennwort oder nur ein Salt aufgerufen wird, da beide Parameter einfach verkettet werden. Da diese Methode im kennwortbasierten Verschlüsselungssystem nicht verwendet wird, werde ich nicht weiter darauf eingehen.

Die Methode getSecretKey()leitet einen Schlüssel aus einem charArray des Kennworts und einem hexadezimal codierten Salt ab, wie von zurückgegeben generateSalt(). Der verwendete Algorithmus ist PBKDF1 (glaube ich) von PKCS5 mit SHA-256 als Hash-Funktion und gibt einen 256-Bit-Schlüssel zurück. getSecretKey()generiert einen Schlüssel durch wiederholtes Generieren von Hashes des Kennworts, des Salt und eines Zählers (bis zu der in PBE_ITERATION_COUNT, hier 100 angegebenen Iterationszahl ), um die Zeit zu erhöhen, die zum Ausführen eines Brute-Force-Angriffs erforderlich ist. Die Länge des Salzes sollte mindestens so lang sein wie der zu erzeugende Schlüssel, in diesem Fall mindestens 256 Bit. Die Iterationszahl sollte so lange wie möglich eingestellt werden, ohne eine unangemessene Verzögerung zu verursachen. Weitere Informationen zu Salzen und Iterationszahlen bei der Schlüsselableitung finden Sie in Abschnitt 4 in RFC2898 .

Die Implementierung in Javas PBE ist jedoch fehlerhaft, wenn das Kennwort Unicode-Zeichen enthält, dh solche, für deren Darstellung mehr als 8 Bit erforderlich sind. Wie in angegeben PBEKeySpec, "betrachtet der in PKCS # 5 definierte PBE-Mechanismus nur die niederwertigen 8 Bits jedes Zeichens". Um dieses Problem zu umgehen, können Sie versuchen, eine Hex-Zeichenfolge (die nur 8-Bit-Zeichen enthält) aller 16-Bit-Zeichen im Kennwort zu generieren, bevor Sie sie an übergeben PBEKeySpec. Beispielsweise wird "ABC" zu "004100420043". Beachten Sie auch, dass PBEKeySpec "das Kennwort als char-Array anfordert, damit es nach Abschluss [mit clearPassword()] überschrieben werden kann". (Bezüglich des "Schutzes von Zeichenfolgen im Speicher" siehe diese Frage .) Ich sehe jedoch keine Probleme.

Verschlüsselung

Sobald ein Schlüssel generiert wurde, können wir ihn zum Ver- und Entschlüsseln von Text verwenden.

In Implementierung 1 wird als Verschlüsselungsalgorithmus AES/CBC/PKCS5PaddingAES im CBC-Verschlüsselungsmodus (Cipher Block Chaining) verwendet, wobei die Auffüllung in PKCS # 5 definiert ist. (Andere AES-Verschlüsselungsmodi umfassen den Zählermodus (CTR), den elektronischen Codebuchmodus (EZB) und den Galois-Zählermodus (GCM). Eine weitere Frage zum Stapelüberlauf enthält Antworten, in denen die verschiedenen AES-Verschlüsselungsmodi und die empfohlenen zu erläutern sind. Beachten Sie auch, dass es mehrere Angriffe auf die Verschlüsselung im CBC-Modus gibt, von denen einige in RFC 7457 erwähnt werden.)

Beachten Sie, dass Sie einen Verschlüsselungsmodus verwenden sollten, der auch die verschlüsselten Daten auf Integrität überprüft (z. B. authentifizierte Verschlüsselung mit zugehörigen Daten , AEAD, beschrieben in RFC 5116). Bietet AES/CBC/PKCS5Paddingjedoch keine Integritätsprüfung, sodass dies allein nicht empfohlen wird . Für AEAD-Zwecke wird die Verwendung eines Geheimnisses empfohlen, das mindestens doppelt so lang ist wie ein normaler Verschlüsselungsschlüssel, um verwandte Schlüsselangriffe zu vermeiden: Die erste Hälfte dient als Verschlüsselungsschlüssel und die zweite Hälfte als Schlüssel für die Integritätsprüfung. (In diesem Fall wird aus einem Kennwort und Salt ein einzelnes Geheimnis generiert und dieses Geheimnis in zwei Teile geteilt.)

Java-Implementierung

Die verschiedenen Funktionen in Implementierung 1 verwenden einen bestimmten Anbieter, nämlich "BC", für seine Algorithmen. Im Allgemeinen wird jedoch nicht empfohlen, bestimmte Anbieter anzufordern, da nicht alle Anbieter auf allen Java-Implementierungen verfügbar sind, sei es aus Mangel an Unterstützung, um Code-Duplikationen zu vermeiden oder aus anderen Gründen. Dieser Rat ist seit der Veröffentlichung der Android P-Vorschau Anfang 2018 besonders wichtig geworden, da einige Funktionen des "BC" -Anbieters dort veraltet sind - siehe Artikel "Änderungen der Kryptografie in Android P" im Android Developers Blog. Siehe auch die Einführung in Oracle-Anbieter .

So PROVIDERsollte nicht existieren und die Zeichenfolge -BCsollte entfernt werden PBE_ALGORITHM. Implementierung 2 ist in dieser Hinsicht korrekt.

Es ist für eine Methode unangemessen, alle Ausnahmen abzufangen, sondern nur die Ausnahmen zu behandeln, die sie kann. Die in Ihrer Frage angegebenen Implementierungen können eine Vielzahl von geprüften Ausnahmen auslösen. Eine Methode kann wählen, ob nur diese geprüften Ausnahmen mit CryptoException umbrochen werden sollen, oder diese geprüften Ausnahmen in der throwsKlausel angeben . Der Einfachheit halber kann es hier angebracht sein, die ursprüngliche Ausnahme mit CryptoException zu verpacken, da die Klassen möglicherweise viele geprüfte Ausnahmen auslösen können.

SecureRandom in Android

Wie im Artikel "Some SecureRandom Thoughts" im Android Developers Blog beschrieben, weist die Implementierung java.security.SecureRandomin Android-Versionen vor 2013 einen Fehler auf, der die Stärke der bereitgestellten Zufallszahlen verringert. Dieser Fehler kann wie in diesem Artikel beschrieben behoben werden.

Peter O.
quelle
Diese doppelte Geheimgenerierung ist meiner Meinung nach etwas verschwenderisch. Sie können das generierte Geheimnis genauso einfach in zwei Teile teilen oder - wenn nicht genügend Bits verfügbar sind - einen Zähler (1 für den ersten Schlüssel, 2 für den zweiten Schlüssel) zum hinzufügen geheim und führe einen einzelnen Hash durch. Es ist nicht erforderlich, alle Iterationen zweimal durchzuführen.
Maarten Bodewes
Vielen Dank für die Informationen zu HMAC und dem Salz. Ich werde HMAC diesmal nicht verwenden, aber später könnte es sehr nützlich sein. Und im Allgemeinen ist dies zweifellos eine gute Sache.
Caw
Vielen Dank für all die Änderungen und diese (jetzt) ​​wunderbare Einführung in die AES-Verschlüsselung in Java!
Caw
1
Es sollte. getInstancehat eine Überladung, die nur den Namen des Algorithmus nimmt. Beispiel: Cipher.getInstance () In der Java-Implementierung sind möglicherweise mehrere Anbieter, einschließlich Bouncy Castle, registriert. Diese Art der Überlastung durchsucht die Liste der Anbieter nach einem von ihnen, der den angegebenen Algorithmus implementiert. Sie sollten es versuchen und sehen.
Peter O.
1
Ja, es werden die Anbieter in der von Security.getProviders () angegebenen Reihenfolge durchsucht - obwohl jetzt auch geprüft wird, ob der Schlüssel von diesem Anbieter während des Aufrufs von init () akzeptiert wird, wodurch eine hardwareunterstützte Verschlüsselung ermöglicht wird. Weitere Details finden Sie hier: docs.oracle.com/javase/6/docs/technotes/guides/security/crypto/… .
Maarten Bodewes
18

# 2 sollte niemals verwendet werden, da nur "AES" (was bedeutet, dass die Verschlüsselung im EZB-Modus für Text ein großes Nein-Nein ist) für die Verschlüsselung verwendet wird. Ich werde nur über # 1 sprechen.

Die erste Implementierung scheint den Best Practices für die Verschlüsselung zu entsprechen. Die Konstanten sind im Allgemeinen in Ordnung, obwohl sowohl die Salzgröße als auch die Anzahl der Iterationen zur Durchführung von PBE eher kurz sind. Darüber hinaus scheint es für AES-256 zu sein, da die PBE-Schlüsselgenerierung 256 als fest codierten Wert verwendet (eine Schande nach all diesen Konstanten). Es verwendet CBC und PKCS5Padding, was zumindest Ihren Erwartungen entspricht.

Völlig fehlt ein Authentifizierungs- / Integritätsschutz, sodass ein Angreifer den Chiffretext ändern kann. Dies bedeutet, dass Padding-Orakel-Angriffe in einem Client / Server-Modell möglich sind. Dies bedeutet auch, dass ein Angreifer versuchen kann, die verschlüsselten Daten zu ändern. Dies führt wahrscheinlich irgendwo zu einem Fehler, da das Auffüllen oder der Inhalt von der Anwendung nicht akzeptiert wird. Dies ist jedoch keine Situation, in der Sie sich befinden möchten.

Die Ausnahmebehandlung und die Eingabevalidierung könnten verbessert werden. Das Abfangen von Ausnahmen ist in meinem Buch immer falsch. Außerdem implementiert die Klasse ICrypt, was ich nicht kenne. Ich weiß, dass es etwas seltsam ist, nur Methoden ohne Nebenwirkungen in einer Klasse zu haben. Normalerweise würden Sie diese statisch machen. Es gibt keine Pufferung von Verschlüsselungsinstanzen usw., sodass jedes erforderliche Objekt ad nauseum erstellt wird. Sie können ICrypto jedoch sicher aus der Definition entfernen. In diesem Fall können Sie den Code auch in statische Methoden umgestalten (oder ihn nach Ihrer Wahl objektorientierter umschreiben).

Das Problem ist, dass jeder Wrapper immer Annahmen über den Anwendungsfall macht. Zu sagen, dass ein Wrapper richtig oder falsch ist, ist daher eine Koje. Deshalb versuche ich immer zu vermeiden, Wrapper-Klassen zu generieren. Aber zumindest scheint es nicht explizit falsch zu sein.

Maarten Bodewes
quelle
Vielen Dank für diese ausführliche Antwort! Ich weiß, dass es eine Schande ist, aber ich kannte den Abschnitt zur Codeüberprüfung noch nicht: D Danke für diesen Hinweis, ich werde das überprüfen. Aber diese Frage passt meiner Meinung nach auch hierher, da ich nicht nur eine Überprüfung dieser Codefragmente möchte. Stattdessen möchte ich Sie alle fragen, welche Aspekte bei der Implementierung der AES-Verschlüsselung in Android wichtig sind. Und Sie haben wieder Recht, dieses Code-Snippet ist für AES-256. Sie würden also sagen, dass dies im Allgemeinen eine sichere Implementierung von AES-256 ist? Der Anwendungsfall ist, dass ich nur Textinformationen sicher in einer Datenbank speichern möchte.
Caw
1
Es sieht gut aus, aber die Idee, keine Integritätsprüfungen und Authentifizierung durchzuführen, würde mich stören. Wenn Sie genügend Speicherplatz haben, würde ich ernsthaft in Betracht ziehen, einen HMAC über dem Chiffretext hinzuzufügen. Das heißt, da Sie wahrscheinlich versuchen, einfach die Vertraulichkeit zu erhöhen, würde ich dies als großes Plus betrachten, aber nicht direkt als Anforderung.
Maarten Bodewes
Aber wenn nur beabsichtigt wird, dass andere keinen Zugriff auf die verschlüsselten Informationen haben sollen, brauche ich keinen HMAC, oder? Wenn sie den Chiffretext ändern und ein "falsches" Ergebnis der Entschlüsselung erzwingen, gibt es kein wirkliches Problem, oder?
Caw
Wenn das nicht in Ihrem Risikoszenario ist, ist das in Ordnung. Wenn sie nach Änderung des Chiffretextes (ein Padding-Orakel-Angriff) eine wiederholte Entschlüsselung durch das System auslösen können, können sie die Daten entschlüsseln, ohne jemals den Schlüssel zu kennen. Sie können dies nicht tun, wenn sie einfach die Daten auf einem System abrufen, das nicht über den Schlüssel verfügt. Deshalb ist es immer empfehlenswert, einen HMAC hinzuzufügen. Persönlich würde ich ein System mit AES-128 und HMAC als sicherer als AES-256 ohne betrachten - aber wie gesagt, wahrscheinlich nicht erforderlich.
Maarten Bodewes
1
Warum nicht AES im Galois / Counter-Modus (AES-GCM) verwenden, wenn Sie Integrität wünschen?
Kimvais
1

Sie haben eine ziemlich interessante Frage gestellt. Wie bei allen Algorithmen ist der Chiffrierschlüssel die "geheime Sauce", denn sobald dies der Öffentlichkeit bekannt ist, ist auch alles andere vorhanden. Sie suchen also nach Möglichkeiten für dieses Dokument von Google

Sicherheit

Neben Google In-App Billing gibt es auch aufschlussreiche Gedanken zur Sicherheit

billing_best_practices

the100rabh
quelle
Danke für diese Links! Was genau meinst du mit "Wenn der Chiffrierschlüssel heraus ist, ist auch alles andere raus"?
Caw
Was ich meine ist, dass der Verschlüsselungsschlüssel sicher sein muss. Wenn jemand darauf zugreifen kann, sind Ihre verschlüsselten Daten so gut wie Klartext. Bitte stimmen Sie zu, wenn Sie meine Antwort bis zu einem gewissen Grad hilfreich fanden :-)
the100rabh
0

Verwenden Sie die BouncyCastle Lightweight-API. Es bietet 256 AES mit PBE und Salz.
Hier Beispielcode, der Dateien verschlüsseln / entschlüsseln kann.

public void encrypt(InputStream fin, OutputStream fout, String password) {
    try {
        PKCS12ParametersGenerator pGen = new PKCS12ParametersGenerator(new SHA256Digest());
        char[] passwordChars = password.toCharArray();
        final byte[] pkcs12PasswordBytes = PBEParametersGenerator.PKCS12PasswordToBytes(passwordChars);
        pGen.init(pkcs12PasswordBytes, salt.getBytes(), iterationCount);
        CBCBlockCipher aesCBC = new CBCBlockCipher(new AESEngine());
        ParametersWithIV aesCBCParams = (ParametersWithIV) pGen.generateDerivedParameters(256, 128);
        aesCBC.init(true, aesCBCParams);
        PaddedBufferedBlockCipher aesCipher = new PaddedBufferedBlockCipher(aesCBC, new PKCS7Padding());
        aesCipher.init(true, aesCBCParams);

        // Read in the decrypted bytes and write the cleartext to out
        int numRead = 0;
        while ((numRead = fin.read(buf)) >= 0) {
            if (numRead == 1024) {
                byte[] plainTemp = new byte[aesCipher.getUpdateOutputSize(numRead)];
                int offset = aesCipher.processBytes(buf, 0, numRead, plainTemp, 0);
                final byte[] plain = new byte[offset];
                System.arraycopy(plainTemp, 0, plain, 0, plain.length);
                fout.write(plain, 0, plain.length);
            } else {
                byte[] plainTemp = new byte[aesCipher.getOutputSize(numRead)];
                int offset = aesCipher.processBytes(buf, 0, numRead, plainTemp, 0);
                int last = aesCipher.doFinal(plainTemp, offset);
                final byte[] plain = new byte[offset + last];
                System.arraycopy(plainTemp, 0, plain, 0, plain.length);
                fout.write(plain, 0, plain.length);
            }
        }
        fout.close();
        fin.close();
    } catch (Exception e) {
        e.printStackTrace();
    }

}

public void decrypt(InputStream fin, OutputStream fout, String password) {
    try {
        PKCS12ParametersGenerator pGen = new PKCS12ParametersGenerator(new SHA256Digest());
        char[] passwordChars = password.toCharArray();
        final byte[] pkcs12PasswordBytes = PBEParametersGenerator.PKCS12PasswordToBytes(passwordChars);
        pGen.init(pkcs12PasswordBytes, salt.getBytes(), iterationCount);
        CBCBlockCipher aesCBC = new CBCBlockCipher(new AESEngine());
        ParametersWithIV aesCBCParams = (ParametersWithIV) pGen.generateDerivedParameters(256, 128);
        aesCBC.init(false, aesCBCParams);
        PaddedBufferedBlockCipher aesCipher = new PaddedBufferedBlockCipher(aesCBC, new PKCS7Padding());
        aesCipher.init(false, aesCBCParams);

        // Read in the decrypted bytes and write the cleartext to out
        int numRead = 0;
        while ((numRead = fin.read(buf)) >= 0) {
            if (numRead == 1024) {
                byte[] plainTemp = new byte[aesCipher.getUpdateOutputSize(numRead)];
                int offset = aesCipher.processBytes(buf, 0, numRead, plainTemp, 0);
                // int last = aesCipher.doFinal(plainTemp, offset);
                final byte[] plain = new byte[offset];
                System.arraycopy(plainTemp, 0, plain, 0, plain.length);
                fout.write(plain, 0, plain.length);
            } else {
                byte[] plainTemp = new byte[aesCipher.getOutputSize(numRead)];
                int offset = aesCipher.processBytes(buf, 0, numRead, plainTemp, 0);
                int last = aesCipher.doFinal(plainTemp, offset);
                final byte[] plain = new byte[offset + last];
                System.arraycopy(plainTemp, 0, plain, 0, plain.length);
                fout.write(plain, 0, plain.length);
            }
        }
        fout.close();
        fin.close();
    } catch (Exception e) {
        e.printStackTrace();
    }
}
Kelheor
quelle
Danke dir! Dies ist wahrscheinlich eine gute und sichere Lösung, aber ich möchte keine Software von Drittanbietern verwenden. Ich bin sicher, dass es möglich sein muss, AES auf sichere Weise selbst zu implementieren.
Caw
2
Hängt davon ab, ob Sie Schutz vor Seitenkanalangriffen einschließen möchten. Im Allgemeinen sollten Sie davon ausgehen, dass es hübsch ist unsicher ist , kryptografische Algorithmen selbst zu implementieren. Da AES CBC in den Java-Laufzeitbibliotheken von Oracle verfügbar ist, ist es wahrscheinlich am besten, diese zu verwenden und die Bouncy Castle-Bibliotheken zu verwenden, wenn ein Algorithmus nicht verfügbar ist.
Maarten Bodewes
Es fehlt die Definition von buf(I. hoffe wirklich, dass es kein staticFeld ist). Es sieht auch nach beidem aus encrypt()und decrypt()kann den letzten Block nicht korrekt verarbeiten, wenn die Eingabe ein Vielfaches von 1024 Bytes ist.
tc.