2016-07-19 16 views
5

Thỉnh thoảng, tôi gặp một điều thú vị, lạ: cùng một khối văn bản được mã hóa có thể được giải mã bằng nhiều phím khác nhau!Hành vi DES lạ - giải mã thành công bằng cách sử dụng các phím khác nhau

Có ai vui lòng cho tôi biết điều gì đang xảy ra không? Cảm ơn rất nhiều.

Xin vui lòng không cố gắng để tôi chuyển sang ba DES/AES vv, tôi chỉ muốn biết vấn đề ở đâu - cách gọi Java SDK, hoặc lỗi trong Java SDK?

Dưới đây là đầu ra trên Windows 7, cùng kết quả trong Linux hộp:

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

Mã nguồn:

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

Tôi gỡ lỗi mã, thấy rằng chuỗi khóa khác nhau sẽ tạo cùng một khóa bảo mật (dòng 39), tức là [97, 98, 98, 100, 100, 56, 55, 55], cùng một secureKey gây ra thành công của giải mã . nhưng tôi không biết tại sao khóa khác nhau sẽ tạo ra cùng một khóa bảo mật, và làm thế nào để tránh nó? – reqresp

Trả lời

4

Các hoạt động DES (cả mã hóa và giải mã) bỏ qua các lsbit của mỗi byte của khóa. Nghĩa là, nếu bạn lật bất kỳ lsbits nào trong khóa, thao tác vẫn giữ nguyên. Đó là những gì đang xảy ra trong các khóa bạn đã thử: mã ASCII cho không gian là 0x20, trong khi mã ASCII cho! là 0x21; chúng chỉ khác nhau ở lsbit. Vì vậy, nếu khóa có một byte với mã ASCII cho một không gian, bạn có thể thay thế nó bằng một ký tự! Và nó vẫn có thể giải mã được. Tương tự, mã ASCII cho * là 0x2a, trong khi mã ASCII cho + là 0x2b; cũng chỉ khác nhau trong lsbit.

Trong tiêu chuẩn DES ban đầu, lsbit được cho là được sử dụng làm bit kiểm tra chẵn lẻ (với mỗi byte luôn có tính chẵn lẻ lẻ). Nó được coi là một kiểm tra lỗi yếu cho các phím được nhập thủ công. Ngày nay, không ai kiểm tra tính chẵn lẻ này, và do đó lsbit bị bỏ qua.

Trích xuất từ ​​Poncho's sâu sắc Answer trên Cryptography Stackexchange.

+0

đã nhận được. cảm ơn vì lời giải thích! – reqresp

1

DES có khóa 56 bit, lsbit của mỗi byte khóa được sử dụng một cách bắt đầu cho tính chẵn lẻ, giờ đây nó bị bỏ qua.

Câu trả lời là: không sử dụng DES! DES không an toàn và đã bị vượt trội bởi AES (Chuẩn mã hóa nâng cao) AES được thiết kế đặc biệt để thay thế DES.

Không nên sử dụng chuỗi ký tự làm khóa, cách tốt nhất là lấy khóa mã hóa từ chuỗi ký tự bằng hàm như PBKDF2 (Chức năng dẫn xuất khóa dựa trên mật khẩu).

+0

đã nhận được. cảm ơn vì lời giải thích! – reqresp

Các vấn đề liên quan