Quelles sont les meilleures pratiques pour utiliser le chiffrement AES sous Android?


89

Pourquoi je pose cette question:

Je sais qu'il y a eu beaucoup de questions sur le cryptage AES, même pour Android. Et il existe de nombreux extraits de code si vous recherchez sur le Web. Mais sur chaque page, dans chaque question de Stack Overflow, je trouve une autre implémentation avec des différences majeures.

J'ai donc créé cette question pour trouver une "meilleure pratique". J'espère que nous pourrons rassembler une liste des exigences les plus importantes et mettre en place une implémentation vraiment sécurisée!

J'ai lu sur les vecteurs d'initialisation et les sels. Toutes les implémentations que j'ai trouvées n'avaient pas ces fonctionnalités. Alors en avez-vous besoin? Augmente-t-il beaucoup la sécurité? Comment le mettez-vous en œuvre? L'algorithme devrait-il déclencher des exceptions si les données chiffrées ne peuvent pas être déchiffrées? Ou est-ce non sécurisé et il devrait simplement renvoyer une chaîne illisible? L'algorithme peut-il utiliser Bcrypt au lieu de SHA?

Qu'en est-il de ces deux implémentations que j'ai trouvées? Est-ce qu'ils vont bien? Parfait ou il manque des choses importantes? Qu'est-ce qui est sécurisé?

L'algorithme doit prendre une chaîne et un «mot de passe» pour le cryptage, puis crypter la chaîne avec ce mot de passe. La sortie doit être à nouveau une chaîne (hexadécimale ou base64?). Le décryptage devrait également être possible, bien sûr.

Quelle est l'implémentation AES parfaite pour Android?

Mise en œuvre n ° 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;
        }

}

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

Mise en œuvre n ° 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));
    }

}

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


J'essaye d'implémenter la solution 1 mais il fallait quelques classes. avez-vous le code source complet?
albanx

1
Non, je n'ai pas, désolé. Mais je l'ai fait fonctionner simplement en supprimant implements ICryptoet en changeant throws CryptoExceptionen throws Exceptionet ainsi de suite. Vous n'aurez donc plus besoin de ces cours.
caw

Mais aussi la classe HexEncoder est manquante? Où puis-je le trouver?
albanx

HexEncoder fait partie de la bibliothèque BouncyCastle, je pense. Vous pouvez simplement le télécharger. Ou vous pouvez rechercher sur Google "byte [] to hex" et inversement en Java.
caw

Merci Marco. Mais je remarque qu'il ya 3 méthodes getSecretKey, getHash, generateSaltdans la première mise en œuvre qui ne sont pas utilisés. Peut-être que je me trompe, mais comment cette classe pourrait-elle être utilisée pour crypter une chaîne en pratique?
albanx

Réponses:


37

Aucune des implémentations que vous donnez dans votre question n'est entièrement correcte, et aucune des implémentations que vous donnez ne doit être utilisée telle quelle. Dans ce qui suit, je discuterai des aspects du cryptage par mot de passe sous Android.

Clés et hachages

Je vais commencer à discuter du système basé sur les mots de passe avec des sels. Le sel est un nombre généré aléatoirement. Ce n'est pas «déduit». La mise en œuvre 1 comprend une generateSalt()méthode qui génère un nombre aléatoire cryptographiquement fort. Parce que le sel est important pour la sécurité, il doit être gardé secret une fois qu'il est généré, bien qu'il ne doive être généré qu'une seule fois. S'il s'agit d'un site Web, il est relativement facile de garder le secret du sel, mais pour les applications installées (pour les ordinateurs de bureau et les appareils mobiles), ce sera beaucoup plus difficile.

La méthode getHash()renvoie un hachage du mot de passe et du sel donnés, concaténés en une seule chaîne. L'algorithme utilisé est SHA-512, qui renvoie un hachage de 512 bits. Cette méthode renvoie un hachage utile pour vérifier l'intégrité d'une chaîne, elle peut donc aussi bien être utilisée en appelant getHash()avec juste un mot de passe ou juste un sel, car elle concatène simplement les deux paramètres. Étant donné que cette méthode ne sera pas utilisée dans le système de cryptage basé sur un mot de passe, je n'en parlerai pas davantage.

La méthode getSecretKey()dérive une clé d'un chartableau du mot de passe et d'un sel codé en hexadécimal, comme renvoyé de generateSalt(). L'algorithme utilisé est PBKDF1 (je pense) de PKCS5 avec SHA-256 comme fonction de hachage, et renvoie une clé de 256 bits. getSecretKey()génère une clé en générant à plusieurs reprises des hachages du mot de passe, du sel et d'un compteur (jusqu'au nombre d'itérations donné dans PBE_ITERATION_COUNT, ici 100) afin d'augmenter le temps nécessaire pour monter une attaque par force brute. La longueur du sel doit être au moins aussi longue que la clé générée, dans ce cas, au moins 256 bits. Le nombre d'itérations doit être défini aussi longtemps que possible sans provoquer de retard déraisonnable. Pour plus d'informations sur les sels et le nombre d'itérations dans la dérivation de clé, voir la section 4 de la RFC2898 .

L'implémentation dans le PBE de Java, cependant, est imparfaite si le mot de passe contient des caractères Unicode, c'est-à-dire ceux qui nécessitent plus de 8 bits pour être représentés. Comme indiqué dans PBEKeySpec, "le mécanisme PBE défini dans PKCS # 5 ne regarde que les 8 bits de poids faible de chaque caractère". Pour contourner ce problème, vous pouvez essayer de générer une chaîne hexadécimale (qui contiendra uniquement des caractères 8 bits) de tous les caractères 16 bits du mot de passe avant de le transmettre PBEKeySpec. Par exemple, "ABC" devient "004100420043". Notez également que PBEKeySpec "demande le mot de passe sous forme de tableau de caractères, afin qu'il puisse être écrasé [avec clearPassword()] une fois terminé". (En ce qui concerne la "protection des chaînes en mémoire", voir cette question .) Je ne vois cependant aucun problème,

Chiffrement

Une fois qu'une clé est générée, nous pouvons l'utiliser pour crypter et décrypter du texte.

Dans l'implémentation 1, l'algorithme de chiffrement utilisé est AES/CBC/PKCS5Padding, c'est-à-dire AES en mode de chiffrement Cipher Block Chaining (CBC), avec un remplissage défini dans PKCS # 5. (Les autres modes de chiffrement AES incluent le mode compteur (CTR), le mode livre de codes électronique (ECB) et le mode compteur Galois (GCM). Une autre question sur Stack Overflow contient des réponses qui discutent en détail des différents modes de chiffrement AES et de ceux recommandés à utiliser. Sachez également qu'il existe plusieurs attaques contre le cryptage en mode CBC, dont certaines sont mentionnées dans la RFC 7457.)

Notez que vous devez utiliser un mode de cryptage qui vérifie également l'intégrité des données cryptées (par exemple, cryptage authentifié avec les données associées , AEAD, décrit dans la RFC 5116). Cependant, AES/CBC/PKCS5Paddingne fournit pas de vérification d'intégrité, donc elle seule n'est pas recommandée . Pour les besoins de l'AEAD, il est recommandé d'utiliser un secret qui est au moins deux fois plus long qu'une clé de cryptage normale, pour éviter les attaques de clé associées: la première moitié sert de clé de cryptage et la seconde moitié sert de clé pour le contrôle d'intégrité. (Autrement dit, dans ce cas, générez un seul secret à partir d'un mot de passe et d'un sel, et divisez ce secret en deux.)

Implémentation Java

Les différentes fonctions de l'implémentation 1 utilisent un fournisseur spécifique, à savoir "BC", pour ses algorithmes. En général, cependant, il n'est pas recommandé de demander des fournisseurs spécifiques, car tous les fournisseurs ne sont pas disponibles sur toutes les implémentations Java, que ce soit par manque de support, pour éviter la duplication de code ou pour d'autres raisons. Ce conseil est devenu particulièrement important depuis la sortie de l'aperçu d'Android P début 2018, car certaines fonctionnalités du fournisseur «BC» y sont devenues obsolètes - voir l'article «Changements de cryptographie dans Android P» dans le blog des développeurs Android. Consultez également l' introduction aux fournisseurs Oracle .

Ainsi, PROVIDERne doit pas exister et la chaîne -BCdoit être supprimée PBE_ALGORITHM. La mise en œuvre 2 est correcte à cet égard.

Il est inapproprié pour une méthode d'intercepter toutes les exceptions, mais plutôt de ne gérer que les exceptions qu'elle peut. Les implémentations données dans votre question peuvent générer diverses exceptions vérifiées. Une méthode peut choisir d'encapsuler uniquement les exceptions vérifiées avec CryptoException ou de spécifier ces exceptions vérifiées dans la throwsclause. Pour plus de commodité, encapsuler l'exception d'origine avec CryptoException peut être approprié ici, car il existe potentiellement de nombreuses exceptions vérifiées que les classes peuvent lever.

SecureRandom sous Android

Comme détaillé dans l'article «Quelques réflexions sur le hasard sécurisé», dans le blog des développeurs Android, la mise java.security.SecureRandomen œuvre de dans les versions d'Android avant 2013 présente un défaut qui réduit la force des nombres aléatoires qu'elle délivre. Cette faille peut être atténuée comme décrit dans cet article.


Cette double génération de secret est un peu inutile à mon avis, vous pouvez aussi facilement diviser le secret généré en deux, ou - si pas assez de bits sont disponibles - ajouter un compteur (1 pour la première clé, 2 pour la deuxième clé) au secret et effectuez un seul hachage. Pas besoin d'effectuer toutes les itérations deux fois.
Maarten Bodewes

Merci pour les informations sur HMAC et le sel. Je n'utiliserai pas HMAC cette fois, mais plus tard, cela pourrait être très utile. Et en général, c'est une bonne chose, sans aucun doute.
caw

Merci beaucoup pour toutes les modifications et cette (maintenant) merveilleuse introduction au cryptage AES en Java!
caw

1
Cela devrait. getInstancea une surcharge qui ne prend que le nom de l'algorithme. Exemple: Cipher.getInstance () Plusieurs fournisseurs, y compris Bouncy Castle, peuvent être enregistrés dans l'implémentation Java et ce type de surcharge recherche dans la liste des fournisseurs l'un d'entre eux qui implémente l'algorithme donné. Vous devriez l'essayer et voir.
Peter O.

1
Oui, il recherchera les fournisseurs dans l'ordre donné par Security.getProviders () - bien qu'il vérifie maintenant également si la clé est acceptée par ce fournisseur lors de l'appel init () permettant un cryptage assisté par matériel. Plus de détails ici: docs.oracle.com/javase/6/docs/technotes/guides/security/crypto/… .
Maarten Bodewes

18

# 2 ne doit jamais être utilisé car il utilise uniquement "AES" (ce qui signifie un cryptage en mode ECB sur du texte, un grand non-non) pour le chiffrement. Je vais juste parler du n ° 1.

La première implémentation semble adhérer aux meilleures pratiques de chiffrement. Les constantes sont généralement correctes, bien que la taille du sel et le nombre d'itérations pour effectuer PBE soient sur le côté court. De plus, cela semble être pour AES-256 puisque la génération de clé PBE utilise 256 comme valeur codée en dur (dommage après toutes ces constantes). Il utilise CBC et PKCS5Padding, ce qui est au moins ce à quoi vous vous attendez.

Il manque complètement toute protection d'authentification / d'intégrité, de sorte qu'un attaquant peut modifier le texte chiffré. Cela signifie que les attaques d'oracle de remplissage sont possibles dans un modèle client / serveur. Cela signifie également qu'un attaquant peut essayer de modifier les données chiffrées. Cela entraînera probablement une erreur quelque part car le remplissage ou le contenu n'est pas accepté par l'application, mais ce n'est pas une situation dans laquelle vous souhaitez vous trouver.

La gestion des exceptions et la validation des entrées pourraient être améliorées, la capture d'exception est toujours erronée dans mon livre. De plus, la classe implémente ICrypt, ce que je ne sais pas. Je sais que n'avoir que des méthodes sans effets secondaires dans une classe est un peu bizarre. Normalement, vous les rendriez statiques. Il n'y a pas de mise en mémoire tampon des instances de Cipher, etc., donc chaque objet requis est créé ad nauseum. Cependant, vous pouvez supprimer ICrypto de la définition en toute sécurité, dans ce cas, vous pouvez également refactoriser le code en méthodes statiques (ou le réécrire pour qu'il soit plus orienté objet, votre choix).

Le problème est que tout wrapper émet toujours des hypothèses sur le cas d'utilisation. Dire qu'un emballage a raison ou tort est donc superposé. C'est pourquoi j'essaie toujours d'éviter de générer des classes wrapper. Mais au moins, cela ne semble pas explicitement faux.


Merci beaucoup pour cette réponse détaillée! Je sais que c'est dommage mais je ne connaissais pas encore la section de révision du code: D Merci pour cet indice, je vais vérifier cela. Mais cette question convient également à mon avis, car je ne veux pas seulement un examen de ces extraits de code. Au lieu de cela, je veux vous demander tous quels aspects sont importants lors de la mise en œuvre du cryptage AES dans Android. Et vous avez encore raison, cet extrait de code est pour AES-256. Donc, vous diriez qu'il s'agit d'une implémentation générale sûre d'AES-256? Le cas d'utilisation est que je souhaite simplement stocker en toute sécurité des informations textuelles dans une base de données.
caw

1
Cela a l'air bien, mais l'idée de ne pas avoir de contrôles d'intégrité et d'authentification me dérangerait. Si vous avez suffisamment d'espace, j'envisagerais sérieusement d'ajouter un HMAC sur le texte chiffré. Cela dit, comme vous essayez probablement simplement d'ajouter la confidentialité, je considérerais que c'est un gros plus, mais pas directement une exigence.
Maarten Bodewes

Mais si l'intention est seulement que d'autres ne devraient pas avoir accès aux informations cryptées, je n'ai pas besoin d'un HMAC, non? S'ils changent le texte chiffré et imposent un résultat "faux" de déchiffrement, il n'y a pas de réel problème, n'est-ce pas?
caw

Si cela ne fait pas partie de votre scénario de risque, c'est très bien. S'ils peuvent d'une manière ou d'une autre déclencher un déchiffrement répété par le système après avoir modifié le texte chiffré (une attaque d'oracle de remplissage), ils pourraient déchiffrer les données sans jamais connaître la clé. Ils ne peuvent pas le faire s'ils se contentent de récupérer les données sur un système qui n'a pas la clé. Mais c'est pourquoi il est toujours recommandé d'ajouter un HMAC. Personnellement, je considérerais un système avec AES-128 et HMAC plus sûr que AES-256 sans - mais comme dit, probablement pas nécessaire.
Maarten Bodewes

1
Pourquoi ne pas utiliser AES en Galois / Counter-mode (AES-GCM) si vous voulez l'intégrité?
Kimvais

1

Vous avez posé une question assez intéressante. Comme pour tous les algorithmes, la clé de chiffrement est la "sauce secrète", puisqu'une fois qu'elle est connue du public, tout le reste l'est aussi. Vous cherchez donc les moyens de ce document par Google

Sécurité

Outre Google In-App Billing, donne également des réflexions sur la sécurité, ce qui est également perspicace

billing_best_practices


Merci pour ces liens! Qu'entendez-vous exactement par «lorsque la clé de chiffrement est sortie, tout le reste est également sorti»?
caw

Ce que je veux dire, c'est que la clé de cryptage doit être sécurisée, si quelqu'un peut s'en procurer, alors vos données cryptées sont aussi bonnes que du texte brut. S'il vous plaît upvote si vous avez trouvé ma réponse utile dans une certaine mesure :-)
the100rabh

0

Utilisez l'API BouncyCastle Lightweight. Il fournit 256 AES avec PBE et sel.
Voici un exemple de code, qui peut crypter / décrypter des fichiers.

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

Je vous remercie! C'est probablement une bonne solution sécurisée, mais je ne veux pas utiliser de logiciel tiers. Je suis sûr qu'il doit être possible de mettre en œuvre AES de manière sûre par soi-même.
caw

2
Cela dépend si vous souhaitez inclure une protection contre les attaques de canaux secondaires. En règle générale, vous devez supposer qu'il est assez dangereux d'implémenter vous-même des algorithmes cryptographiques. Comme AES CBC est disponible dans les bibliothèques d'exécution Java d'Oracle, il est probablement préférable de les utiliser et d'utiliser les bibliothèques Bouncy Castle si un algorithme n'est pas disponible.
Maarten Bodewes

Il manque la définition de buf(j'espère vraiment que ce n'est pas un staticchamp). Il ressemble également aux deux encrypt()et decrypt()ne traitera pas correctement le bloc final si l'entrée est un multiple de 1024 octets.
tc.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.