AES Encryption random IV per file

3

Vai all'aggiornamento di seguito < Non sai come creare un collegamento: (

Ho apportato alcune migliorie al codice da: Csharp- AES-bits-crittografia-Biblioteca-con-Salt

  • saltBytes ora è SHA512 della password.
  • Casuale IV per ogni chiamata di crittografia. (IV lunghezza 16 viene aggiunta al file crittografato, rimossa dal file prima della decrittografia)

Vedi qualche difetto, qualcosa che ha bisogno di ottimizzazione? In particolare, le mie domande sono:

  • Il mio metodo generateIV() di seguito nel "Codice di crittografia" è sicuro e protetto? Si noti che la sua unica dipendenza è su .NET RNGCryptoServiceProvider Class .

  • È sicuro usare l'hash della password come un salino? o dovrebbe essere casuale come la IV e archiviata insieme al testo cifrato?

Solo per riferimento, ecco il mio codice.

Il codice qui sotto funziona, ho eseguito un test per crittografare due file di testo con lo stesso testo all'interno di ciascuno. Il risultato è che entrambi hanno dati diversi e decrittano.

Inoltre, ho controllato prima del IV casuale, entrambi i file avevano lo stesso testo crittografato, risultati negli stessi dati.

Codice di crittografia:

    private static int IV_LENGTH = 16;

    public static byte[] AES_Encrypt(byte[] bytesToBeEncrypted, byte[] passwordBytes)
    {

        byte[] encryptedBytes = null;
        byte[] encryptedBytesAndIV = null;
        byte[] saltBytes = SHA512.Create().ComputeHash(passwordBytes);
        using (System.IO.MemoryStream ms = new System.IO.MemoryStream())
        {
            using (AesCryptoServiceProvider AES = new AesCryptoServiceProvider())
            {
                AES.KeySize = 256;
                //AES.BlockSize = 128;

                var key = new Rfc2898DeriveBytes(passwordBytes, saltBytes, 100);
                AES.Key = key.GetBytes(AES.KeySize / 8);
                AES.IV = generateIV();

                AES.Mode = CipherMode.CBC;

                using (var cs = new CryptoStream(ms, AES.CreateEncryptor(), CryptoStreamMode.Write))
                {
                    cs.Write(bytesToBeEncrypted, 0, bytesToBeEncrypted.Length);
                    cs.Close();
                }
                encryptedBytes = ms.ToArray();
                encryptedBytesAndIV = new byte[encryptedBytes.Length + AES.IV.Length];
                AES.IV.CopyTo(encryptedBytesAndIV, 0);
                encryptedBytes.CopyTo(encryptedBytesAndIV, IV_LENGTH);
            }
        }

        return encryptedBytesAndIV;
    }

    private static byte[] generateIV()
    {
        using (RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider())
        {
            byte[] nonce = new byte[IV_LENGTH];
            rng.GetBytes(nonce);
            return nonce;
        }
    }

Codice di decrittazione:

    public static byte[] AES_Decrypt(byte[] bytesToBeDecrypted, byte[] passwordBytes)
    {
        byte[] decryptedBytes = null;


        byte[] saltBytes = SHA512.Create().ComputeHash(passwordBytes);

        using (MemoryStream ms = new MemoryStream())
        {
            using (AesCryptoServiceProvider AES = new AesCryptoServiceProvider())
            {
                AES.KeySize = 256;
                //AES.BlockSize = 128;

                var key = new Rfc2898DeriveBytes(passwordBytes, saltBytes, 100);
                AES.Key = key.GetBytes(AES.KeySize / 8);
                AES.IV = getIV(bytesToBeDecrypted);
                bytesToBeDecrypted = removeTagAndIV(bytesToBeDecrypted);
                AES.Mode = CipherMode.CBC;

                using (var cs = new CryptoStream(ms, AES.CreateDecryptor(), CryptoStreamMode.Write))
                {
                    cs.Write(bytesToBeDecrypted, 0, bytesToBeDecrypted.Length);
                    cs.Close();
                }
                decryptedBytes = ms.ToArray();


            }
        }

        return decryptedBytes;
    }

    private static byte[] removeTagAndIV(byte[] arr)
    {
        byte[] enc = new byte[arr.Length - IV_LENGTH];
        Array.Copy(arr, IV_LENGTH, enc, 0, arr.Length - IV_LENGTH);
        return enc;
    }

    private static byte[] getIV(byte[] arr)
    {
        byte[] IV = new byte[IV_LENGTH];
        Array.Copy(arr, 0, IV, 0, IV_LENGTH);
        return IV;
    }

Aggiornamento:

Ecco un codice aggiornato basato su commenti / raccomandazioni / consigli

  • Random Salt True
  • iterazioni 100000

Crittografia:

        public static byte[] AES_Encrypt(byte[] bytesToBeEncrypted, byte[] passwordBytes)
    {

        byte[] encryptedBytes = null;
        byte[] encryptedBytesFinal = null;
        byte[] saltBytes = generateIVandSalt(16);
        using (System.IO.MemoryStream ms = new System.IO.MemoryStream())
        {
            using (AesCryptoServiceProvider AES = new AesCryptoServiceProvider())
            {
                AES.KeySize = 256;

                var key = new Rfc2898DeriveBytes(passwordBytes, saltBytes, 100000);

                AES.Key = key.GetBytes(AES.KeySize / 8);

                AES.IV = generateIVandSalt(16);

                AES.Mode = CipherMode.CBC;

                using (var cs = new CryptoStream(ms, AES.CreateEncryptor(), CryptoStreamMode.Write))
                {
                    cs.Write(bytesToBeEncrypted, 0, bytesToBeEncrypted.Length);
                    cs.Close();
                }
                encryptedBytes = ms.ToArray();
                encryptedBytesFinal = new byte[encryptedBytes.Length + AES.IV.Length + saltBytes.Length];

                AES.IV.CopyTo(encryptedBytesFinal, 0);
                saltBytes.CopyTo(encryptedBytesFinal, 16);
                encryptedBytes.CopyTo(encryptedBytesFinal, 16 + 16);

            }
        }

        return encryptedBytesFinal;
    }
    private static byte[] generateIVandSalt(int len)
    {
        using (RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider())
        {
            byte[] nonce = new byte[len];
            rng.GetBytes(nonce);
            return nonce;
        }
    }

Decodifica:

        public static byte[] AES_Decrypt(byte[] bytesToBeDecrypted, byte[] passwordBytes)
    {
        byte[] decryptedBytes = null;


        byte[] saltBytes = getSalt(bytesToBeDecrypted);

        using (MemoryStream ms = new MemoryStream())
        {
            using (AesCryptoServiceProvider AES = new AesCryptoServiceProvider())
            {
                AES.KeySize = 256;


                var key = new Rfc2898DeriveBytes(passwordBytes, saltBytes, 100000);
                AES.Key = key.GetBytes(AES.KeySize / 8);
                AES.IV = getIV(bytesToBeDecrypted);
                bytesToBeDecrypted = removeIVandSalt(bytesToBeDecrypted);
                AES.Mode = CipherMode.CBC;

                using (var cs = new CryptoStream(ms, AES.CreateDecryptor(), CryptoStreamMode.Write))
                {
                    cs.Write(bytesToBeDecrypted, 0, bytesToBeDecrypted.Length);
                    cs.Close();
                }
                decryptedBytes = ms.ToArray();


            }
        }

        return decryptedBytes;
    }

    private static byte[] removeIVandSalt(byte[] arr)
    {
        byte[] enc = new byte[arr.Length - 16 - IV_LENGTH];
        Array.Copy(arr, IV_LENGTH + 16, enc, 0, arr.Length - IV_LENGTH - 16);
        return enc;
    }
    private static byte[] getIV(byte[] arr)
    {
        byte[] IV = new byte[IV_LENGTH];
        Array.Copy(arr, 0, IV, 0, IV_LENGTH);
        return IV;
    }

    private static byte[] getSalt(byte[] arr)
    {
        byte[] salt = new byte[16];
        Array.Copy(arr, IV_LENGTH, salt, 0, 16);
        return salt;
    }
    
posta xhxx 21.11.2016 - 18:27
fonte

2 risposte

6

Da un punto di vista della sicurezza, il metodo generateIV() va bene, se un po 'ridondante dato che .NET genererà un IV per te esattamente nello stesso modo se non ne viene fornito uno.

I tre problemi più grandi sono che 100 iterazioni sono modo troppo poche per PBKDF2, la mancanza di autenticazione, che è un problema se il testo cifrato è sempre fuori dal tuo controllo (a parte hai un metodo denominato removeTagAndIV() ma nessun tag implementato in primo luogo) e l'uso dell'hash della password come salt. Il punto di un sale deve essere globalmente unico. L'hash di una password non valida. Ora hai indebolito in modo significativo il tuo PBKFD, hai notevolmente limitato le dimensioni potenziali dello spazio delle chiavi pratico e hai creato il potenziale per l'utilizzo delle tabelle arcobaleno per identificare le chiavi. Il sale deve essere globalmente unico. Il modo più semplice per ottenere questo risultato è semplicemente generare un salt usando il metodo generateIV() che hai già e memorizzarlo accanto alla IV e al testo cifrato.

    
risposta data 21.11.2016 - 19:07
fonte
3
  1. Non sono un programmatore .NET e non comprendo completamente parte del codice qui.

  2. Quanta entropia è in passwordBytes ? Se l'entropia è inferiore a 72 bit, dovrai utilizzare un Hash password lento () o più specificamente una funzione di derivazione chiave.

  3. generateIV dovrebbe andare bene fintanto che è casuale. Non ha nemmeno bisogno di essere un numero casuale crittograficamente sicuro, purché sia globalmente unico.

  4. Se vuoi essere sicuro che CBC sia una buona modalità di concatenazione, puoi fare una domanda a parte. Avremo risposte sui pro e contro di ciascuna modalità di concatenazione.

  5. Non dovresti chiederci di controllare il codice decrittazione per sicurezza. Puoi controllare te stesso semplicemente testando.

  6. Il sale dovrebbe essere casuale, non deterministico. Aggiungi anche Pepper.

  7. Vedi # 5, quindi rendi il tuo codice di crittografia il più breve e conciso possibile. Fai domande su ogni singolo punto che ti piace, ma facendoci rivedere tutto è un po 'fuori tema qui.

  8. AES è una buona scelta.

risposta data 21.11.2016 - 19:02
fonte

Leggi altre domande sui tag