为啥我的 AES 加密会引发 InvalidKeyException?
Posted
技术标签:
【中文标题】为啥我的 AES 加密会引发 InvalidKeyException?【英文标题】:Why does my AES encryption throws an InvalidKeyException?为什么我的 AES 加密会引发 InvalidKeyException? 【发布时间】:2011-10-03 21:27:11 【问题描述】:我目前正在开发一个使用密钥加密/解密特定文件的功能。我写了三个类,一个生成密钥,一个用密钥加密文件,一个解密。
生成密钥和加密文件工作正常,但是当我尝试解密文件时,在以下行抛出异常:c.init(Cipher.DECRYPT_MODE, keySpec);
:
java.security.InvalidKeyException:缺少参数
我认为我在将密钥流式传输到我的 byte[]
时做错了,或者在解密文件时出现了问题。
三个类的快速解释: KeyHandler 创建一个 AES 密钥并将其存储在硬盘上。密钥/明文/加密/解密文件的名称目前是硬编码的,用于测试目的。
EncryptionHandler
将磁盘上的 .txt 文件转换为字节,用密钥加密文件,然后使用CipherOutputStream
将加密后的字节写入磁盘。
DecryptionHandler
当然与EncryptionHandler
正好相反。
代码如下:
public class KeyHandler
Scanner scan = new Scanner(System.in);
public KeyHandler()
try
startMenu();
catch (Exception e)
System.out.println("fel någonstanns :)");
public void startMenu() throws Exception
System.out.println("Hej. Med detta program kan du generera en hemlig nyckel"+"\n"+"Vill du:"+"\n"+ "1. Generera en nyckel"+"\n"+"2. Avsluta");
int val=Integer.parseInt(scan.nextLine());
do
switch (val)
case 1: generateKey(); break;
case 2: System.exit(1);
default: System.out.println("Du måste välja val 1 eller 2");
while (val!=3);
public void generateKey() throws Exception
try
KeyGenerator gen = KeyGenerator.getInstance("AES");
gen.init(128);
SecretKey key=gen.generateKey();
byte[] keyBytes = key.getEncoded();
System.out.print("Ge nyckeln ett filnamn: ");
String filename = scan.next();
System.out.println("Genererar nyckeln...");
FileOutputStream fileOut = new FileOutputStream(filename);
fileOut.write(keyBytes);
fileOut.close();
System.out.println("Nyckeln är genererad med filnamnet: "+filename+"...");
System.exit(1);
catch (NoSuchAlgorithmException e)
public static void main(String[] args)
new KeyHandler();
public class EncryptHandler
private String encryptedDataString;
private Cipher ecipher;
AlgorithmParameterSpec paramSpec;
byte[] iv;
public EncryptHandler(String dataString, String secretKey, String encryptedDataString)
this.encryptedDataString=encryptedDataString;
try
encryptFile(dataString, secretKey);
catch (Exception e)
public void encryptFile(String dataString, String secretKey) throws Exception
FileInputStream fis = new FileInputStream(secretKey);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int theByte = 0;
while ((theByte = fis.read()) != -1)
baos.write(theByte);
fis.close();
byte[] keyBytes = baos.toByteArray();
baos.close();
SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
try
ecipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
ecipher.init(Cipher.ENCRYPT_MODE, keySpec);
catch (Exception e)
e.printStackTrace();
System.out.println("Encrypting file...");
try
encryptStream(new FileInputStream(dataString),new FileOutputStream(encryptedDataString));
catch(Exception e)
e.printStackTrace();
public void encryptStream(InputStream in, OutputStream out)
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
byte[] buf = new byte[1024];
try
out = new CipherOutputStream(out, ecipher);
// read the cleartext and write it to out
int numRead = 0;
while ((numRead = in.read(buf)) >= 0)
out.write(buf, 0, numRead);
bOut.writeTo(out);
out.close();
bOut.reset();
catch (java.io.IOException e)
public static void main(String[] args)
String data = "test.txt";
String keyFileName="a";
String encryptedFile="krypterad.txt";
//String encryptedFile =args[2];
new EncryptHandler(data, keyFileName, encryptedFile);
public class DecryptHandler
public DecryptHandler()
try
decryptFile();
catch (Exception e)
System.out.println("något gick fel :) ");
public void decryptFile()throws Exception
byte[] buf = new byte[1024];
String keyFilename = "hemlig";
FileInputStream fis = new FileInputStream(keyFilename);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int theByte = 0;
while ((theByte = fis.read()) != -1)
baos.write(theByte);
fis.close();
byte[] keyBytes = baos.toByteArray();
baos.close();
SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
System.out.println("här");
c.init(Cipher.DECRYPT_MODE, keySpec);
System.out.println("Decrypting file...");
try
decryptStream(new FileInputStream("krypterad.txt"),new FileOutputStream("Dekryperad.txt"), c, buf);
catch (java.io.IOException e)
System.out.println("File decrypted!");
public void decryptStream(InputStream in, OutputStream out, Cipher dcipher, byte[] buf)
try
in = new CipherInputStream(in, dcipher);
// Read in the decrypted bytes and write the cleartext to out
int numRead = 0;
while ((numRead = in.read(buf)) >= 0)
out.write(buf, 0, numRead);
out.close();
catch (java.io.IOException e)
public static void main(String[]args)
new DecryptHandler();
【问题讨论】:
你能发布堆栈跟踪吗?会更容易:] 抱歉 :) java.security.InvalidKeyException: 缺少参数 是我的错,当使用密码块链接时,需要使用 IV。我更改为不需要 IV 的 ECB 模式 请记住,ECB 模式不安全,它会泄露信息。***页面 en.wikipedia.org/wiki/…> 上有一个很好的说明(字面意思)信息泄露。为了更好的安全性,请使用 CBC 或 CTR 模式。 【参考方案1】:如果您使用像 CBC 这样的块链模式,您还需要为 Cipher 提供一个 IvParameterSpec。
所以你可以像这样初始化一个 IvParameterSpec:
// build the initialization vector. This example is all zeros, but it
// could be any value or generated using a random number generator.
byte[] iv = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ;
IvParameterSpec ivspec = new IvParameterSpec(iv);
然后为了加密,将你初始化密码的代码更改为:
ecipher.init(Cipher.ENCRYPT_MODE, keySpec, ivspec);
你在哪里解密:
c.init(Cipher.DECRYPT_MODE, keySpec, ivspec);
所以你的完整代码应该是这样的(它适用于我):
public class KeyHandler
Scanner scan = new Scanner(System.in);
public KeyHandler()
try
startMenu();
catch (Exception e)
System.out.println("fel någonstanns :)");
public void startMenu() throws Exception
System.out.println("Hej. Med detta program kan du generera en hemlig nyckel" + "\n" + "Vill du:" + "\n" + "1. Generera en nyckel" + "\n" + "2. Avsluta");
int val = Integer.parseInt(scan.nextLine());
do
switch (val)
case 1:
generateKey();
break;
case 2:
System.exit(1);
default:
System.out.println("Du måste välja val 1 eller 2");
while (val != 3);
public void generateKey() throws Exception
try
KeyGenerator gen = KeyGenerator.getInstance("AES");
gen.init(128);
SecretKey key = gen.generateKey();
byte[] keyBytes = key.getEncoded();
System.out.print("Ge nyckeln ett filnamn: ");
String filename = scan.next();
System.out.println("Genererar nyckeln...");
FileOutputStream fileOut = new FileOutputStream(filename);
fileOut.write(keyBytes);
fileOut.close();
System.out.println("Nyckeln är genererad med filnamnet: " + filename + "...");
System.exit(1);
catch (NoSuchAlgorithmException e)
public static void main(String[] args)
new KeyHandler();
public class EncryptHandler
private String encryptedDataString;
private Cipher ecipher;
AlgorithmParameterSpec paramSpec;
byte[] iv;
public EncryptHandler(String dataString, String secretKey, String encryptedDataString)
this.encryptedDataString = encryptedDataString;
try
encryptFile(dataString, secretKey);
catch (Exception e)
public void encryptFile(String dataString, String secretKey) throws Exception
FileInputStream fis = new FileInputStream(secretKey);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int theByte = 0;
while ((theByte = fis.read()) != -1)
baos.write(theByte);
fis.close();
byte[] keyBytes = baos.toByteArray();
baos.close();
SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
// build the initialization vector. This example is all zeros, but it
// could be any value or generated using a random number generator.
byte[] iv = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ;
IvParameterSpec ivspec = new IvParameterSpec(iv);
try
ecipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
ecipher.init(Cipher.ENCRYPT_MODE, keySpec, ivspec);
catch (Exception e)
e.printStackTrace();
System.out.println("Encrypting file...");
try
encryptStream(new FileInputStream(dataString), new FileOutputStream(encryptedDataString));
catch (Exception e)
e.printStackTrace();
public void encryptStream(InputStream in, OutputStream out)
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
byte[] buf = new byte[1024];
try
out = new CipherOutputStream(out, ecipher);
// read the cleartext and write it to out
int numRead = 0;
while ((numRead = in.read(buf)) >= 0)
out.write(buf, 0, numRead);
bOut.writeTo(out);
out.close();
bOut.reset();
catch (java.io.IOException e)
public static void main(String[] args)
String data = "test.txt";
String keyFileName = "a";
String encryptedFile = "krypterad.txt";
//String encryptedFile =args[2];
new EncryptHandler(data, keyFileName, encryptedFile);
public class DecryptHandler
public DecryptHandler()
try
decryptFile();
catch (Exception e)
e.printStackTrace();
System.out.println("något gick fel :) ");
public void decryptFile() throws Exception
byte[] buf = new byte[1024];
String keyFilename = "hemlig";
FileInputStream fis = new FileInputStream(keyFilename);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int theByte = 0;
while ((theByte = fis.read()) != -1)
baos.write(theByte);
fis.close();
byte[] keyBytes = baos.toByteArray();
baos.close();
SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
// build the initialization vector. This example is all zeros, but it
// could be any value or generated using a random number generator.
byte[] iv = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ;
IvParameterSpec ivspec = new IvParameterSpec(iv);
Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
System.out.println("här");
c.init(Cipher.DECRYPT_MODE, keySpec, ivspec);
System.out.println("Decrypting file...");
try
decryptStream(new FileInputStream("krypterad.txt"), new FileOutputStream("Dekryperad.txt"), c, buf);
catch (java.io.IOException e)
System.out.println("File decrypted!");
public void decryptStream(InputStream in, OutputStream out, Cipher dcipher, byte[] buf)
try
in = new CipherInputStream(in, dcipher);
// Read in the decrypted bytes and write the cleartext to out
int numRead = 0;
while ((numRead = in.read(buf)) >= 0)
out.write(buf, 0, numRead);
out.close();
catch (java.io.IOException e)
public static void main(String[] args)
new DecryptHandler();
【讨论】:
意识到 CBC 请求者和 IV 所以我更改为 ECB 模式。无论如何,欧洲央行模式对于我想要完成的事情来说都很好。感谢您的回复 @Jimmy:你不应该使用 ECB 模式,它不安全。相反,生成一个随机 IV 并将其存储为文件的第一个块。 (这只是 16 字节的开销。) 如何生成随机 IV 并将其存储在文件的第一个块中会比 ECB 模式更安全,因为 IV 是可见的? @adamitj 根据this page IV不需要保密。它所需要的只是随机且不可预测:defuse.ca/cbcmodeiv.htm 请注意,CBC 使用前一个块的密文作为下一个块的 IV,不像 ECB 对每个块使用相同的 IV。这导致输出的伪随机性更高。至于 IV 的存储,Bruce Schneier 是这样说的。如果你隐藏了第一个 IV,并且你的密文是 N 块长,因为每个密文块都是下一个块的 IV,你仍然有 N-1 个 IV 可见。以上是关于为啥我的 AES 加密会引发 InvalidKeyException?的主要内容,如果未能解决你的问题,请参考以下文章
java aes加密与网上在线加密不同。谁能告诉我为啥?求个正确的例子,谢谢了!
Amazon S3 存储桶加密 - KMS 与 AES 256
php openssl aes-256-cbc key长度自动匹配了128的长度,为啥