Quali sono le migliori pratiche per l'utilizzo della crittografia AES in Android?


90

Perché pongo questa domanda:

So che ci sono state molte domande sulla crittografia AES, anche per Android. E ci sono molti frammenti di codice se cerchi sul Web. Ma su ogni singola pagina, in ogni domanda di Stack Overflow, trovo un'altra implementazione con grandi differenze.

Quindi ho creato questa domanda per trovare una "best practice". Spero che potremo raccogliere un elenco dei requisiti più importanti e impostare un'implementazione che sia davvero sicura!

Ho letto di vettori e sali di inizializzazione. Non tutte le implementazioni che ho trovato avevano queste caratteristiche. Quindi ne hai bisogno? Aumenta molto la sicurezza? Come lo implementate? L'algoritmo dovrebbe generare eccezioni se i dati crittografati non possono essere decrittografati? O è insicuro e dovrebbe semplicemente restituire una stringa illeggibile? L'algoritmo può utilizzare Bcrypt invece di SHA?

E queste due implementazioni che ho trovato? Stanno bene? Perfetto o mancano alcune cose importanti? Cosa di questi è sicuro?

L'algoritmo dovrebbe prendere una stringa e una "password" per la crittografia e quindi crittografare la stringa con quella password. L'output dovrebbe essere di nuovo una stringa (hex o base64?). Ovviamente dovrebbe essere possibile anche la decrittazione.

Qual è la perfetta implementazione AES per Android?

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

}

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

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

}

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


Sto cercando di implementare la soluzione 1 ma necessitava di alcune classi. hai il codice sorgente completo?
albanx

1
No, non l'ho fatto, mi dispiace. Ma ho funzionato semplicemente cancellando implements ICryptoe cambiando throws CryptoExceptionin throws Exceptione così via. Quindi non avrai più bisogno di quelle lezioni.
caw

Ma manca anche la classe HexEncoder? Dove posso trovarlo?
albanx

HexEncoder fa parte della libreria BouncyCastle, credo. Puoi semplicemente scaricarlo. Oppure puoi cercare su Google "byte [] in esadecimale" e viceversa in Java.
caw

Grazie Marco. Ma ho notato che ci sono 3 metodi getSecretKey, getHash, generateSaltnella prima implementazione che sono inutilizzati. Forse mi sbaglio, ma come si potrebbe utilizzare questa classe per crittografare una stringa in pratica?
albanx

Risposte:


37

Nessuna delle implementazioni fornite nella domanda è del tutto corretta e nessuna delle due implementazioni dovrebbe essere utilizzata così com'è. In quanto segue, parlerò degli aspetti della crittografia basata su password in Android.

Chiavi e hash

Inizierò a discutere del sistema basato su password con i sali. Il sale è un numero generato casualmente. Non è "dedotto". L'implementazione 1 include un generateSalt()metodo che genera un numero casuale crittograficamente forte. Poiché il sale è importante per la sicurezza, dovrebbe essere tenuto segreto una volta generato, anche se deve essere generato solo una volta. Se si tratta di un sito Web, è relativamente facile mantenere il segreto del sale, ma per le applicazioni installate (per desktop e dispositivi mobili), ciò sarà molto più difficile.

Il metodo getHash()restituisce un hash della password e del salt forniti, concatenati in una singola stringa. L'algoritmo utilizzato è SHA-512, che restituisce un hash a 512 bit. Questo metodo restituisce un hash utile per controllare l'integrità di una stringa, quindi potrebbe anche essere utilizzato chiamando getHash()solo una password o solo un salt, poiché concatena semplicemente entrambi i parametri. Poiché questo metodo non verrà utilizzato nel sistema di crittografia basato su password, non ne parlerò ulteriormente.

Il metodo getSecretKey()deriva una chiave da un chararray della password e un salt con codifica esadecimale, come restituito da generateSalt(). L'algoritmo utilizzato è PBKDF1 (credo) da PKCS5 con SHA-256 come funzione hash e restituisce una chiave a 256 bit. getSecretKey()genera una chiave generando ripetutamente hash della password, salt e un contatore (fino al conteggio delle iterazioni indicato PBE_ITERATION_COUNT, qui 100) al fine di aumentare il tempo necessario per montare un attacco di forza bruta. La lunghezza del salt dovrebbe essere lunga almeno quanto la chiave generata, in questo caso almeno 256 bit. Il conteggio delle iterazioni dovrebbe essere impostato il più a lungo possibile senza causare ritardi irragionevoli. Per ulteriori informazioni sui sali e sui conteggi delle iterazioni nella derivazione della chiave, vedere la sezione 4 in RFC2898 .

L'implementazione nel PBE di Java, tuttavia, è difettosa se la password contiene caratteri Unicode, cioè quelli che richiedono più di 8 bit per essere rappresentati. Come affermato in PBEKeySpec, "il meccanismo PBE definito in PKCS # 5 guarda solo gli 8 bit di ordine basso di ciascun carattere". Per aggirare questo problema, puoi provare a generare una stringa esadecimale (che conterrà solo caratteri a 8 bit) di tutti i caratteri a 16 bit nella password prima di passarla a PBEKeySpec. Ad esempio, "ABC" diventa "004100420043". Nota anche che PBEKeySpec "richiede la password come un array di caratteri, quindi può essere sovrascritta [con clearPassword()] una volta fatto". (Rispetto alla "protezione delle stringhe in memoria", vedere questa domanda .) Non vedo alcun problema, però,

Crittografia

Una volta generata una chiave, possiamo usarla per crittografare e decrittografare il testo.

Nell'implementazione 1, l'algoritmo di cifratura utilizzato è AES/CBC/PKCS5Padding, cioè, AES in modalità Cipher Block Chaining (CBC), con riempimento definito in PKCS # 5. (Altre modalità di cifratura AES includono la modalità contatore (CTR), la modalità registro codici elettronico (ECB) e la modalità contatore Galois (GCM). Un'altra domanda su Stack Overflow contiene risposte che discutono in dettaglio le varie modalità di cifratura AES e quelle consigliate da usare. Tieni anche presente che ci sono diversi attacchi alla crittografia in modalità CBC, alcuni dei quali sono menzionati nella RFC 7457.)

Si noti che è necessario utilizzare una modalità di crittografia che controlli anche l'integrità dei dati crittografati (ad esempio, crittografia autenticata con dati associati , AEAD, descritta in RFC 5116). Tuttavia, AES/CBC/PKCS5Paddingnon fornisce il controllo dell'integrità, quindi da solo non è consigliato . Ai fini di AEAD, si consiglia di utilizzare un segreto lungo almeno il doppio di una normale chiave di crittografia, per evitare attacchi chiave correlati: la prima metà funge da chiave di crittografia e la seconda metà funge da chiave per il controllo di integrità. (Cioè, in questo caso, genera un singolo segreto da una password e sale e dividi quel segreto in due.)

Implementazione Java

Le varie funzioni nell'implementazione 1 utilizzano un provider specifico, ovvero "BC", per i suoi algoritmi. In generale, tuttavia, non è consigliabile richiedere provider specifici, poiché non tutti i provider sono disponibili su tutte le implementazioni Java, sia per mancanza di supporto, per evitare la duplicazione del codice o per altri motivi. Questo consiglio è diventato particolarmente importante dal rilascio dell'anteprima di Android P all'inizio del 2018, perché alcune funzionalità del provider "BC" sono state deprecate lì - vedere l'articolo "Modifiche alla crittografia in Android P" nel Blog degli sviluppatori Android. Vedi anche Introduzione ai provider Oracle .

Pertanto, PROVIDERnon dovrebbe esistere e la stringa -BCdovrebbe essere rimossa da PBE_ALGORITHM. L'attuazione 2 è corretta sotto questo aspetto.

Non è appropriato che un metodo catturi tutte le eccezioni, ma piuttosto gestisca solo le eccezioni che può. Le implementazioni fornite nella tua domanda possono generare una varietà di eccezioni verificate. Un metodo può scegliere di racchiudere solo quelle eccezioni verificate con CryptoException o specificare quelle eccezioni verificate nella throwsclausola. Per comodità, il wrapping dell'eccezione originale con CryptoException può essere appropriato qui, poiché ci sono potenzialmente molte eccezioni controllate che le classi possono generare.

SecureRandom in Android

Come spiegato in dettaglio nell'articolo "Some SecureRandom Thoughts", nel blog degli sviluppatori Android, l'implementazione di java.security.SecureRandomnelle versioni Android prima del 2013 presenta un difetto che riduce la forza dei numeri casuali che fornisce. Questo difetto può essere mitigato come descritto in quell'articolo.


Quella generazione di doppio segreto è un po 'dispendiosa secondo me, potresti facilmente dividere il segreto generato in due, o - se non sono disponibili abbastanza bit - aggiungere un contatore (1 per la prima chiave, 2 per la seconda chiave) al segreto ed eseguire un singolo hash. Non è necessario eseguire tutte le iterazioni due volte.
Maarten Bodewes

Grazie per le informazioni su HMAC e il sale. Questa volta non userò HMAC ma in seguito potrebbe essere molto utile. E in generale, questa è una buona cosa, senza dubbio.
caw

Grazie mille per tutte le modifiche e questa (ora) meravigliosa introduzione alla crittografia AES in Java!
caw

1
Dovrebbe. getInstanceha un sovraccarico che prende solo il nome dell'algoritmo. Esempio: Cipher.getInstance () Diversi provider, incluso Bouncy Castle, possono essere registrati nell'implementazione Java e questo tipo di sovraccarico cerca nell'elenco dei provider uno di essi che implementa l'algoritmo dato. Dovresti provarlo e vedere.
Peter O.

1
Sì, cercherà i provider nell'ordine dato da Security.getProviders (), sebbene ora controllerà anche se la chiave è accettata da quel provider durante la chiamata init () consentendo la crittografia assistita dall'hardware. Maggiori dettagli qui: docs.oracle.com/javase/6/docs/technotes/guides/security/crypto/… .
Maarten Bodewes

18

# 2 non dovrebbe mai essere utilizzato in quanto utilizza solo "AES" (che significa crittografia in modalità ECB sul testo, un grande no-no) per il cifrario. Parlerò solo del n. 1.

La prima implementazione sembra aderire alle migliori pratiche per la crittografia. Le costanti sono generalmente OK, sebbene sia la dimensione del sale sia il numero di iterazioni per eseguire PBE siano sul lato corto. Inoltre, sembra essere per AES-256 poiché la generazione della chiave PBE utilizza 256 come valore hardcoded (un peccato dopo tutte quelle costanti). Utilizza CBC e PKCS5Padding, che è almeno quello che ti aspetteresti.

Manca completamente qualsiasi protezione di autenticazione / integrità, quindi un utente malintenzionato può modificare il testo cifrato. Ciò significa che il riempimento degli attacchi Oracle è possibile in un modello client / server. Significa anche che un utente malintenzionato può provare a modificare i dati crittografati. Questo probabilmente risulterà in qualche errore da qualche parte perché il riempimento o il contenuto non è accettato dall'applicazione, ma non è una situazione in cui vorresti trovarti.

La gestione delle eccezioni e la convalida dell'input potrebbero essere migliorate, catturare l'eccezione è sempre sbagliato nel mio libro. Inoltre, la classe implementa ICrypt, che non conosco. So che avere solo metodi senza effetti collaterali in una classe è un po 'strano. Normalmente, li renderesti statici. Non vi è alcun buffering delle istanze di Cipher ecc., Quindi ogni oggetto richiesto viene creato fino alla nausea. Tuttavia, puoi rimuovere in sicurezza ICrypto dalla definizione a quanto pare, in quel caso potresti anche rifattorizzare il codice in metodi statici (o riscriverlo per essere più orientato agli oggetti, a tua scelta).

Il problema è che qualsiasi wrapper fa sempre ipotesi sul caso d'uso. Dire che un involucro è giusto o sbagliato è quindi una sciocchezza. Questo è il motivo per cui cerco sempre di evitare di generare classi wrapper. Ma almeno non sembra esplicitamente sbagliato.


Grazie mille per questa risposta dettagliata! So che è un peccato, ma non conoscevo ancora la sezione di revisione del codice: D Grazie per questo suggerimento, lo controllerò. Ma questa domanda rientra anche qui, secondo me, poiché non voglio solo una revisione di questi frammenti di codice. Invece, voglio chiedere a tutti voi quali aspetti sono importanti quando si implementa la crittografia AES in Android. E hai ragione di nuovo, questo frammento di codice è per AES-256. Quindi diresti che questa è generalmente un'implementazione sicura di AES-256? Il caso d'uso è che voglio solo memorizzare in modo sicuro le informazioni di testo in un database.
caw

1
Sembra buono, ma l'idea di non avere controlli di integrità e autenticazione mi darebbe fastidio. Se hai abbastanza spazio, prenderei seriamente in considerazione l'aggiunta di un HMAC sul testo cifrato. Detto questo, poiché probabilmente stai cercando di aggiungere semplicemente riservatezza, lo considererei un grande vantaggio, ma non direttamente un requisito.
Maarten Bodewes

Ma se l'intenzione è solo che altri non dovrebbero avere accesso alle informazioni crittografate, non ho bisogno di un HMAC, giusto? Se cambiano il testo cifrato e forzano un risultato "sbagliato" della decrittazione, non c'è un vero problema, vero?
caw

Se questo non è nel tuo scenario di rischio, allora va bene. Se possono in qualche modo attivare una decrittografia ripetuta dal sistema dopo aver alterato il testo cifrato (un attacco oracolo di riempimento), allora potrebbero decrittografare i dati senza mai conoscere la chiave. Non possono farlo se semplicemente acquisiscono i dati su un sistema che non ha la chiave. Ma è per questo che è sempre buona norma aggiungere un HMAC. Personalmente, considererei un sistema con AES-128 e HMAC più sicuro di AES-256 senza - ma come detto, probabilmente non richiesto.
Maarten Bodewes

1
Perché non utilizzare AES in modalità Galois / Counter (AES-GCM) se si desidera l'integrità?
Kimvais

1

Hai posto una domanda piuttosto interessante. Come con tutti gli algoritmi, la chiave di cifratura è la "salsa segreta", poiché una volta che è nota al pubblico, lo è anche tutto il resto. Quindi esamini i modi per questo documento di Google

sicurezza

Oltre alla fatturazione in-app di Google, fornisce anche considerazioni sulla sicurezza che sono anche approfondite

billing_best_practices


Grazie per questi link! Cosa intendi esattamente con "quando la chiave di cifratura è fuori, anche tutto il resto è fuori"?
caw

Quello che voglio dire è che la chiave di crittografia deve essere sicura, se qualcuno può ottenerla, i tuoi dati crittografati sono buoni come il testo in chiaro. Per favore vota se hai trovato la mia risposta utile fino a un certo punto :-)
the100rabh

0

Utilizza l'API BouncyCastle Lightweight. Fornisce 256 AES con PBE e sale.
Qui codice di esempio, che può crittografare / decrittografare i file.

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

Grazie! Questa è probabilmente una soluzione buona e sicura, ma non voglio utilizzare software di terze parti. Sono sicuro che deve essere possibile implementare AES in modo sicuro da soli.
caw

2
Dipende se si desidera includere la protezione dagli attacchi del canale laterale. In generale, dovresti presumere che sia piuttosto pericoloso implementare algoritmi crittografici da solo. Poiché AES CBC è disponibile nelle librerie di runtime Java di Oracle, è probabilmente meglio utilizzarle e utilizzare le librerie Bouncy Castle se un algoritmo non è disponibile.
Maarten Bodewes

Manca la definizione di buf( spero davvero che non sia un staticcampo). Inoltre sembra entrambi encrypt()e decrypt()non riuscirà a elaborare correttamente il blocco finale se l'input è un multiplo di 1024 byte.
tc.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.