Java 256-битное шифрование на основе AES на основе паролей

Мне нужно реализовать 256-битное шифрование AES, но все примеры, которые я нашел в Интернете, используют «KeyGenerator» для генерации 256-битного ключа, но я бы хотел использовать свой собственный ключ доступа. Как я могу создать свой собственный ключ? Я попытался заполнить его до 256 бит, но потом я получаю сообщение об ошибке, что ключ слишком длинный. У меня есть установленный неограниченный патч юрисдикции, так что это не проблема 🙂

То есть. KeyGenerator выглядит так …

// Get the KeyGenerator KeyGenerator kgen = KeyGenerator.getInstance("AES"); kgen.init(128); // 192 and 256 bits may not be available // Generate the secret key specs. SecretKey skey = kgen.generateKey(); byte[] raw = skey.getEncoded(); 

Код, взятый отсюда

РЕДАКТИРОВАТЬ

Я действительно заполнял пароль до 256 байтов, а не бит, который слишком длинный. Ниже приведен некоторый код, который я использую сейчас, когда у меня есть некоторый опыт.

 byte[] key = null; // TODO byte[] input = null; // TODO byte[] output = null; SecretKeySpec keySpec = null; keySpec = new SecretKeySpec(key, "AES"); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding"); cipher.init(Cipher.ENCRYPT_MODE, keySpec); output = cipher.doFinal(input) 

Биты «TODO» вам нужно сделать сами 🙂

Поделитесь password ( char[] ) и salt ( byte[] -8 байт, выбранный SecureRandom делает хорошую соль, которая не должна храниться в секрете) с получателем вне диапазона. Затем, чтобы получить хороший ключ от этой информации:

 /* Derive the key, given password and salt. */ SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256"); KeySpec spec = new PBEKeySpec(password, salt, 65536, 256); SecretKey tmp = factory.generateSecret(spec); SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES"); 

Магические числа (которые могут быть определены как константы где-то) 65536 и 256 – это число итераций ключевого деривации и размер ключа соответственно.

Функция деривации ключа повторяется, чтобы потребовать значительных вычислительных усилий и которая не позволяет злоумышленникам быстро пробовать много разных паролей. Счетчик итераций может быть изменен в зависимости от доступных вычислительных ресурсов.

Размер ключа может быть уменьшен до 128 бит, который по-прежнему считается «сильным» шифрованием, но он не дает значительного запаса прочности, если обнаружены атаки, которые ослабляют AES.

Используемый в правильном режиме блочной цепочки один и тот же производный ключ может использоваться для шифрования многих сообщений. В CBC для каждого сообщения генерируется случайный вектор инициализации (IV), который дает другой текст шифрования, даже если простой текст идентичен. CBC не может быть наиболее безопасным для вас способом (см. Ниже AEAD); существует много других режимов с различными свойствами безопасности, но все они используют подобный случайный ввод. В любом случае выходы каждой операции шифрования являются шифровым текстом и вектором инициализации:

 /* Encrypt the message. */ Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); cipher.init(Cipher.ENCRYPT_MODE, secret); AlgorithmParameters params = cipher.getParameters(); byte[] iv = params.getParameterSpec(IvParameterSpec.class).getIV(); byte[] ciphertext = cipher.doFinal("Hello, World!".getBytes("UTF-8")); 

Храните ciphertext и iv . При дешифровании SecretKey регенерируется точно так же, используя пароль с теми же параметрами соли и итерации. Инициализируйте шифр с помощью этого ключа и вектора инициализации, сохраненного с сообщением:

 /* Decrypt the message, given derived key and initialization vector. */ Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(iv)); String plaintext = new String(cipher.doFinal(ciphertext), "UTF-8"); System.out.println(plaintext); 

Java 7 включала поддержку API для режимов шифрования AEAD, а поставщик «SunJCE», включенный в дистрибутивы OpenJDK и Oracle, реализует их, начиная с Java 8. Один из этих режимов настоятельно рекомендуется вместо CBC; он будет защищать целостность данных, а также их конфиденциальность.


java.security.InvalidKeyException с сообщением «Недопустимый размер ключа или параметры по умолчанию» означает, что сила криптографии ограничена; файлы политики неограниченной силы не находятся в правильном месте. В JDK они должны быть помещены под ${jdk}/jre/lib/security

Основываясь на описании проблемы, похоже, что файлы политики установлены неправильно. Системы могут легко иметь несколько сеансов Java; дважды проверьте, чтобы убедиться, что используется правильное местоположение.

Рассмотрите возможность использования криптографического модуля Spring Security Crypto Module

Модуль Spring Security Crypto обеспечивает поддержку симметричного шифрования, генерации ключей и кодирования паролей. Код распространяется как часть основного модуля, но не имеет никаких зависимостей от какого-либо другого кода Spring Security (или Spring).

Он обеспечивает простую абстракцию для шифрования и, похоже, соответствует тому, что требуется здесь,

«Стандартный» метод шифрования – это 256-разрядный AES с использованием PKCS № 5 PBKDF2 (функция определения ключа на основе пароля № 2). Этот метод требует Java 6. Пароль, используемый для создания SecretKey, должен храниться в безопасном месте и не использоваться совместно. Соль используется для предотвращения атаки словаря против ключа в случае взлома ваших зашифрованных данных. Также применяется 16-разрядный случайный вектор инициализации, поэтому каждое зашифрованное сообщение является уникальным.

Взгляд на внутренности показывает структуру, подобную ответу Эриксона .

Как отмечалось в этом вопросе, для этого также требуется политика Jurisdiction с InvalidKeyException: Illegal Key Size Java (JCE) (иначе вы столкнетесь с InvalidKeyException: Illegal Key Size ). Он загружается для Java 6 , Java 7 и Java 8 .

Пример использования

 import org.springframework.security.crypto.encrypt.Encryptors; import org.springframework.security.crypto.encrypt.TextEncryptor; import org.springframework.security.crypto.keygen.KeyGenerators; public class CryptoExample { public static void main(String[] args) { final String password = "I AM SHERLOCKED"; final String salt = KeyGenerators.string().generateKey(); TextEncryptor encryptor = Encryptors.text(password, salt); System.out.println("Salt: \"" + salt + "\""); String textToEncrypt = "*royal secrets*"; System.out.println("Original text: \"" + textToEncrypt + "\""); String encryptedText = encryptor.encrypt(textToEncrypt); System.out.println("Encrypted text: \"" + encryptedText + "\""); // Could reuse encryptor but wanted to show reconstructing TextEncryptor TextEncryptor decryptor = Encryptors.text(password, salt); String decryptedText = decryptor.decrypt(encryptedText); System.out.println("Decrypted text: \"" + decryptedText + "\""); if(textToEncrypt.equals(decryptedText)) { System.out.println("Success: decrypted text matches"); } else { System.out.println("Failed: decrypted text does not match"); } } } 

И выбор образца,

 Соль: "feacbc02a3a697b0"
 Первоначальный текст: "* королевские секреты *"
 Зашифрованный текст: "7c73c5a83fa580b5d6f8208768adc931ef3123291ac8bc335a1277a39d256d9a" 
 Расшифрованный текст: * * королевские секреты *
 Успех: разграниченные текстовые совпадения

Прочитав советы Эриксона и извлеки все, что мог, из пары других сообщений и этого примера, я попытался обновить код Дуга с рекомендованными изменениями. Не стесняйтесь редактировать, чтобы сделать его лучше.

  • Вектор инициализации больше не фиксируется
  • ключ шифрования выводится с использованием кода от erickson
  • 8-байтовая соль генерируется в setupEncrypt () с использованием SecureRandom ()
  • ключ расшифровки генерируется из соли шифрования и пароля
  • шифрование дешифрования генерируется из ключа дешифрования и вектора инициализации
  • удаленное шестнадцатеричное скручивание вместо org.apache.commons codec Шестигранные процедуры

Некоторые примечания: для этого используется 128-битный ключ шифрования – java, по-видимому, не будет делать 256-битное шифрование из коробки. Для реализации 256 требуется установить некоторые дополнительные файлы в каталог установки java.

Кроме того, я не криптозащитник. Прислушайтесь.

 import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.security.AlgorithmParameters; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.security.spec.InvalidKeySpecException; import java.security.spec.InvalidParameterSpecException; import java.security.spec.KeySpec; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.CipherInputStream; import javax.crypto.CipherOutputStream; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import javax.crypto.SecretKey; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.PBEKeySpec; import javax.crypto.spec.SecretKeySpec; import org.apache.commons.codec.DecoderException; import org.apache.commons.codec.binary.Hex; public class Crypto { String mPassword = null; public final static int SALT_LEN = 8; byte [] mInitVec = null; byte [] mSalt = null; Cipher mEcipher = null; Cipher mDecipher = null; private final int KEYLEN_BITS = 128; // see notes below where this is used. private final int ITERATIONS = 65536; private final int MAX_FILE_BUF = 1024; /** * create an object with just the passphrase from the user. Don't do anything else yet * @param password */ public Crypto (String password) { mPassword = password; } /** * return the generated salt for this object * @return */ public byte [] getSalt () { return (mSalt); } /** * return the initialization vector created from setupEncryption * @return */ public byte [] getInitVec () { return (mInitVec); } /** * debug/print messages * @param msg */ private void Db (String msg) { System.out.println ("** Crypt ** " + msg); } /** * this must be called after creating the initial Crypto object. It creates a salt of SALT_LEN bytes * and generates the salt bytes using secureRandom(). The encryption secret key is created * along with the initialization vectory. The member variable mEcipher is created to be used * by the class later on when either creating a CipherOutputStream, or encrypting a buffer * to be written to disk. * * @throws NoSuchAlgorithmException * @throws InvalidKeySpecException * @throws NoSuchPaddingException * @throws InvalidParameterSpecException * @throws IllegalBlockSizeException * @throws BadPaddingException * @throws UnsupportedEncodingException * @throws InvalidKeyException */ public void setupEncrypt () throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidParameterSpecException, IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException, InvalidKeyException { SecretKeyFactory factory = null; SecretKey tmp = null; // crate secureRandom salt and store as member var for later use mSalt = new byte [SALT_LEN]; SecureRandom rnd = new SecureRandom (); rnd.nextBytes (mSalt); Db ("generated salt :" + Hex.encodeHexString (mSalt)); factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); /* Derive the key, given password and salt. * * in order to do 256 bit crypto, you have to muck with the files for Java's "unlimted security" * The end user must also install them (not compiled in) so beware. * see here: http://www.javamex.com/tutorials/cryptography/unrestricted_policy_files.shtml */ KeySpec spec = new PBEKeySpec (mPassword.toCharArray (), mSalt, ITERATIONS, KEYLEN_BITS); tmp = factory.generateSecret (spec); SecretKey secret = new SecretKeySpec (tmp.getEncoded(), "AES"); /* Create the Encryption cipher object and store as a member variable */ mEcipher = Cipher.getInstance ("AES/CBC/PKCS5Padding"); mEcipher.init (Cipher.ENCRYPT_MODE, secret); AlgorithmParameters params = mEcipher.getParameters (); // get the initialization vectory and store as member var mInitVec = params.getParameterSpec (IvParameterSpec.class).getIV(); Db ("mInitVec is :" + Hex.encodeHexString (mInitVec)); } /** * If a file is being decrypted, we need to know the pasword, the salt and the initialization vector (iv). * We have the password from initializing the class. pass the iv and salt here which is * obtained when encrypting the file initially. * * @param initvec * @param salt * @throws NoSuchAlgorithmException * @throws InvalidKeySpecException * @throws NoSuchPaddingException * @throws InvalidKeyException * @throws InvalidAlgorithmParameterException * @throws DecoderException */ public void setupDecrypt (String initvec, String salt) throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, DecoderException { SecretKeyFactory factory = null; SecretKey tmp = null; SecretKey secret = null; // since we pass it as a string of input, convert to a actual byte buffer here mSalt = Hex.decodeHex (salt.toCharArray ()); Db ("got salt " + Hex.encodeHexString (mSalt)); // get initialization vector from passed string mInitVec = Hex.decodeHex (initvec.toCharArray ()); Db ("got initvector :" + Hex.encodeHexString (mInitVec)); /* Derive the key, given password and salt. */ // in order to do 256 bit crypto, you have to muck with the files for Java's "unlimted security" // The end user must also install them (not compiled in) so beware. // see here: // http://www.javamex.com/tutorials/cryptography/unrestricted_policy_files.shtml factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); KeySpec spec = new PBEKeySpec(mPassword.toCharArray (), mSalt, ITERATIONS, KEYLEN_BITS); tmp = factory.generateSecret(spec); secret = new SecretKeySpec(tmp.getEncoded(), "AES"); /* Decrypt the message, given derived key and initialization vector. */ mDecipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); mDecipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(mInitVec)); } /** * This is where we write out the actual encrypted data to disk using the Cipher created in setupEncrypt(). * Pass two file objects representing the actual input (cleartext) and output file to be encrypted. * * there may be a way to write a cleartext header to the encrypted file containing the salt, but I ran * into uncertain problems with that. * * @param input - the cleartext file to be encrypted * @param output - the encrypted data file * @throws IOException * @throws IllegalBlockSizeException * @throws BadPaddingException */ public void WriteEncryptedFile (File input, File output) throws IOException, IllegalBlockSizeException, BadPaddingException { FileInputStream fin; FileOutputStream fout; long totalread = 0; int nread = 0; byte [] inbuf = new byte [MAX_FILE_BUF]; fout = new FileOutputStream (output); fin = new FileInputStream (input); while ((nread = fin.read (inbuf)) > 0 ) { Db ("read " + nread + " bytes"); totalread += nread; // create a buffer to write with the exact number of bytes read. Otherwise a short read fills inbuf with 0x0 // and results in full blocks of MAX_FILE_BUF being written. byte [] trimbuf = new byte [nread]; for (int i = 0; i < nread; i++) trimbuf[i] = inbuf[i]; // encrypt the buffer using the cipher obtained previosly byte [] tmp = mEcipher.update (trimbuf); // I don't think this should happen, but just in case.. if (tmp != null) fout.write (tmp); } // finalize the encryption since we've done it in blocks of MAX_FILE_BUF byte [] finalbuf = mEcipher.doFinal (); if (finalbuf != null) fout.write (finalbuf); fout.flush(); fin.close(); fout.close(); fout.close (); Db ("wrote " + totalread + " encrypted bytes"); } /** * Read from the encrypted file (input) and turn the cipher back into cleartext. Write the cleartext buffer back out * to disk as (output) File. * * I left CipherInputStream in here as a test to see if I could mix it with the update() and final() methods of encrypting * and still have a correctly decrypted file in the end. Seems to work so left it in. * * @param input - File object representing encrypted data on disk * @param output - File object of cleartext data to write out after decrypting * @throws IllegalBlockSizeException * @throws BadPaddingException * @throws IOException */ public void ReadEncryptedFile (File input, File output) throws IllegalBlockSizeException, BadPaddingException, IOException { FileInputStream fin; FileOutputStream fout; CipherInputStream cin; long totalread = 0; int nread = 0; byte [] inbuf = new byte [MAX_FILE_BUF]; fout = new FileOutputStream (output); fin = new FileInputStream (input); // creating a decoding stream from the FileInputStream above using the cipher created from setupDecrypt() cin = new CipherInputStream (fin, mDecipher); while ((nread = cin.read (inbuf)) > 0 ) { Db ("read " + nread + " bytes"); totalread += nread; // create a buffer to write with the exact number of bytes read. Otherwise a short read fills inbuf with 0x0 byte [] trimbuf = new byte [nread]; for (int i = 0; i < nread; i++) trimbuf[i] = inbuf[i]; // write out the size-adjusted buffer fout.write (trimbuf); } fout.flush(); cin.close(); fin.close (); fout.close(); Db ("wrote " + totalread + " encrypted bytes"); } /** * adding main() for usage demonstration. With member vars, some of the locals would not be needed */ public static void main(String [] args) { // create the input.txt file in the current directory before continuing File input = new File ("input.txt"); File eoutput = new File ("encrypted.aes"); File doutput = new File ("decrypted.txt"); String iv = null; String salt = null; Crypto en = new Crypto ("mypassword"); /* * setup encryption cipher using password. print out iv and salt */ try { en.setupEncrypt (); iv = Hex.encodeHexString (en.getInitVec ()).toUpperCase (); salt = Hex.encodeHexString (en.getSalt ()).toUpperCase (); } catch (InvalidKeyException e) { e.printStackTrace(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (InvalidKeySpecException e) { e.printStackTrace(); } catch (NoSuchPaddingException e) { e.printStackTrace(); } catch (InvalidParameterSpecException e) { e.printStackTrace(); } catch (IllegalBlockSizeException e) { e.printStackTrace(); } catch (BadPaddingException e) { e.printStackTrace(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } /* * write out encrypted file */ try { en.WriteEncryptedFile (input, eoutput); System.out.printf ("File encrypted to " + eoutput.getName () + "\niv:" + iv + "\nsalt:" + salt + "\n\n"); } catch (IllegalBlockSizeException e) { e.printStackTrace(); } catch (BadPaddingException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } /* * decrypt file */ Crypto dc = new Crypto ("mypassword"); try { dc.setupDecrypt (iv, salt); } catch (InvalidKeyException e) { e.printStackTrace(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (InvalidKeySpecException e) { e.printStackTrace(); } catch (NoSuchPaddingException e) { e.printStackTrace(); } catch (InvalidAlgorithmParameterException e) { e.printStackTrace(); } catch (DecoderException e) { e.printStackTrace(); } /* * write out decrypted file */ try { dc.ReadEncryptedFile (eoutput, doutput); System.out.println ("decryption finished to " + doutput.getName ()); } catch (IllegalBlockSizeException e) { e.printStackTrace(); } catch (BadPaddingException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } } 

Я реализовал ответ Эриксона в действительно простом classе:
Класс AES 256-битного шифрования / дешифрования

Если вы получите java.security.InvalidKeyException вам необходимо установить файлы политики политики неограниченной прочности Java Cryptography Extension (JCE):

  • Ссылка Java 6
  • Ссылка Java 7

Просто поместите банки в свой {JDK HOME}\jre\lib\security

Создание собственного ключа из массива байтов очень просто:

 byte[] raw = ...; // 32 bytes in size for a 256 bit key Key skey = new javax.crypto.spec.SecretKeySpec(raw, "AES"); 

Но создать 256-битный ключ недостаточно. Если генератор ключей не может генерировать 256-битные ключи для вас, то class Cipher вероятно, также не поддерживает 256-битный AES. Вы говорите, что у вас установлен патч неограниченной юрисдикции, поэтому должен поддерживаться шифр AES-256 (но тогда должны быть и 256-битные ключи, поэтому это может быть проблема конфигурации).

 Cipher cipher = Cipher.getInstance("AES"); cipher.init(Cipher.ENCRYPT_MODE, skey); byte[] encrypted = cipher.doFinal(plainText.getBytes()); 

Обходной путь из-за отсутствия поддержки AES-256 заключается в том, чтобы взять некоторую свободно доступную реализацию AES-256 и использовать ее как пользовательский поставщик. Это предполагает создание собственного Cipher.getInstance(String, Provider) Provider и использование его с помощью Cipher.getInstance(String, Provider) . Но это может быть вовлеченный процесс.

То, что я делал в прошлом, это hash-ключ с помощью чего-то вроде SHA256, а затем извлечение байтов из hashа в байт ключа [].

После того, как у вас есть свой байт [], вы можете просто сделать:

 SecretKeySpec key = new SecretKeySpec(keyBytes, "AES"); Cipher cipher = Cipher.getInstance("AES"); cipher.init(Cipher.ENCRYPT_MODE, key); byte[] encryptedBytes = cipher.doFinal(clearText.getBytes()); 

Добавляя к редактированию @ Wufoo, следующая версия использует InputStreams вместо файлов, чтобы упростить работу с различными файлами. Он также сохраняет IV и Соль в начале файла, делая так, чтобы отслеживался только пароль. Поскольку IV и Соль не должны быть секретными, это облегчает жизнь.

 import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.security.AlgorithmParameters; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.security.spec.InvalidKeySpecException; import java.security.spec.InvalidParameterSpecException; import java.security.spec.KeySpec; import java.util.logging.Level; import java.util.logging.Logger; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.CipherInputStream; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import javax.crypto.SecretKey; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.PBEKeySpec; import javax.crypto.spec.SecretKeySpec; public class AES { public final static int SALT_LEN = 8; static final String HEXES = "0123456789ABCDEF"; String mPassword = null; byte[] mInitVec = null; byte[] mSalt = new byte[SALT_LEN]; Cipher mEcipher = null; Cipher mDecipher = null; private final int KEYLEN_BITS = 128; // see notes below where this is used. private final int ITERATIONS = 65536; private final int MAX_FILE_BUF = 1024; /** * create an object with just the passphrase from the user. Don't do anything else yet * @param password */ public AES(String password) { mPassword = password; } public static String byteToHex(byte[] raw) { if (raw == null) { return null; } final StringBuilder hex = new StringBuilder(2 * raw.length); for (final byte b : raw) { hex.append(HEXES.charAt((b & 0xF0) >> 4)).append(HEXES.charAt((b & 0x0F))); } return hex.toString(); } public static byte[] hexToByte(String hexString) { int len = hexString.length(); byte[] ba = new byte[len / 2]; for (int i = 0; i < len; i += 2) { ba[i / 2] = (byte) ((Character.digit(hexString.charAt(i), 16) << 4) + Character.digit(hexString.charAt(i + 1), 16)); } return ba; } /** * debug/print messages * @param msg */ private void Db(String msg) { System.out.println("** Crypt ** " + msg); } /** * This is where we write out the actual encrypted data to disk using the Cipher created in setupEncrypt(). * Pass two file objects representing the actual input (cleartext) and output file to be encrypted. * * there may be a way to write a cleartext header to the encrypted file containing the salt, but I ran * into uncertain problems with that. * * @param input - the cleartext file to be encrypted * @param output - the encrypted data file * @throws IOException * @throws IllegalBlockSizeException * @throws BadPaddingException */ public void WriteEncryptedFile(InputStream inputStream, OutputStream outputStream) throws IOException, IllegalBlockSizeException, BadPaddingException { try { long totalread = 0; int nread = 0; byte[] inbuf = new byte[MAX_FILE_BUF]; SecretKeyFactory factory = null; SecretKey tmp = null; // crate secureRandom salt and store as member var for later use mSalt = new byte[SALT_LEN]; SecureRandom rnd = new SecureRandom(); rnd.nextBytes(mSalt); Db("generated salt :" + byteToHex(mSalt)); factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); /* * Derive the key, given password and salt. * * in order to do 256 bit crypto, you have to muck with the files for Java's "unlimted security" * The end user must also install them (not compiled in) so beware. * see here: http://www.javamex.com/tutorials/cryptography/unrestricted_policy_files.shtml */ KeySpec spec = new PBEKeySpec(mPassword.toCharArray(), mSalt, ITERATIONS, KEYLEN_BITS); tmp = factory.generateSecret(spec); SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES"); /* * Create the Encryption cipher object and store as a member variable */ mEcipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); mEcipher.init(Cipher.ENCRYPT_MODE, secret); AlgorithmParameters params = mEcipher.getParameters(); // get the initialization vectory and store as member var mInitVec = params.getParameterSpec(IvParameterSpec.class).getIV(); Db("mInitVec is :" + byteToHex(mInitVec)); outputStream.write(mSalt); outputStream.write(mInitVec); while ((nread = inputStream.read(inbuf)) > 0) { Db("read " + nread + " bytes"); totalread += nread; // create a buffer to write with the exact number of bytes read. Otherwise a short read fills inbuf with 0x0 // and results in full blocks of MAX_FILE_BUF being written. byte[] trimbuf = new byte[nread]; for (int i = 0; i < nread; i++) { trimbuf[i] = inbuf[i]; } // encrypt the buffer using the cipher obtained previosly byte[] tmpBuf = mEcipher.update(trimbuf); // I don't think this should happen, but just in case.. if (tmpBuf != null) { outputStream.write(tmpBuf); } } // finalize the encryption since we've done it in blocks of MAX_FILE_BUF byte[] finalbuf = mEcipher.doFinal(); if (finalbuf != null) { outputStream.write(finalbuf); } outputStream.flush(); inputStream.close(); outputStream.close(); outputStream.close(); Db("wrote " + totalread + " encrypted bytes"); } catch (InvalidKeyException ex) { Logger.getLogger(AES.class.getName()).log(Level.SEVERE, null, ex); } catch (InvalidParameterSpecException ex) { Logger.getLogger(AES.class.getName()).log(Level.SEVERE, null, ex); } catch (NoSuchAlgorithmException ex) { Logger.getLogger(AES.class.getName()).log(Level.SEVERE, null, ex); } catch (NoSuchPaddingException ex) { Logger.getLogger(AES.class.getName()).log(Level.SEVERE, null, ex); } catch (InvalidKeySpecException ex) { Logger.getLogger(AES.class.getName()).log(Level.SEVERE, null, ex); } } /** * Read from the encrypted file (input) and turn the cipher back into cleartext. Write the cleartext buffer back out * to disk as (output) File. * * I left CipherInputStream in here as a test to see if I could mix it with the update() and final() methods of encrypting * and still have a correctly decrypted file in the end. Seems to work so left it in. * * @param input - File object representing encrypted data on disk * @param output - File object of cleartext data to write out after decrypting * @throws IllegalBlockSizeException * @throws BadPaddingException * @throws IOException */ public void ReadEncryptedFile(InputStream inputStream, OutputStream outputStream) throws IllegalBlockSizeException, BadPaddingException, IOException { try { CipherInputStream cin; long totalread = 0; int nread = 0; byte[] inbuf = new byte[MAX_FILE_BUF]; // Read the Salt inputStream.read(this.mSalt); Db("generated salt :" + byteToHex(mSalt)); SecretKeyFactory factory = null; SecretKey tmp = null; SecretKey secret = null; factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); KeySpec spec = new PBEKeySpec(mPassword.toCharArray(), mSalt, ITERATIONS, KEYLEN_BITS); tmp = factory.generateSecret(spec); secret = new SecretKeySpec(tmp.getEncoded(), "AES"); /* Decrypt the message, given derived key and initialization vector. */ mDecipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); // Set the appropriate size for mInitVec by Generating a New One AlgorithmParameters params = mDecipher.getParameters(); mInitVec = params.getParameterSpec(IvParameterSpec.class).getIV(); // Read the old IV from the file to mInitVec now that size is set. inputStream.read(this.mInitVec); Db("mInitVec is :" + byteToHex(mInitVec)); mDecipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(mInitVec)); // creating a decoding stream from the FileInputStream above using the cipher created from setupDecrypt() cin = new CipherInputStream(inputStream, mDecipher); while ((nread = cin.read(inbuf)) > 0) { Db("read " + nread + " bytes"); totalread += nread; // create a buffer to write with the exact number of bytes read. Otherwise a short read fills inbuf with 0x0 byte[] trimbuf = new byte[nread]; for (int i = 0; i < nread; i++) { trimbuf[i] = inbuf[i]; } // write out the size-adjusted buffer outputStream.write(trimbuf); } outputStream.flush(); cin.close(); inputStream.close(); outputStream.close(); Db("wrote " + totalread + " encrypted bytes"); } catch (Exception ex) { Logger.getLogger(AES.class.getName()).log(Level.SEVERE, null, ex); } } /** * adding main() for usage demonstration. With member vars, some of the locals would not be needed */ public static void main(String[] args) { // create the input.txt file in the current directory before continuing File input = new File("input.txt"); File eoutput = new File("encrypted.aes"); File doutput = new File("decrypted.txt"); String iv = null; String salt = null; AES en = new AES("mypassword"); /* * write out encrypted file */ try { en.WriteEncryptedFile(new FileInputStream(input), new FileOutputStream(eoutput)); System.out.printf("File encrypted to " + eoutput.getName() + "\niv:" + iv + "\nsalt:" + salt + "\n\n"); } catch (IllegalBlockSizeException | BadPaddingException | IOException e) { e.printStackTrace(); } /* * decrypt file */ AES dc = new AES("mypassword"); /* * write out decrypted file */ try { dc.ReadEncryptedFile(new FileInputStream(eoutput), new FileOutputStream(doutput)); System.out.println("decryption finished to " + doutput.getName()); } catch (IllegalBlockSizeException | BadPaddingException | IOException e) { e.printStackTrace(); } } } 

Use this class for encryption. Оно работает.

 public class ObjectCrypter { public static byte[] encrypt(byte[] ivBytes, byte[] keyBytes, byte[] mes) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException, IOException { AlgorithmParameterSpec ivSpec = new IvParameterSpec(ivBytes); SecretKeySpec newKey = new SecretKeySpec(keyBytes, "AES"); Cipher cipher = null; cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); cipher.init(Cipher.ENCRYPT_MODE, newKey, ivSpec); return cipher.doFinal(mes); } public static byte[] decrypt(byte[] ivBytes, byte[] keyBytes, byte[] bytes) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException, IOException, ClassNotFoundException { AlgorithmParameterSpec ivSpec = new IvParameterSpec(ivBytes); SecretKeySpec newKey = new SecretKeySpec(keyBytes, "AES"); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); cipher.init(Cipher.DECRYPT_MODE, newKey, ivSpec); return cipher.doFinal(bytes); } 

}

And these are ivBytes and a random key;

 String key = "e8ffc7e56311679f12b6fc91aa77a5eb"; byte[] ivBytes = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; keyBytes = key.getBytes("UTF-8"); 

Consider using Encryptor4j

First make sure you have Unlimited Strength Jurisdiction Policy files installed before your proceed so that you can use 256-bit AES keys.

Then do the following:

 String password = "mysupersecretpassword"; Key key = KeyFactory.AES.keyFromPassword(password.toCharArray()); Encryptor encryptor = new Encryptor(key, "AES/CBC/PKCS7Padding", 16); 

You can now use the encryptor to encrypt your message. You can also perform streaming encryption if you’d like. It automatically generates and prepends a secure IV for your convenience.

If it’s a file that you wish to compress take a look at this answer Encrypting a large file with AES using JAVA for an even simpler approach.

Interesting Posts
Давайте будем гением компьютера.