256-битное Java 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" нужно делать самостоятельно :-)

 Joshua14 июн. 2009 г., 06:24
Будьте внимательны при наборе номера, возможно, ваш AES менее защищен.
 Mitch Wheat14 июн. 2009 г., 04:45
Не могли бы вы уточнить: работает ли вызов kgen.init (256)?
 Nippysaurus14 июн. 2009 г., 05:25
Да, но это автоматически генерирует ключ ... но так как я хочу зашифровать данные между двумя местами, мне нужно знать ключ заранее, поэтому мне нужно указать один вместо "генерировать" один. Я могу указать 16-битный, который работает для 128-битного шифрования, которое работает. Я пробовал 32-битный для 256-битного шифрования, но он не работал, как ожидалось.
 erickson14 июн. 2009 г., 06:13
Если я правильно понимаю, вы пытаетесь использовать предварительно скомпонованный 256-битный ключ, указанный, например, как массив байтов. Если это так, подход DarkSquid с использованием SecretKeySpec должен работать. Также возможно получить ключ AES из пароля; если это то, что вам нужно, пожалуйста, дайте мне знать, и я покажу вам правильный способ сделать это; простое хеширование пароля не лучшая практика.
 Nippysaurus14 июн. 2009 г., 06:59
@erickson: это именно то, что мне нужно сделать (получить ключ AES из пароля).

Ответы на вопрос(9)

затем извлекал байты из хэша в байт ключа [].

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

SecretKeySpec key = new SecretKeySpec(keyBytes, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] encryptedBytes = cipher.doFinal(clearText.getBytes());
 Niranjan Subramanian16 апр. 2014 г., 12:46
@DarkSquidCipher aes256 = Cipher.getInstance("AES/OFB/NoPadding"); MessageDigest keyDigest = MessageDigest.getInstance("SHA-256"); byte[] keyHash = keyDigest.digest(secret.getBytes("UTF-8")); SecretKeySpec key = new SecretKeySpec(keyHash, "AES"); aes256.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(initializationVector)); Я также делаю то же самое, что было предложено в вашем ответе, но я все равно получаю исключение java.security.InvalidKeyException: Недопустимый размер ключа Обязательно ли загружать файл политики JCE?
 IcedDante02 нояб. 2014 г., 18:38
НЕ ИСПОЛЬЗУЙТЕ этот метод в любом типе производственной среды. При запуске шифрования на основе пароля многие пользователи перегружены стенами кода и не понимают, как работают атаки по словарю и другие простые хаки. В то время как это может быть неприятно, чтобы учиться, это стоит инвестиций, чтобы исследовать это. Вот хорошая статья для начинающих:adambard.com/blog/3-wrong-ways-to-store-a-password
 Hut828 апр. 2011 г., 08:55
Для других: это не очень безопасный метод. Вы должны использовать PBKDF 2, указанный в PKCS # 5. Эриксон сказал, как это сделать выше. Метод DarkSquid уязвим для парольных атак и также не работает, если размер вашего открытого текста не кратен размеру блока AES (128 бит), потому что он исключил заполнение. Также это не определяет режим; прочитайте режимы работы блочного шифра Википедии для беспокойства.

следующая версия использует InputStreams, а не файлы, чтобы упростить работу с различными файлами. Он также хранит IV и Salt в начале файла, что позволяет отслеживать только пароль. Поскольку 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();
        }
    }
}
 Maarten Bodewes17 мар. 2016 г., 00:20
Кажется, что это решение использует некоторую неловкую обработку буфера и обработку исключений на уровне ниже подпрограммы, в основном регистрируя их, а затем забывая о них. Имейте в виду, что использование CBC - это нормально для файлов, но не для безопасности транспорта. Конечно, можно защитить использование PBKDF2 и AES, в этом смысле это может стать хорошей основой для решения.

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

Но создания 256-битного ключа недостаточно. Если генератор ключей не может сгенерировать для вас 256-битные ключи, то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 и использовать ее в качестве пользовательского поставщика. Это включает в себя создание собственногоProvider подкласс и использование его сCipher.getInstance(String, Provider), Но это может быть сложным процессом.

 Maarten Bodewes09 мая 2014 г., 16:01
Вы не можете создать своего собственного провайдера, поставщики должны быть подписаны (не могу поверить, что я сначала перечитал эту ошибку). Даже если бы вы могли, ограничение размера ключа заключается в реализацииCipher, а не в самом провайдере. Вы можете использовать AES-256 в Java 8 и ниже, но вам нужно использовать собственный API. Или среда выполнения, которая не накладывает ограничений на размер ключа, конечно.
 Maarten Bodewes25 февр. 2012 г., 00:54
Вы должны всегда указывать режим и алгоритм заполнения. Java по умолчанию использует небезопасный режим ECB.

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

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

Некоторые примечания: при этом используется 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();

        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();
      }
   }


}
 dynamokaj16 нояб. 2014 г., 16:32
@owlstead - это отличный ответ. Он показывает, как зашифровать поток, зашифровав байтовый буфер, вместо того, чтобы хранить все в памяти. Ответ Эриксона не будет работать для больших файлов, которые не помещаются в памяти. Так что +1 к wufoo. :)
 Maarten Bodewes16 нояб. 2014 г., 20:25
@dynamokaj ИспользованиеCipherInputStream а такжеCipherOutputStream это не большая проблема. Перетасовка всех исключений под таблицей является проблемой. Тот факт, что соль внезапно стала полем и что требуется IV, является проблемой. Тот факт, что он не следует соглашениям Java-кодирования, является проблемой. И тот факт, что это работает только с файлами, пока об этом не просили, является проблемой. И то, что остальная часть кода в основном является копией, тоже не помогает. Но, может быть, я настрою это, чтобы сделать его лучше, как и предлагалось ...
 Maarten Bodewes09 мая 2014 г., 16:06
Это в основном тот же ответ, что и у Эриксона, в окружении - не слишком хорошо запрограммированного, по моему мнению - обертки.printStackTrace()
 dynamokaj17 нояб. 2014 г., 20:38
@owlstead Я согласен, что кодирование могло бы выглядеть лучше, я сократил его до 1/4 или около того, но мне нравится, что он познакомил меня с CipherInputStream и CipherOutputStream, поскольку это было именно то, что мне было нужно вчера! ;)
 Marian Paździoch20 февр. 2017 г., 13:40
почему дважды? fout.close (); fout.close ();
Рассмотрите возможность использованияКрипто модуль Spring Security

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

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

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

Посмотрите навнутренности показывает структуру, похожую наответ Эриксона.

Как отмечено в вопросе, это также требуетJava Cryptography Extension (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");
        }       
    }
}

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

Salt: "feacbc02a3a697b0"
Original text: "*royal secrets*"
Encrypted text: "7c73c5a83fa580b5d6f8208768adc931ef3123291ac8bc335a1277a39d256d9a" 
Decrypted text: "*royal secrets*"
Success: decrypted text matches
 John McCarthy20 июл. 2015 г., 17:17
@IvanRF извините, не похоже на это. 256 жестко закодирован висточник
 John McCarthy13 мар. 2014 г., 17:48
@theglauber Да, вы можете использовать модуль без Spring Security или Spring Framework. Смотря наП, единственная зависимость во время выполнения - apacheрегистрация общего пользования 1.1.1, Ты можешьвытащить банку с мавеном или жескачать его прямо с официального бинарного репо (видетьSpring 4 исполняемые файлы скачать для получения дополнительной информации о бинарных файлах Spring).
 erickson03 мая 2016 г., 21:12
NULL_IV_GENERATOR используемая утилитой Spring небезопасна. Если приложение не предоставляет IV, пусть поставщик выберет его и запросит его после инициализации.
 theglauber13 мар. 2014 г., 16:56
Можете ли вы использовать этот модуль без загрузки всей Spring? Похоже, они не сделали файлы JAR доступными для скачивания.
 IvanRF17 июл. 2015 г., 18:36
Можно ли установить длину ключа в 128 бит? Изменение папки безопасности на каждом ПК для меня не вариант.
Решение Вопроса

Поделиться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; это защитит целостность данных, а также их конфиденциальность.

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

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

 erickson15 июн. 2009 г., 01:26
@ Ник: Прочитайте PKCS # 5. Соли необходимы для PBKDF2, поэтому API для шифрования на основе паролей требует их в качестве входных данных для получения ключей. Без солей можно использовать атаку по словарю, что позволяет предварительно вычислить список наиболее вероятных симметричных ключей шифрования. Шифраторы IV и соли для получения ключей служат различным целям. IV позволяют повторно использовать один и тот же ключ для нескольких сообщений. Соли предотвращают словарные атаки на ключ.
 happy09 июл. 2016 г., 09:25
PBKDF2WithHmacSHA256 не поддерживается в java6 и java 7
 Steve Clay21 апр. 2011 г., 06:10
Я реализовал ответ @ erickson как класс:github.com/mrclay/jSecureEdit/tree/master/src/org/mrclay/crypto (PBE выполняет свою работу, PBEStorage является ценным объектом для совместного хранения IV / шифротекста.)
 erickson15 окт. 2009 г., 18:10
Во-первых, это будет шифрование DES, а не AES. Большинство провайдеров не имеют хорошей поддержки дляPBEwith<prf>and<encryption> алгоритмы; например, SunJCE не предоставляет и PBE для AES. Во-вторых, включение jasypt не является целью. Пакет, который подразумевает обеспечение безопасности без понимания основополагающих принципов, кажется опасным prima facie.
 Amir Moghimi25 июн. 2012 г., 08:01
Для запуска этого кода убедитесь, что у вас есть правильные файлы политики неограниченной юрисдикции в вашей JRE, как указано вngs.ac.uk/tools/jcepolicyfiles


Java AES 256-битный класс шифрования / дешифрования

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

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

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

 Jay26 нояб. 2013 г., 17:22
Работает нормально. Однако, если я расшифровываю с использованием другого экземпляра AESEncrypter, дешифрованный файл имеет тенденцию иметь некоторые проблемы в первых нескольких байтах (9 байтов). Я зашифровал файл с помощью ключевой фразы, а затем попытался расшифровать с помощью той же ключевой фразы. Все содержимое файла, кроме первых 9 байтов, было правильно расшифровано.
 Shaun the Sheep24 июн. 2012 г., 18:54
Похоже, что вы разделяете одно и то же фиксированное значение соли между всеми ключевыми экземплярами. Это, вероятно, не очень хорошая идея.
 Duncan Jones04 авг. 2014 г., 13:41
-1 Это должен был быть комментарий под ответом Эриксона. Нет необходимости иметь отдельный ответ, чтобы указать, что вы реализовали чужой код.
 erickson03 мая 2016 г., 21:16
Это не следует совету в моем ответе.
 Maarten Bodewes09 мая 2014 г., 16:11
@Jay С криптографией иметь только работающую реализацию - это не то же самое, что отвечать вашим требованиям. Опять же, этот класс является оболочкой для решения Эриксона (хотя сам код на первый взгляд кажется нормальным, за исключением статической соли). Обратите внимание, что ответы не должны быть только ссылками без объяснения процесса, который был использован.

Используйте этот класс для шифрования. Оно работает.

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);

}

}

И это ivBytes и случайный ключ;

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");
 Michele Giuseppe Fadda13 нояб. 2015 г., 11:07
IV инициализируется до нуля. Поиск BEAST и ACPA атак.
 Phil15 окт. 2016 г., 05:41
Исключения составляют wazoo, метод генерации «случайного» ключа и нулевой IV - проблема с этой реализацией, но эти проблемы тривиально исправить. +1.
 Maarten Bodewes09 мая 2014 г., 16:13
"это работает" .... да, но оно не отвечает требованиям для создания криптографически безопасного решения (и, на мой взгляд, не отвечает стандартам кодирования Java в отношении обработки исключений).

Encryptor4j

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

Затем сделайте следующее:

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

Теперь вы можете использовать шифратор для шифрования вашего сообщения. Вы также можете выполнить потоковое шифрование, если хотите. Он автоматически генерирует и добавляет безопасный IV для вашего удобства.

Если это файл, который вы хотите сжать, посмотрите на этот ответШифрование большого файла с помощью AES с использованием JAVA для еще более простого подхода.

Ваш ответ на вопрос