Strange DES behavior - decryption is performed using different keys

Sometimes I come across an interesting, strange thing: the same block of ciphertext can be decrypted using several different keys!

Can anyone tell me what is going wrong? Thank you very much.

Please do not try to let me switch to triple DES / AES, etc., I just want to know where the problem is: the way to call the Java SDK or an error in the Java SDK?

The following is displayed on Windows 7, the same in the Linux window:

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

Source:

 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 = "0123456789abcdef".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; } } 
+5
source share
2 answers

The DES operation (both encryption and decryption) ignores the lsbit of each key byte. That is, if you flip any of the lsbits inside the key, the operation will remain the same. What happens in the keys you tried: the ASCII code for the space is 0x20, and the ASCII code for! 0x21; they differ only in lsbit. So, if the key has a byte with an ASCII code for a space, you can replace it with !, And it can still decrypt. Similarly, the ASCII code for * is 0x2a, while the ASCII code for + is 0x2b; also differs only in lsbit.

In the original DES standard, lsbit was to be used as a parity bit (each byte always had odd parity). This was supposed to be a small error check for manually entered keys. No one currently does this parity check, and therefore lsbit is ignored.

Extracted from Poncho's Insightful Answer to Cryptography Stackexchange .

+4
source

DES has a 56-bit key, the lsbit of each key byte was originally used for parity, now it is ignored.

Answer: do not use DES! DES is unsafe and has been surpassed by AES (Advanced Encryption Standard). AES was specifically designed to replace DES.

In addition, you should not use a character string as a key; it is best practice to obtain an encryption key from a character string using a function such as PBKDF2 (password-based key determination function).

+1
source

All Articles