如何修复无效的 AES 密钥长度?
How to fix Invalid AES key length?
我正在做一个文本加密和解密项目(跟随Struts 2)
每当我输入密码和纯文本时,我都会收到无效的 AES 密钥长度错误。
服务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();
}
}
这是错误
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)
一般要知道的事情:
- 密钥!=密码
SecretKeySpec
需要密钥,而不是密码。见下文
- 这可能是由于政策限制导致无法使用 32 字节密钥。查看其他答案
你的情况
第 1 个问题:您传递的是密码而不是密钥。
AES 仅支持 16、24 或 32 字节的密钥大小。您要么需要准确提供该金额,要么从您输入的内容中得出密钥。
有多种方法可以从密码导出密钥。 Java 为此提供了 PBKDF2 实现。
我用埃里克森的answer画了一幅完整的图(只有加密,因为解密是相似的,但包括拆分密文):
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;
其他注意事项:
- 始终使用完全限定的密码名称。
AES
在这种情况下是不合适的,因为不同的 JVMs/JCE 供应商可能对操作模式和填充使用不同的默认值。使用 AES/CBC/PKCS5Padding
。不要使用 ECB 模式,因为它在语义上不安全。
- 如果您不使用 ECB 模式,那么您需要将 IV 与密文一起发送。这通常是通过将 IV 作为密文字节数组的前缀来完成的。 IV 是自动为您创建的,您可以通过
cipherInstance.getIV()
. 获取它
- 无论何时发送内容,您都需要确保它在发送过程中没有被更改。很难用 MAC 正确地实现加密。我建议您使用像 CCM 或 GCM 这样的身份验证模式。
我遇到了同样的问题,然后我将密钥设为 16 字节,现在它工作正常。创建您的密钥恰好 16 字节。肯定有用。
您可以验证密钥长度限制:
int maxKeyLen = Cipher.getMaxAllowedKeyLength("AES");
System.out.println("MaxAllowedKeyLength=[" + maxKeyLen + "].");
您可以使用此代码,此代码用于 AES-256-CBC,或者您可以将其用于其他 AES 加密。密钥长度错误主要出现在256位加密中。
此错误是由于我们在 SecretKeySpec 中传递的编码或字符集名称所致。假设,在我的例子中,我的密钥长度为 44,但我无法使用这个长密钥来加密我的文本; Java 抛出一个无效密钥长度的错误。因此,我在函数中将我的密钥作为 BASE64 传递,并将我的 44 长度密钥转换为 32 字节,这是 256 位加密所必需的。
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.MessageDigest;
import java.security.Security;
import java.util.Base64;
public class Encrypt {
static byte [] arr = {1,2,3,4,5,6,7,8,9};
// static byte [] arr = new byte[16];
public static void main(String...args) {
try {
// System.out.println(Cipher.getMaxAllowedKeyLength("AES"));
Base64.Decoder decoder = Base64.getDecoder();
// static byte [] arr = new byte[16];
Security.setProperty("crypto.policy", "unlimited");
String key = "Your key";
// System.out.println("-------" + key);
String value = "Hey, i am adnan";
String IV = "0123456789abcdef";
// System.out.println(value);
// log.info(value);
IvParameterSpec iv = new IvParameterSpec(IV.getBytes());
// IvParameterSpec iv = new IvParameterSpec(arr);
// System.out.println(key);
SecretKeySpec skeySpec = new SecretKeySpec(decoder.decode(key), "AES");
// System.out.println(skeySpec);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
// System.out.println("ddddddddd"+IV);
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
// System.out.println(cipher.getIV());
byte[] encrypted = cipher.doFinal(value.getBytes());
String encryptedString = Base64.getEncoder().encodeToString(encrypted);
System.out.println("encrypted string,,,,,,,,,,,,,,,,,,,: " + encryptedString);
// vars.put("input-1",encryptedString);
// log.info("beanshell");
}catch (Exception e){
System.out.println(e.getMessage());
}
}
}
我正在做一个文本加密和解密项目(跟随Struts 2)
每当我输入密码和纯文本时,我都会收到无效的 AES 密钥长度错误。
服务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();
}
}
这是错误
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)
一般要知道的事情:
- 密钥!=密码
SecretKeySpec
需要密钥,而不是密码。见下文
- 这可能是由于政策限制导致无法使用 32 字节密钥。查看其他答案
你的情况
第 1 个问题:您传递的是密码而不是密钥。
AES 仅支持 16、24 或 32 字节的密钥大小。您要么需要准确提供该金额,要么从您输入的内容中得出密钥。
有多种方法可以从密码导出密钥。 Java 为此提供了 PBKDF2 实现。
我用埃里克森的answer画了一幅完整的图(只有加密,因为解密是相似的,但包括拆分密文):
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;
其他注意事项:
- 始终使用完全限定的密码名称。
AES
在这种情况下是不合适的,因为不同的 JVMs/JCE 供应商可能对操作模式和填充使用不同的默认值。使用AES/CBC/PKCS5Padding
。不要使用 ECB 模式,因为它在语义上不安全。 - 如果您不使用 ECB 模式,那么您需要将 IV 与密文一起发送。这通常是通过将 IV 作为密文字节数组的前缀来完成的。 IV 是自动为您创建的,您可以通过
cipherInstance.getIV()
. 获取它
- 无论何时发送内容,您都需要确保它在发送过程中没有被更改。很难用 MAC 正确地实现加密。我建议您使用像 CCM 或 GCM 这样的身份验证模式。
我遇到了同样的问题,然后我将密钥设为 16 字节,现在它工作正常。创建您的密钥恰好 16 字节。肯定有用。
您可以验证密钥长度限制:
int maxKeyLen = Cipher.getMaxAllowedKeyLength("AES");
System.out.println("MaxAllowedKeyLength=[" + maxKeyLen + "].");
您可以使用此代码,此代码用于 AES-256-CBC,或者您可以将其用于其他 AES 加密。密钥长度错误主要出现在256位加密中。
此错误是由于我们在 SecretKeySpec 中传递的编码或字符集名称所致。假设,在我的例子中,我的密钥长度为 44,但我无法使用这个长密钥来加密我的文本; Java 抛出一个无效密钥长度的错误。因此,我在函数中将我的密钥作为 BASE64 传递,并将我的 44 长度密钥转换为 32 字节,这是 256 位加密所必需的。
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.MessageDigest;
import java.security.Security;
import java.util.Base64;
public class Encrypt {
static byte [] arr = {1,2,3,4,5,6,7,8,9};
// static byte [] arr = new byte[16];
public static void main(String...args) {
try {
// System.out.println(Cipher.getMaxAllowedKeyLength("AES"));
Base64.Decoder decoder = Base64.getDecoder();
// static byte [] arr = new byte[16];
Security.setProperty("crypto.policy", "unlimited");
String key = "Your key";
// System.out.println("-------" + key);
String value = "Hey, i am adnan";
String IV = "0123456789abcdef";
// System.out.println(value);
// log.info(value);
IvParameterSpec iv = new IvParameterSpec(IV.getBytes());
// IvParameterSpec iv = new IvParameterSpec(arr);
// System.out.println(key);
SecretKeySpec skeySpec = new SecretKeySpec(decoder.decode(key), "AES");
// System.out.println(skeySpec);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
// System.out.println("ddddddddd"+IV);
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
// System.out.println(cipher.getIV());
byte[] encrypted = cipher.doFinal(value.getBytes());
String encryptedString = Base64.getEncoder().encodeToString(encrypted);
System.out.println("encrypted string,,,,,,,,,,,,,,,,,,,: " + encryptedString);
// vars.put("input-1",encryptedString);
// log.info("beanshell");
}catch (Exception e){
System.out.println(e.getMessage());
}
}
}