Шифрование и дешифрование с использованием Java: невозможно получить одинаковый вывод
Я пытаюсь изучить и проверить API шифрования / дешифрования Java 1.6. Я хочу знать, что я делаю неправильно и чего мне не хватает в плане знаний.
В приведенном ниже коде я создаю два шифра: один для шифрования, а другой для дешифрования. Когда я использую эти шифры, я инициализирую их с другими секретными ключами, но я все еще могу вернуть то же значение. Почему это?
String algorithm = "DES";
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(algorithm);
byte[] encBytes = "12345678".getBytes("UTF8");
byte[] decBytes = "56781234".getBytes("UTF8");
DESKeySpec keySpecEncrypt = new DESKeySpec(encBytes);
DESKeySpec keySpecDecrypt = new DESKeySpec(decBytes);
SecretKey keyEncrypt = keyFactory.generateSecret(keySpecEncrypt);
SecretKey keyDecrypt = keyFactory.generateSecret(keySpecDecrypt);
Cipher cipherEncrypt = Cipher.getInstance(algorithm);
Cipher cipherDecrypt = Cipher.getInstance(algorithm);
String input = "john doe";
cipherEncrypt.init(Cipher.ENCRYPT_MODE, keyEncrypt);
byte[] inputBytes = cipherEncrypt.doFinal(input.getBytes());
System.out.println("inputBytes: " + new String(inputBytes));
cipherDecrypt.init(Cipher.DECRYPT_MODE, keyDecrypt);
byte[] outputBytes = cipherDecrypt.doFinal(inputBytes);
System.out.println("outputBytes: " + new String(outputBytes));
3 ответа
Вот описание из документа JDK:
DESKeySpec открытый DESKeySpec(ключ byte[]) выдает InvalidKeyException Создает объект DESKeySpec, используя первые 8 байтов ключа как материал ключа для ключа DES. Байты, составляющие ключ DES, это байты между ключом [0] и ключом [7] включительно.
DESKeySpec использует только первые 8 байтов байта [] в качестве ключа. Таким образом, используемые ключи в вашем примере идентичны.
Добро пожаловать в шифрование! Как уже упоминалось, DES является симметричным и требует того же ключа для шифрования, что и для дешифрования. Этот ключ должен соответствовать правильному количеству бит для используемого вами шифра. Для DES это 56 бит. Прежде чем вы зайдете слишком далеко с этим, вот несколько вещей, которые вы могли бы рассмотреть:
- Вы должны использовать более строгий стандарт шифрования, такой как AES. Теперь можно сломать шифрование DES.
- Если вы хотите использовать строку в качестве ключа, тогда вам следует использовать сильную хеш-функцию, такую как SHA-256, против этой ключевой строки. Затем возьмите столько битов из этого вывода хеша, сколько вам нужно для ключа шифрования, 128-бит достаточно для AES. Ваша ключевая строка должна быть длинной, как у вас.
- Будет лучше использовать режим блочного шифра, который не генерирует один и тот же выход каждый раз для одного и того же ввода. Посмотрите режимы работы блочного шифра для информации и визуализации того, почему режим ECB плох.
Вот рабочий пример использования 128-битного шифрования AES в режиме CBC с заполнением PKCS #5:
import java.security.MessageDigest;
import java.security.SecureRandom;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class EncryptDecrypt {
public static void main(String[] args) throws Exception {
// here are your inputs
String keyString = "averylongtext!@$@#$#@$#*&(*&}{23432432432dsfsdf";
String input = "john doe";
// setup AES cipher in CBC mode with PKCS #5 padding
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
// setup an IV (initialization vector) that should be
// randomly generated for each input that's encrypted
byte[] iv = new byte[cipher.getBlockSize()];
new SecureRandom().nextBytes(iv);
IvParameterSpec ivSpec = new IvParameterSpec(iv);
// hash keyString with SHA-256 and crop the output to 128-bit for key
MessageDigest digest = MessageDigest.getInstance("SHA-256");
digest.update(keyString.getBytes());
byte[] key = new byte[16];
System.arraycopy(digest.digest(), 0, key, 0, key.length);
SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
// encrypt
cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
byte[] encrypted = cipher.doFinal(input.getBytes("UTF-8"));
System.out.println("encrypted: " + new String(encrypted));
// include the IV with the encrypted bytes for transport, you'll
// need the same IV when decrypting (it's safe to send unencrypted)
// decrypt
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
byte[] decrypted = cipher.doFinal(encrypted);
System.out.println("decrypted: " + new String(decrypted, "UTF-8"));
}
}
Вот рабочий пример использования 56-битного шифрования DES.
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
public class CipherHelper {
// Algorithm used
private final static String ALGORITHM = "DES";
/**
* Encrypt data
* @param secretKey - a secret key used for encryption
* @param data - data to encrypt
* @return Encrypted data
* @throws Exception
*/
public static String cipher(String secretKey, String data) throws Exception {
// Key has to be of length 8
if (secretKey == null || secretKey.length() != 8)
throw new Exception("Invalid key length - 8 bytes key needed!");
SecretKey key = new SecretKeySpec(secretKey.getBytes(), ALGORITHM);
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, key);
return toHex(cipher.doFinal(data.getBytes()));
}
/**
* Decrypt data
* @param secretKey - a secret key used for decryption
* @param data - data to decrypt
* @return Decrypted data
* @throws Exception
*/
public static String decipher(String secretKey, String data) throws Exception {
// Key has to be of length 8
if (secretKey == null || secretKey.length() != 8)
throw new Exception("Invalid key length - 8 bytes key needed!");
SecretKey key = new SecretKeySpec(secretKey.getBytes(), ALGORITHM);
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, key);
return new String(cipher.doFinal(toByte(data)));
}
// Helper methods
private static byte[] toByte(String hexString) {
int len = hexString.length()/2;
byte[] result = new byte[len];
for (int i = 0; i < len; i++)
result[i] = Integer.valueOf(hexString.substring(2*i, 2*i+2), 16).byteValue();
return result;
}
public static String toHex(byte[] stringBytes) {
StringBuffer result = new StringBuffer(2*stringBytes.length);
for (int i = 0; i < stringBytes.length; i++) {
result.append(HEX.charAt((stringBytes[i]>>4)&0x0f)).append(HEX.charAt(stringBytes[i]&0x0f));
}
return result.toString();
}
private final static String HEX = "0123456789ABCDEF";
// Helper methods - end
/**
* Quick test
* @param args
*/
public static void main(String[] args) {
try {
String secretKey = "01234567";
String data="test";
String encryptedData = cipher(secretKey, data);
System.out.println("encryptedData: " + encryptedData);
String decryptedData = decipher(secretKey, encryptedData);
System.out.println("decryptedData: " + decryptedData);
} catch (Exception e) {
e.printStackTrace();
}
}
}