Java 256位AES基于密码的encryption
我需要实现256位AESencryption,但是我在网上find的所有例子都使用“KeyGenerator”生成一个256位的密钥,但是我想用我自己的密码。 我如何创build自己的密钥? 我已经尝试填充到256位,但后来我得到一个错误说,关键是太长了。 我有无限的pipe辖区的补丁安装,所以这不是问题:)
IE浏览器。 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
select一个好盐 – 这不需要被保密)带外收件人。 然后从这个信息中得到一个好钥匙:
/* 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分别是密钥导出迭代计数和密钥大小。
密钥派生函数被迭代需要大量的计算工作,并且可以防止攻击者快速尝试许多不同的密码。 迭代计数可以根据可用的计算资源而改变。
密钥大小可以减less到128位,这仍然被认为是“强大的”encryption,但是如果发现削弱AES的攻击,则不会提供太大的安全余量。
与正确的块链模式一起使用,可以使用相同的派生密钥来encryption许多消息。 在CBC中,为每个消息生成随机初始化向量(IV),即使明文是相同的,也会产生不同的密文。 CBC可能不是您可以使用的最安全的模式(请参阅下面的AEAD); 还有很多其他模式具有不同的安全属性,但都使用了类似的随机input。 在任何情况下,每个encryption操作的输出都是密文和初始化向量:
/* 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
以完全相同的方式重新生成,使用具有相同salt和迭代参数的密码。 使用此密钥初始化密码, 并使用消息存储初始化向量:
/* 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包含了对AEAD密码模式的API支持,OpenJDK和Oracle发行版中包含的“SunJCE”提供程序从Java 8开始实现了这些模式。强烈build议使用这些模式之一来代替CBC; 它将保护数据的完整性和隐私。
带有“非法密钥大小或默认参数”消息的java.security.InvalidKeyException
意味着密码强度是有限的; 无限力量pipe辖权政策文件不在正确的位置。 在JDK中,它们应该放置在${jdk}/jre/lib/security
根据问题描述,这听起来像政策文件没有正确安装。 系统可以轻松拥有多个Java运行时; 仔细检查以确保正确的位置正在被使用。
考虑使用Spring Securityencryption模块
Spring Security Crypto模块为对称encryption,密钥生成和密码编码提供支持。 该代码作为核心模块的一部分进行分发,但不依赖于任何其他Spring Security(或Spring)代码。
它提供了一个简单的encryption抽象,似乎匹配这里所要求的,
“标准”encryption方法是使用PKCS#5的PBKDF2(基于密码的密钥推导函数#2)的256位AES。 此方法需要Java 6.用于生成SecretKey的密码应保存在安全的地方,不要共享。 salt用于防止encryption的数据受到攻击时对字典的攻击。 一个16字节的随机初始化向量也被应用,所以每个encryption的消息是唯一的。
看内部结构揭示了一个类似于埃里克森的答案的结构。
如问题所述,这也需要Java密码扩展(JCE)无限强度pipe辖权策略 (否则您将遇到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” 原始文本:“*皇家秘密*” encryption文本:“7c73c5a83fa580b5d6f8208768adc931ef3123291ac8bc335a1277a39d256d9a” 解密文本:“*皇家秘密*” 成功:解密文本匹配
在阅读了erickson的build议之后,我从其他贴子和这个例子中搜集了一些可能的东西 ,我试图用推荐的修改来更新Doug的代码。 随意编辑,使其更好。
- 初始化vector不再是固定的
- encryption密钥是使用来自erickson的代码派生的
- setupEncrypt()中使用SecureRandom()生成8字节的salt
- 解密密钥由encryption盐和密码生成
- 解密密码由解密密钥和初始化向量生成
- 删除hex代替org.apache.commons 编解码器hex例程
一些注意事项:这使用128位encryption密钥 – java显然不会立即执行256位encryption。 实现256需要在java安装目录中安装一些额外的文件。
另外,我不是一个encryption的人。 谨慎。
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(); } } }
我已经在一个非常简单的类中实现了erickson的答案:
Java AES 256位encryption/解密类
如果得到java.security.InvalidKeyException
,则必须安装Javaencryption扩展(JCE)无限强度pipe辖区域策略文件:
- Java 6链接
- Java 7链接
只需将这些jar放在您的{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");
但是创build一个256位的密钥是不够的。 如果密钥生成器不能为您生成256位密钥,则Cipher
类可能不支持AES 256位。 你说你已经安装了不受限制的pipe理补丁,所以应该支持AES-256密码(但是256位密钥也应该是这样,所以这可能是一个configuration问题)。
Cipher cipher = Cipher.getInstance("AES"); cipher.init(Cipher.ENCRYPT_MODE, skey); byte[] encrypted = cipher.doFinal(plainText.getBytes());
缺lessAES-256支持的解决方法是采取一些免费的AES-256实现,并将其用作自定义提供者。 这涉及到创build您自己的Provider
子类并将其与Cipher.getInstance(String, Provider)
。 但这可能是一个涉及的过程。
我过去所做的是通过类似SHA256的方式散列密钥,然后将散列中的字节提取到密钥字节[]中。
有了你的byte []之后,你可以简单地做:
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和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(); } } }
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.