В данной статье говорится о том, как осуществить шифрование с помощью алгоритма AES-256 на Java.
Как уже говорилось ранее, AES-256 представляет собой алгоритм шифрования с симметричным ключом. Это означает, что для шифрования и дешифрования используется один и тот же криптографический ключ.
Алгоритм используется в военных целях, используется для шифрования передачи сообщений по открытым каналам, для шифрования файлов. Так же с помощью AES-256 злоумышленники шифруют файлы на компьютерах «жертв», используя его в целях вымогательства денег (программы-вымогатели). Расшифровать данные возможно только лишь зная ключ.
Как зашифровать и дешифровать с помощью AES-256 средствами Java
Класс для шифрования и дешифрования потока байт на Java может выглядеть следующим образом:
package Crypto; import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; import java.security.NoSuchAlgorithmException; /** * Created by administrator on 16.03.2018. */ public class Aes256Class { private SecretKey secretKey; public Aes256Class() { try { KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); keyGenerator.init(256); this.secretKey = keyGenerator.generateKey(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } } public byte[] makeAes(byte[] rawMessage, int cipherMode){ try { Cipher cipher = Cipher.getInstance("AES"); cipher.init(cipherMode, this.secretKey); byte [] output = cipher.doFinal(rawMessage); return output; } catch (Exception e){ e.printStackTrace(); return null; } } }
В конструкторе Aes256Class() инициализируется объект keyGenerator
, которому задается размер ключа 256 бит. Далее с помощью функции generateKey()
генерируется случайный ключ с заданным размером (256 бит).
В функцию makeAes
передается исходный массив байт и режим работы объекта класса Cipher:
Cipher.ENCRYPT_MODE либо Cipher.DECRYPT_MODE.
В классе с функцией main можем вызвать описанные выше методы следующим образом:
package Main; import Crypto.Aes256Class; import javax.crypto.Cipher; /** * Created by administrator on 16.03.2018. */ public class Main { public static void main(String[] args) { Aes256Class aes256 = new Aes256Class(); String mes = "Hello"; for (int i = 0; i < 3; i++) { byte[] shifr = aes256.makeAes(mes.getBytes(), Cipher.ENCRYPT_MODE); System.out.println(new String(shifr)); byte[] src = aes256.makeAes(shifr, Cipher.DECRYPT_MODE); System.out.println(new String(src)); } } }
Получаем следующий результат:
Объясняю, зачем я зациклил выполнение функции makeAes.
Благодаря шифрованию и дешифрованию одного и того же текста несколько раз, можно увидеть, что шифр получается один и тот же, что не есть хорошо.
Это уже может дать злоумышленнику какую-то информацию. Представьте себе, что ваша система отправляет в канал сообщение, например, «Опасность». И отправляет его 10 раз подряд.
Если каким-то образом сеть будет прослушиваться, перехваченное сообщение будет иметь 10 раз подряд один и тот же шифр.
Для того, чтобы одно и то же сообщение имело разный шифр, можно использовать соль.
Не смотря на то, что AES-256 не поддерживает концепцию соли, ничего не мешает сгенерировать случайный массив байт из, например, 8 элементов (получим 8 байтовую соль) и добавить к сообщению.
При дешифрации сообщения можно просто выбросить эти самые последние 8 байт.
Функция приобретет следующий вид:
public byte[] makeAes(byte[] rawMessage, int cipherMode){ try { Cipher cipher = Cipher.getInstance("AES"); cipher.init(cipherMode, this.secretKey); byte [] output = cipher.doFinal(rawMessage); if(cipherMode == Cipher.DECRYPT_MODE){ byte[]result = new byte[output.length-8]; //Выбрасываем последние 8 байт System.arraycopy(output, 0, result, 0, output.length-8); return result; } return output; } catch (Exception e){ e.printStackTrace(); return null; } }
Вызываем функцию makeAes в методе main:
package Main; import Crypto.Aes256Class; import javax.crypto.Cipher; import java.security.SecureRandom; /** * Created by administrator on 16.03.2018. */ public class Main { public static void main(String[] args) { Aes256Class aes256 = new Aes256Class(); //Массив для соли byte[] salt = new byte[8]; //Исходное сообщение String mes = "Опасность"; //Выполним операцию 10 раз for (int i = 0; i < 10; i++) { //Генерация соли SecureRandom random = new SecureRandom(); random.nextBytes(salt); //Преобразуем исходный текст в поток байт и добавим полученную соль byte[]srcMessage = mes.getBytes(); byte[]fullsrcMessage = new byte[srcMessage.length+8]; System.arraycopy(srcMessage, 0, fullsrcMessage, 0, srcMessage.length); System.arraycopy(salt, 0, fullsrcMessage, srcMessage.length, salt.length); //Шифруем byte[] shifr = aes256.makeAes(fullsrcMessage, Cipher.ENCRYPT_MODE); System.out.println(new String(shifr)); //Дешифруем byte[] src = aes256.makeAes(shifr, Cipher.DECRYPT_MODE); System.out.println(new String(src)); } } }
В этом примере шифруем и дешифруем 10 раз сообщение «Опасность». Получаем следующий результат:
Как видим, одно и то же сообщение, но шифр разный. Однако здесь есть нюанс, если обратите внимание, то увидите, что первая часть сообщения всегда одна и та же. Это и имелось ввиду, когда говорилось, что AES не поддерживает концепцию соли.
Можете попробовать прикреплять соль к началу сообщения, к середине, или использовать несколько солей. Однако Принцип Керкгоффса подразумевает, что злоумышленнику известен алгоритм шифрования и какую-то закономерность он все же найдет.
Нужна очень «хитрая» реализация алгоритма, задача которой максимально запутать злоумышленника.
Когда я писал статью о реализации данного алгоритма на C# я положился на реализацию AES от Microsoft.
Реализация AES-256 на Java с помощью фреймворка Spring Security
В данной статье приведу реализацию AES с помощью Java фреймворка Spring Security:
package Main; import org.springframework.security.crypto.encrypt.Encryptors; import org.springframework.security.crypto.encrypt.TextEncryptor; import org.springframework.security.crypto.keygen.KeyGenerators; /** * Created by administrator on 16.03.2018. */ public class Main { public static void main(String[] args) { final String password = "Here is the password"; String textToEncrypt = "Hello"; for (int i = 0; i < 10; i++) { String salt = KeyGenerators.string().generateKey(); TextEncryptor encryptor = Encryptors.text(password, salt); String cipherText = encryptor.encrypt(textToEncrypt); String decryptedText = encryptor.decrypt(cipherText); System.out.println("Src: " + textToEncrypt); System.out.println("Cipher: " + cipherText); System.out.println("Decrypted: " + decryptedText); System.out.println("__________________"); } } }
Результат работы кода:
Как видите, реализация AES-256 от Spring Security не подводит. Как знать, что это именно AES? В официальной документации есть такое упоминание:
The "standard" encryption method is 256-bit AES
(Spring Crypto Module)
P.S. Расшифровать подобные сообщения относительно разумными средствами (взломать шифр), не имея ключа в настоящее время не представляется возможным.
Можете попробовать перебором паролей, если у вас в запасе есть сотни миллиардов лет.
P.P.S. Так же на сайте имеется запись о шифровании данных с помощью AES-256 на C#.