如何修复无效的AES密钥长度?

Ris*_*yay 30 java encryption aes

我正在eclipse 上开展基于Web的文本加密和解密项目(遵循Struts 2)

每当我输入密码和纯文本时,我都会收到无效的AES密钥长度错误.

服务类(SymAES.java)

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

这是Action类(SymEncrypt.java)

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)
Run Code Online (Sandbox Code Playgroud)

这就是错误

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

Art*_* B. 60

AES仅支持16,24或32字节的密钥大小.您需要准确提供该金额,或者从键入的内容中获取密钥.

从密码短语中导出密钥的方法有很多种.Java为此目的提供了PBKDF2实现.

我使用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;
Run Code Online (Sandbox Code Playgroud)

其他要记住的事项:

  • 始终使用完全限定的密码名称.SecretKeySpec在这种情况下是不合适的,因为不同的JVM/JCE提供程序可能会对操作模式和填充使用不同的默认值.使用AES.不要使用ECB模式,因为它在语义上不安全.
  • 如果您不使用ECB模式,则需要将IV与密文一起发送.这通常通过将IV添加到密文字节数组来完成.IV会自动为您创建,您可以通过它AES/CBC/PKCS5Padding.
  • 每当你发送东西时,你需要确保它一直没有改变.很难用MAC正确实现加密.我建议您使用CCM或GCM等经过身份验证的模式.

  • 需要记住的另一件事是,对于超过128位(16字节)的密钥大小,需要使用适当的权限设置jre.有关详细信息,请参阅此问题和答案:/sf/ask/453713921/.在可以通过`Cipher.getMaxAllowedKeyLength("AES")`访问的代码中,它返回一个不是字节的值. (2认同)

小智 6

您可以验证密钥长度限制:

int maxKeyLen = Cipher.getMaxAllowedKeyLength("AES");
System.out.println("MaxAllowedKeyLength=[" + maxKeyLen + "].");
Run Code Online (Sandbox Code Playgroud)

  • 然后什么 ? (3认同)

vin*_*tel 5

我遇到了同样的问题,然后我将密钥设置为16个字节,并且现在可以正常工作。正好创建16个字节的密钥。它肯定会工作。