Java 256位AES基于密码的加密

我需要实现256位AES加密,但我在网上找到的所有示例都使用“KeyGenerator”来生成256位密钥,但我想使用我自己的passkey。如何创建自己的密钥?我尝试将其填充到256位,但随后我得到一个错误,说键太长。我确实安装了无限管辖权补丁,所以这不是问题:)

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”部分:-)

613972 次浏览

我过去所做的是通过SHA256之类的东西散列密钥,然后从散列中提取字节到密钥字节[]。

在你有字节[]之后,你可以简单地做:

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

从字节数组生成你自己的键很简单:

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

但是创建一个256位的密钥是不够的。如果密钥生成器不能为你生成256位密钥,那么Cipher类可能也不支持AES 256位。您说您已经安装了无限权限补丁,因此应该支持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)一起使用。但这可能是一个复杂的过程。

与带外接收者共享password(一个char[])和salt(一个SecureRandom选择的byte[]-8字节可以制作一个好的盐——这不需要保密)。然后从这些信息中推导出一个好的关键字:

/* 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(StandardCharsets.UTF_8));

存储ciphertextiv。在解密时,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), StandardCharsets.UTF_8);
System.out.println(plaintext);

Java 7包括API 支持AEAD密码模式和“SunJCE"OpenJDK和Oracle发行版中包含的provider从Java 8开始实现这些功能。强烈推荐其中一种模式来代替CBC;它将保护数据的完整性以及他们的隐私。


java.security.InvalidKeyException带有“非法键大小或默认参数”的信息;表示加密强度有限;无限强度权限策略文件不在正确的位置。在JDK中,它们应该放在${jdk}/jre/lib/security

根据问题描述,似乎没有正确安装策略文件。系统可以很容易地拥有多个Java运行时;仔细检查以确保使用了正确的位置。

在阅读了erickson的建议后,并从其他几个帖子和这个例子在这里中收集了我所能做的,我试图用建议的更改更新Doug的代码。请随意编辑,使它更好。

  • 初始化向量不再固定
  • 加密密钥是使用erickson的代码得来的
  • 8字节的salt在setupEncrypt()中使用securerrandom()生成
  • 解密密钥由加密盐和密码生成
  • 解密密码由解密密钥和初始化向量生成
  • 删除了十六进制的旋转,以代替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();
}
}




}

使用这个类进行加密。它的工作原理。

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

考虑使用Spring安全加密模块

Spring Security Crypto模块提供对对称加密、密钥生成和密码编码的支持。该代码作为核心模块的一部分分发,但不依赖于任何其他Spring Security(或Spring)代码。

它为加密提供了一个简单的抽象,似乎与这里的要求相匹配,

“standard"加密方法是256位AES,使用PKCS #5的PBKDF2(基于密码的密钥推导函数#2)。此方法需要Java 6。用于生成SecretKey的密码应保存在安全的地方,不能被共享。在加密数据被泄露的情况下,salt用于防止针对密钥的字典攻击。还应用了一个16字节的随机初始化向量,因此每个加密消息都是唯一的。

查看内部可以发现类似埃里克森的回答的结构。

正如问题中提到的,这也需要Java密码扩展(JCE)无限强度管辖策略(否则你会遇到InvalidKeyException: Illegal Key Size)。它是可下载的Java 6Java 7Java 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

添加到@Wufoo的编辑,下面的版本使用InputStreams而不是文件,使工作与各种文件更容易。它还将IV和Salt存储在文件的开头,这样就只需要跟踪密码。由于IV和Salt不需要保密,这使生活更容易一些。

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

考虑使用Encryptor4j,我是它的作者。

首先确保在继续之前安装了无限力量管辖政策文件,以便您可以使用256位AES密钥。

然后执行以下步骤:

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

您现在可以使用加密器加密您的消息。如果愿意,还可以执行流加密。它会自动生成并预先为您提供安全的静脉注射。

如果这是一个你想要压缩的文件,看看这个答案 使用JAVA对大文件进行AES加密是一个更简单的方法

(可能对其他有类似需求的人有帮助)

我有一个类似的要求,在Java中使用AES-256-CBC加密和解密。

要实现(或指定)256字节加密/解密,Java Cryptography Extension (JCE)策略应该设置为"Unlimited"

它可以在$JAVA_HOME/jre/lib/security(适用于JDK)或$JAVA_HOME/lib/security(适用于JRE)下的java.security文件中设置

crypto.policy=unlimited

或者在代码中

Security.setProperty("crypto.policy", "unlimited");

Java 9及更高版本默认启用此功能。