2016-07-19 27 views
5

Czasami spotykam ciekawe, dziwne rzeczy: ten sam blok zaszyfrowanego tekstu można odszyfrować, używając kilku różnych kluczy!Dziwne zachowanie DES - odszyfrowywanie się powiodło za pomocą różnych kluczy

Czy ktoś może mi powiedzieć, co się dzieje? Wielkie dzięki.

Proszę, nie próbujcie pozwolić mi przełączać się na potrójne DES/AES itp., Chcę tylko wiedzieć, gdzie jest problem - sposób wywoływania pakietu Java SDK lub błędu w pakiecie Java SDK?

Poniżej znajduje się wyjście na Windows 7, taki sam rezultat w Linuksie:

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:\> 

Kod źródłowy:

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

I debugowania kodu, stwierdziłem, że różne ciąg klucza wygeneruje ten sam klucz secureKey (wiersz 39), tj. [97, 98, 98, 100, 100, 56, 55, 55], ten sam klucz secureKey powoduje sukces deszyfrowania . , ale nie mam pojęcia, dlaczego inny klucz wygeneruje ten sam klucz secureKey i jak go uniknąć? – reqresp

Odpowiedz

4

Operacja DES (zarówno szyfrowanie i deszyfrowanie) ignoruje lsbit każdego bajta klucza. Oznacza to, że jeśli odwrócisz dowolne z lbit w kluczu, operacja pozostanie niezmieniona. Tak dzieje się w kluczach, które wypróbowałeś: kod ASCII dla spacji to 0x20, a kod ASCII dla! jest 0x21; różnią się tylko na lsbicie. Tak więc, jeśli klucz ma bajt z kodem ASCII dla spacji, można go zastąpić znakiem! I nadal będzie mógł odszyfrować. Podobnie kod ASCII dla * to 0x2a, podczas gdy kod ASCII dla + to 0x2b; różni się także tylko na lsbicie.

W oryginalnym standardzie DES, lsbit miał być używany jako bit sprawdzania parzystości (z każdym bajtem zawsze mającym nieparzystą parzystość). Miał to być słaby sprawdzian błędów dla ręcznie wprowadzanych kluczy. W dzisiejszych czasach nikt nie robi tego sprawdzania parzystości, więc lsbit zostaje zignorowany.

Wyodrębniona z Poncho's wnikliwa Answer na Cryptography Stackexchange.

+0

rozumiem. Dziękuję za wyjaśnienie! – reqresp

1

DES ma 56-bitowy klucz, lsbit każdego bajtu klucza był początkowo używany do parzystości, teraz jest ignorowany.

Odpowiedź brzmi: nie używaj DES! DES jest niezabezpieczony i został zastąpiony przez AES (Advanced Encryption Standard) AES został specjalnie zaprojektowany, aby zastąpić DES.

Ponadto nie należy używać ciągu znaków jako klucza, najlepiej jest wyprowadzić klucz szyfrujący z ciągu znaków za pomocą funkcji, takiej jak PBKDF2 (funkcja wyprowadzania klucza opartego na haśle).

+0

rozumiem. Dziękuję za wyjaśnienie! – reqresp