Java 如何修复无效的 AES 密钥长度?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/29354133/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to fix Invalid AES key length?
提问by Rishabh Upadhyay
I am working on a text encryption and decryptionproject (following Struts 2)
我正在做一个文本加密和解密项目(在 Struts 2 之后)
Whenever I enter the password and the plain text I get a Invalid AES Key Length error.
每当我输入密码和纯文本时,我都会收到无效的 AES 密钥长度错误。
The Service Class
服务类
package com.anoncrypt.services;
import java.security.Key;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
public class SymAES
{
private static final String ALGORITHM = "AES";
private static byte[] keyValue= new byte[] { 'T', 'h', 'i', 's', 'I', 's', 'A', 'S', 'e', 'c', 'r', 'e', 't', 'K', 'e', 'y' };
public String encode(String valueToEnc) throws Exception {
Key key = new SecretKeySpec(keyValue, ALGORITHM);
Cipher c = Cipher.getInstance(ALGORITHM);
c.init(Cipher.ENCRYPT_MODE, key);
byte[] encValue = c.doFinal(valueToEnc.getBytes());
String encryptedValue = new BASE64Encoder().encode(encValue);
return encryptedValue;
}
public String decode(String encryptedValue) throws Exception {
Key key = new SecretKeySpec(keyValue, ALGORITHM);
Cipher c = Cipher.getInstance(ALGORITHM);
c.init(Cipher.DECRYPT_MODE, key);
byte[] decordedValue = new BASE64Decoder().decodeBuffer(encryptedValue);
byte[] decValue = c.doFinal(decordedValue);
String decryptedValue = new String(decValue);
return decryptedValue;
}
public void start(String passcode)throws Exception
{
keyValue = passcode.getBytes();
}
}
And this is the error
这是错误
java.security.InvalidKeyException: Invalid AES key length: 6 bytes
com.sun.crypto.provider.AESCrypt.init(AESCrypt.java:87)
com.sun.crypto.provider.ElectronicCodeBook.init(ElectronicCodeBook.java:93)
com.sun.crypto.provider.CipherCore.init(CipherCore.java:582)
com.sun.crypto.provider.CipherCore.init(CipherCore.java:458)
com.sun.crypto.provider.AESCipher.engineInit(AESCipher.java:307)
javax.crypto.Cipher.implInit(Cipher.java:797)
javax.crypto.Cipher.chooseProvider(Cipher.java:859)
javax.crypto.Cipher.init(Cipher.java:1229)
javax.crypto.Cipher.init(Cipher.java:1166)
com.anoncrypt.services.SymAES.encode(SymAES.java:35)
com.anoncrypt.actions.SymEncrypt.execute(SymEncrypt.java:24)
采纳答案by Artjom B.
Things to know in general:
一般要知道的事情:
- Key != Password
SecretKeySpec
expects a key, not a password. See below
- It might be due to a policy restriction that prevents using 32 byte keys. See other answer on that
- 密钥 != 密码
SecretKeySpec
需要密钥,而不是密码。见下文
- 这可能是由于禁止使用 32 字节密钥的策略限制。请参阅其他答案
In your case
在你的情况下
The problem is number 1: you are passing the password instead of the key.
问题是第 1 点:您传递的是密码而不是密钥。
AES only supports key sizes of 16, 24 or 32 bytes. You either need to provide exactly that amount or you derive the key from what you type in.
AES 仅支持 16、24 或 32 字节的密钥大小。您要么需要提供准确的金额,要么从您输入的内容中获取密钥。
There are different ways to derive the key from a passphrase. Java provides a PBKDF2 implementation for such a purpose.
有多种方法可以从密码短语中导出密钥。为此,Java 提供了 PBKDF2 实现。
I used erickson's answerto paint a complete picture (only encryption, since the decryption is similar, but includes splitting the ciphertext):
我用erickson的回答画了一个完整的图(只有加密,因为解密是相似的,但包括拆分密文):
SecureRandom random = new SecureRandom();
byte[] salt = new byte[16];
random.nextBytes(salt);
KeySpec spec = new PBEKeySpec("password".toCharArray(), salt, 65536, 256); // AES-256
SecretKeyFactory f = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
byte[] key = f.generateSecret(spec).getEncoded();
SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
byte[] ivBytes = new byte[16];
random.nextBytes(ivBytes);
IvParameterSpec iv = new IvParameterSpec(ivBytes);
Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
c.init(Cipher.ENCRYPT_MODE, keySpec, iv);
byte[] encValue = c.doFinal(valueToEnc.getBytes());
byte[] finalCiphertext = new byte[encValue.length+2*16];
System.arraycopy(ivBytes, 0, finalCiphertext, 0, 16);
System.arraycopy(salt, 0, finalCiphertext, 16, 16);
System.arraycopy(encValue, 0, finalCiphertext, 32, encValue.length);
return finalCiphertext;
Other things to keep in mind:
其他需要注意的事项:
- Always use a fully qualified Cipher name.
AES
is not appropriate in such a case, because different JVMs/JCE providers may use different defaults for mode of operation and padding. UseAES/CBC/PKCS5Padding
. Don't use ECB mode, because it is not semantically secure. - If you don't use ECB mode then you need to send the IV along with the ciphertext. This is usually done by prefixing the IV to the ciphertext byte array. The IV is automatically created for you and you can get it through
cipherInstance.getIV()
. - Whenever you send something, you need to be sure that it wasn't altered along the way. It is hard to implement a encryption with MAC correctly. I recommend you to use an authenticated mode like CCM or GCM.
- 始终使用完全限定的密码名称。
AES
在这种情况下不合适,因为不同的 JVM/JCE 提供者可能使用不同的默认操作模式和填充。使用AES/CBC/PKCS5Padding
. 不要使用 ECB 模式,因为它在语义上不安全。 - 如果您不使用 ECB 模式,则需要将 IV 与密文一起发送。这通常是通过在密文字节数组中添加 IV 前缀来完成的。IV 是自动为您创建的,您可以通过
cipherInstance.getIV()
. - 每当您发送某些东西时,您都需要确保它在发送过程中没有被更改。很难用 MAC 正确实现加密。我建议你使用像 CCM 或 GCM 这样的认证模式。
回答by vineet patel
I was facing the same issue then i made my key 16 byte and it's working properly now. Create your key exactly 16 byte. It will surely work.
我遇到了同样的问题,然后我将密钥设置为 16 字节,现在它可以正常工作了。创建恰好 16 字节的密钥。它肯定会起作用。
回答by AOL
You can verify the key length limit:
您可以验证密钥长度限制:
int maxKeyLen = Cipher.getMaxAllowedKeyLength("AES");
System.out.println("MaxAllowedKeyLength=[" + maxKeyLen + "].");