it-swarm-eu.dev

Java AES / GCM / NoPadding - Cosa mi dà cipher.getIV ()?

Sto usando la crittografia AES/GCM/NoPadding In Java 8 e mi chiedo se il mio codice ha un difetto di sicurezza. Il mio codice sembra funziona , in quanto crittografa e decodifica il testo, ma alcuni dettagli non sono chiari.

La mia domanda principale è questa:

Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] iv = cipher.getIV(); // ?????

Quel IV soddisfa il requisito "Per un dato tasto, il IV NON DEVE ripetere." da RFC 4106 ?

Gradirei anche qualsiasi risposta/intuizione per le mie domande correlate (vedi sotto), ma quella prima domanda mi infastidisce di più. Non so dove trovare il codice sorgente o la documentazione che risponda a questo.


Ecco il codice completo, approssimativamente. Mi scuso nel caso in cui ho introdotto errori durante la scrittura di questo post:

class Encryptor {
  Key key;

  Encryptor(byte[] key) {
    if (key.length != 32) throw new IllegalArgumentException();
    this.key = new SecretKeySpec(key, "AES");
  }

  // the output is sent to users
  byte[] encrypt(byte[] src) throws Exception {
    Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
    cipher.init(Cipher.ENCRYPT_MODE, key);
    byte[] iv = cipher.getIV(); // See question #1
    assert iv.length == 12; // See question #2
    byte[] cipherText = cipher.doFinal(src);
    assert cipherText.length == src.length + 16; // See question #3
    byte[] message = new byte[12 + src.length + 16]; // See question #4
    System.arraycopy(iv, 0, message, 0, 12);
    System.arraycopy(cipherText, 0, message, 12, cipherText.length);
    return message;
  }

  // the input comes from users
  byte[] decrypt(byte[] message) throws Exception {
    if (message.length < 12 + 16) throw new IllegalArgumentException();
    Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
    GCMParameterSpec params = new GCMParameterSpec(128, message, 0, 12);
    cipher.init(Cipher.DECRYPT_MODE, key, params);
    return cipher.doFinal(message, 12, message.length - 12);
  }
}

Supponiamo che gli utenti rompano la mia chiave segreta = game over.


Domande più dettagliate/domande correlate:

  1. L'IV restituito da cipher.getIV () è sicuro per me da usare in questo modo?

    • Evita la catastrofe di riutilizzare la combinazione di tasti IV, in modalità Galois/Counter?
    • È ancora sicuro quando ho più applicazioni che eseguono questo codice contemporaneamente, tutte mostrando messaggi crittografati agli utenti dagli stessi dati src (possibilmente nello stesso millisecondo)?
    • Di cosa è fatto l'IV restituito? È un contatore atomico più un po 'di rumore casuale?
    • Devo evitare cipher.getIV() e costruire un IV da solo, con il mio contatore?
    • Il codice sorgente che implementa cipher.getIV() è disponibile online da qualche parte, supponendo che sto usando l'estensione Oracle JDK 8 + JCE Unlimited Strength?
  2. Quel IV è sempre lungo 12 byte?

  3. Il tag di autenticazione è sempre lungo 16 byte (128 bit)?

  4. Con # 2 e # 3 e la mancanza di riempimento, ciò significa che i miei messaggi crittografati sono sempre lunghi 12 + src.length + 16 Byte? (E così posso tranquillamente schiacciarli in un array di byte, per il quale conosco la lunghezza corretta?)

  5. È sicuro per me mostrare agli utenti un numero illimitato di crittografie di dati SRC, dati costanti dati SRC che gli utenti conoscono?

  6. È sicuro per me mostrare agli utenti un numero illimitato di crittografia dei dati src, se i dati src sono sempre diversi (ad es. Includendo System.currentTimeMillis() o numeri casuali)?

  7. Aiuterebbe se riempissi i dati di src con numeri casuali prima della crittografia? Di '8 byte casuali davanti e dietro o solo su un'estremità? O ciò non aiuterebbe affatto/peggiorerebbe la mia crittografia?

(Poiché queste domande riguardano tutte lo stesso blocco del mio codice e sono strettamente correlate tra loro e altre potrebbero/dovrebbero avere lo stesso insieme di domande durante l'implementazione della stessa funzionalità, è sembrato sbagliato dividere le domande in più post. Posso ripubblicarli separatamente se è più appropriato per il formato StackOverflow. Fammi sapere!)

32
Michael Hixson

Q1: IV è restituito da cipher.getIV () in modo che io possa usarlo in questo modo?

Sì, è almeno per l'implementazione fornita da Oracle. Viene generato separatamente utilizzando l'implementazione predefinita SecureRandom. Dato che ha una dimensione di 12 byte (il valore predefinito per GCM), hai 96 bit di casualità. La possibilità che il contatore si ripeta è spaventosamente piccola. Puoi cercare l'origine in OpenJDK (GPL'ed) su cui si basa Oracle JDK.

Ti consiglierei comunque di generare i tuoi 12 byte casuali poiché altri provider potrebbero comportarsi diversamente.


D2: Quel IV è sempre lungo 12 byte?

È estremamente probabile poiché è l'impostazione predefinita GCM, ma altre lunghezze sono valide per GCM. L'algoritmo dovrà tuttavia eseguire calcoli aggiuntivi per qualsiasi dimensione diversa da 12 byte. A causa dei punti deboli, si consiglia vivamente di mantenerlo a 12 byte/96 bit e le API possono limitare la scelta di dimensioni IV.


Q3: il tag di autenticazione è sempre lungo 16 byte (128 bit)?

No, può avere qualsiasi dimensione in byte che varia da 64 bit a 128 bit con incrementi di 8 bit. Se è più piccolo, tuttavia, consiste semplicemente dei byte più a sinistra del tag di autenticazione. Puoi specificare un'altra dimensione del tag usando GCMParameterSpec come terzo parametro per la tua chiamata init.

Si noti che la forza di GCM dipende fortemente dalla dimensione del tag. Consiglierei di tenerlo a 128 bit. 96 bit dovrebbe essere il minimo soprattutto se si desidera generare molto testo cifrato.


Q4: Con # 2 e # 3 e la mancanza di riempimento, ciò significa che i miei messaggi crittografati sono sempre lunghi 12 + lunghezza src + 16 byte? (E così posso tranquillamente comprimerli in un array di byte, per il quale conosco la lunghezza corretta?)

Vedi sopra. Per il provider Oracle questo è il caso. Usa GCMParameterSpec per esserne sicuro.


Q5: È sicuro per me mostrare agli utenti un numero illimitato di crittografazioni di dati src, dati costanti dati src che gli utenti conoscono?

Praticamente nessun impegno, sì. Vorrei iniziare a preoccuparmi dopo circa 2 ^ 48 crittografie. In generale dovresti comunque progettare per cambio chiave.


Q6: È sicuro per me mostrare agli utenti un numero illimitato di crittografazioni di dati src, se i dati src sono ogni volta diversi (ad es. Includendo System.currentTimeMillis () o numeri casuali)?

Vedi risposta a Q5


Q7: sarebbe di aiuto se riempissi i dati src di numeri casuali prima della crittografia? Di '8 byte casuali davanti e dietro o solo su un'estremità? O ciò non aiuterebbe affatto/peggiorerebbe la mia crittografia?

No, non sarebbe affatto d'aiuto. GCM utilizza la modalità CTR al di sotto, quindi sarebbe solo crittografato con il flusso di chiavi. no fungerebbe da IV. Se hai bisogno di un numero praticamente illimitato di cifratix (superiore a 2 ^ 48!), Ti suggerirei di usare quel numero casuale e la tua chiave per una funzione di derivazione della chiave o KDF. HKDF è attualmente il migliore della razza, ma potrebbe essere necessario utilizzare Bouncy Castle o implementarlo da soli.

39
Maarten Bodewes