2016-07-19 197 views
5

Occasionalmente, ho una cosa interessante, strana: lo stesso blocco di testo crittografato può essere decifrato usando diverse chiavi diverse!Strano comportamento DES: la decrittografia viene eseguita con successo utilizzando chiavi diverse

Qualcuno può indicarmi che cosa non funziona? Molte grazie.

Si prega di non provare a farmi passare a DES/AES tripla ecc., Voglio solo sapere dove si trova il problema - il modo in cui si chiama Java SDK, o il bug in Java SDK?

Di seguito è uscita su Windows 7, stesso risultato in Linux:

D:\>java -version 
java version "1.7.0_21" 
Java(TM) SE Runtime Environment (build 1.7.0_21-b11) 
Java HotSpot(TM) 64-Bit Server VM (build 23.21-b01, mixed mode) 

D:\>java DESTest -e 12345678 abcde977 

encrypted as [17fd146fa6fdbb5db667efe657dfcb60] 

D:\>java DESTest -d 17fd146fa6fdbb5db667efe657dfcb60 abcde977 

decryted as [12345678] 

D:\>java DESTest -d 17fd146fa6fdbb5db667efe657dfcb60 abcde976 

decryted as [12345678] 

D:\>java DESTest -d 17fd146fa6fdbb5db667efe657dfcb60 abcde967 

decryted as [12345678] 

D:\>java DESTest -d 17fd146fa6fdbb5db667efe657dfcb60 abcde867 

decryted as [12345678] 

D:\>java DESTest -d 17fd146fa6fdbb5db667efe657dfcb60 abcdf867 
Exception in thread "main" java.lang.RuntimeException: javax.crypto.BadPaddingEx 
ception: Given final block not properly padded 
     at DESTest.des(DESTest.java:46) 
     at DESTest.dec(DESTest.java:31) 
     at DESTest.main(DESTest.java:19) 
Caused by: javax.crypto.BadPaddingException: Given final block not properly padd 
ed 
     at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:811) 
     at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:676) 
     at com.sun.crypto.provider.DESCipher.engineDoFinal(DESCipher.java:314) 
     at javax.crypto.Cipher.doFinal(Cipher.java:2087) 
     at DESTest.des(DESTest.java:44) 
     ... 2 more 

D:\>java DESTest -e 12345678 abcde976 

encrypted as [17fd146fa6fdbb5db667efe657dfcb60] 

D:\>java DESTest -e 12345678 abcde967 

encrypted as [17fd146fa6fdbb5db667efe657dfcb60] 

D:\> 

Il codice sorgente:

import java.io.UnsupportedEncodingException; 
import java.security.SecureRandom; 

import javax.crypto.Cipher; 
import javax.crypto.SecretKey; 
import javax.crypto.SecretKeyFactory; 
import javax.crypto.spec.DESKeySpec; 

public class DESTest { 
    public static void main(String[] args) { 
     if (args.length < 3) { 
      System.out.println("usage: java " + DESTest.class.getCanonicalName() + " -e|-d text key"); 
      return; 
     } 
     String mode = args[0].trim(); 
     String text = args[1].trim(); 
     String key = args[2].trim(); 
     try { 
      String s = "-d".equalsIgnoreCase(mode) ? dec(text, key) : enc(text, key); 
      System.out.println("\n" + ("-d".equalsIgnoreCase(mode) ? "decryted as [" : "encrypted as [") + s + "]"); 
     } catch (UnsupportedEncodingException e) { 
      e.printStackTrace(); 
     } 
    } 

    private static String enc(String plainText, String key) throws UnsupportedEncodingException { 
     return new String(encHex(des(plainText.getBytes("UTF-8"), key, Cipher.ENCRYPT_MODE))); 
    } 

    private static String dec(String encrypted, String key) throws UnsupportedEncodingException { 
     return new String(des(decHex(encrypted), key, Cipher.DECRYPT_MODE), "UTF-8"); 
    } 

    private static byte[] des(byte[] bytes, String key, int cipherMode) { 
     final String encoding = "UTF-8"; 
     try { 
      DESKeySpec desKey = new DESKeySpec(key.getBytes(encoding)); 
      SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES"); 
      SecretKey securekey = keyFactory.generateSecret(desKey); 
      // SecretKey securekey = new SecretKeySpec(key.getBytes(encoding), "DES");//same result as the 3 lines above 
      Cipher cipher = Cipher.getInstance("DES"); 
      SecureRandom random = new SecureRandom(); 
      cipher.init(cipherMode, securekey, random); 
      return cipher.doFinal(bytes); 
     } catch (Exception e) { 
      throw new RuntimeException(e); 
     } 
    } 

    private static final char[] HEX_CHARS = "abcdef".toCharArray(); 

    private static String encHex(byte[] bytes) { 
     final char[] chars = new char[bytes.length * 2]; 
     for (int i = 0, j = 0; i < bytes.length; i++) { 
      chars[j++] = HEX_CHARS[(0xF0 & bytes[i]) >>> 4]; 
      chars[j++] = HEX_CHARS[0x0F & bytes[i]]; 
     } 
     return new String(chars); 
    } 

    private static byte[] decHex(String hex) { 
     final int len = hex.length(); 
     final byte[] bytes = new byte[len/2]; 
     for (int i = 0, j = 0; j < len; i++) { 
      int f = Character.digit(hex.charAt(j), 16) << 4; 
      j++; 
      f = f | Character.digit(hex.charAt(j), 16); 
      j++; 
      bytes[i] = (byte) (f & 0xFF); 
     } 
     return bytes; 
    } 
} 
+0

Eseguo il debug del codice, ho rilevato che la stringa di chiavi diverse genererà lo stesso secureKey (riga 39), ovvero [97, 98, 98, 100, 56, 55, 55], la stessa chiave di sicurezza causa il successo della decrittografia . ma non ho idea del perché una chiave diversa genererà lo stesso secureKey e come evitarlo? – reqresp

risposta

4

L'operazione DES (sia la crittografia e la decrittografia) ignora il LSBit di ogni byte della chiave. Cioè, se si capovolge uno degli lsbit all'interno della chiave, l'operazione rimane la stessa. Questo è ciò che sta accadendo nelle chiavi che hai provato: il codice ASCII per lo spazio è 0x20, mentre il codice ASCII per! è 0x21; essi differiscono solo nell'Ibit. Quindi, se la chiave ha un byte con il codice ASCII per uno spazio, è possibile sostituirlo con un!, E sarà ancora in grado di decifrare. Allo stesso modo, il codice ASCII per * è 0x2a, mentre il codice ASCII per + è 0x2b; differisce anche solo nell'lbit.

Nello standard DES originale, si supponeva che l'lsbit fosse usato come bit di controllo di parità (ogni byte aveva sempre parità dispari). Doveva essere un debole controllo degli errori per i tasti inseriti manualmente. Al giorno d'oggi, nessuno fa questo controllo di parità, e così l'lsbit viene ignorato.

Estratto da Poncho's penetranti Answer su Cryptography Stackexchange.

+0

capito. Grazie per la spiegazione! – reqresp

1

DES ha una chiave a 56 bit, l'lsbit di ogni byte chiave è stato inizialmente utilizzato per la parità, ora viene ignorato.

La risposta è: non usare DES! DES è insicuro ed è stato superato da AES (Advanced Encryption Standard) AES è stato specificamente progettato per sostituire DES.

Inoltre, non è necessario utilizzare una stringa di caratteri come chiave, la procedura consigliata consiste nel derivare una chiave di crittografia dalla stringa di caratteri con una funzione come PBKDF2 (funzione di derivazione chiave basata sulla password).

+0

capito. Grazie per la spiegazione! – reqresp