Подтвердить что ты не робот

Что вызывает ошибку "java.security.InvalidKeyException: Параметры отсутствуют"?

Я пытаюсь зашифровать и дешифровать строку с помощью AES, но получить ошибку, я не знаю, как ее решить. Это код:

public class EncryptionTest{

public static void main(String[] args) {        
    String encrypt = new String(encrypt("1234567890123456"));
    System.out.println("decrypted value:" + (decrypt("ThisIsASecretKey",encrypt)));
}

public static String encrypt(String value) {
    try {
        byte[] raw = new byte[]{'T', 'h', 'i', 's', 'I', 's', 'A', 'S', 'e', 'c', 'r', 'e', 't', 'K', 'e', 'y'};
        SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
        byte[] encrypted = cipher.doFinal(value.getBytes());
        System.out.println("encrypted string:" + (new String(encrypted)));
        return new String(skeySpec.getEncoded());
    } catch (Exception ex) {
        ex.printStackTrace();
    }
    return null;
}

public static String decrypt(String key, String encrypted) {
    try {
        SecretKeySpec skeySpec = new SecretKeySpec(Base64.decodeBase64(key), "AES");
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(skeySpec.getEncoded(),"AES"));
            (*)
        byte[] original = cipher.doFinal(Base64.decodeBase64(encrypted));
        original.toString();
    } catch (Exception ex) {
        ex.printStackTrace();
    }
    return null;
}  
}

Когда я запускаю его, значения "decription" равны нулю. Он терпит неудачу перед (***)!!

Это дает мне исключение:

java.security.InvalidKeyException: Parameters missing
    at com.sun.crypto.provider.CipherCore.init(CipherCore.java:388)
    at com.sun.crypto.provider.AESCipher.engineInit(AESCipher.java:186)
    at javax.crypto.Cipher.implInit(Cipher.java:787)
    at javax.crypto.Cipher.chooseProvider(Cipher.java:849)
    at javax.crypto.Cipher.init(Cipher.java:1213)
    at javax.crypto.Cipher.init(Cipher.java:1153)
    at firma.XmlEncryptionTest.decrypt(EncryptionTest.java:63)
    at firma.XmlEncryptionTest.main(EncryptionTest.java:41)

где линия 63 - таковая перед (***). Я не знаю, что я делаю неправильно и как решать. Я огляделся по интернету, но не выяснил, какой кусок этого недостающего параметра

4b9b3361

Ответ 1

Основная проблема в вашем коде была вызвана неспособностью указать значение IV. Вы должны указать значение IV при выполнении шифрования в режиме CBC и использовать это же значение при выполнении дешифрования в режиме CBC.

Другая проблема - это сочетание и совпадение создания строк из массивов байт и кодировки base64. Вы также возвращаете null из своего метода расшифровки каждый раз. Даже если вы имели в виду return original.toString();, это все еще неправильно (потому что toString() не делает то, что вы хотите, в байтовом массиве).

Ниже приведена улучшенная версия вашего кода. Это далеко не оптимально, но оно компилируется и работает. Вам нужно улучшить это, чтобы использовать случайный IV. Кроме того, если вы планируете выводить ключи из паролей, не просто получайте байты, используйте функцию деривации, такую ​​как PBKDF2. Вы можете увидеть пример использования PBKDF2 в источнике JNCryptor.

public class EncryptionTest {

  public static void main(String[] args) {
    try {

      String key = "ThisIsASecretKey";
      byte[] ciphertext = encrypt(key, "1234567890123456");
      System.out.println("decrypted value:" + (decrypt(key, ciphertext)));

    } catch (GeneralSecurityException e) {
      e.printStackTrace();
    }
  }

  public static byte[] encrypt(String key, String value)
      throws GeneralSecurityException {

    byte[] raw = key.getBytes(Charset.forName("UTF-8"));
    if (raw.length != 16) {
      throw new IllegalArgumentException("Invalid key size.");
    }

    SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    cipher.init(Cipher.ENCRYPT_MODE, skeySpec,
        new IvParameterSpec(new byte[16]));
    return cipher.doFinal(value.getBytes(Charset.forName("UTF-8")));
  }

  public static String decrypt(String key, byte[] encrypted)
      throws GeneralSecurityException {

    byte[] raw = key.getBytes(Charset.forName("UTF-8"));
    if (raw.length != 16) {
      throw new IllegalArgumentException("Invalid key size.");
    }
    SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");

    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    cipher.init(Cipher.DECRYPT_MODE, skeySpec,
        new IvParameterSpec(new byte[16]));
    byte[] original = cipher.doFinal(encrypted);

    return new String(original, Charset.forName("UTF-8"));
  }
}

Ответ 2

Если вы используете режим блочной цепочки, например CBC, вам также необходимо предоставить IvParameterSpec для Cipher.

public class EncryptionTest {

static byte[] raw = new byte[]{'T', 'h', 'i', 's', 'I', 's', 'A', 'S', 'e', 'c', 'r', 'e', 't', 'K', 'e', 'y'};

static SecureRandom rnd = new SecureRandom();

static IvParameterSpec iv = new IvParameterSpec(rnd.generateSeed(16));

public static void main(String[] args) {
    String encrypt = encrypt("1234567890123456");
    System.out.println("decrypted value:" + (decrypt("ThisIsASecretKey", encrypt)));
}

public static String encrypt(String value) {
    try {

        SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
        cipher.init(Cipher.ENCRYPT_MODE, skeySpec,iv);
        byte[] encrypted = cipher.doFinal(value.getBytes());
        System.out.println("encrypted string:" + Base64.encodeBase64String(encrypted));
        return Base64.encodeBase64String(encrypted);
    } catch (Exception ex) {
        ex.printStackTrace();
    }
    return null;
}

public static String decrypt(String key, String encrypted) {
    try {
        SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes(), "AES");
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
        cipher.init(Cipher.DECRYPT_MODE, skeySpec,iv);
        byte[] original = cipher.doFinal(Base64.decodeBase64(encrypted));

        return new String(original);
    } catch (Exception ex) {
        ex.printStackTrace();
    }
    return null;
}

}

Ответ 3

Несмотря на то, что слишком поздно, но все же я даю свое решение для других. Пожалуйста, обратитесь к рабочему коду для шифрования и дешифрования данных.

public static byte[] encrypt(String value) {
        byte[] encrypted = null;
        try {

            byte[] raw = new byte[]{'T', 'h', 'i', 's', 'I', 's', 'A', 'S', 'e', 'c', 'r', 'e', 't', 'K', 'e', 'y'};
            Key skeySpec = new SecretKeySpec(raw, "AES");
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            byte[] iv = new byte[cipher.getBlockSize()];

            IvParameterSpec ivParams = new IvParameterSpec(iv);
            cipher.init(Cipher.ENCRYPT_MODE, skeySpec,ivParams);
            encrypted  = cipher.doFinal(value.getBytes());
            System.out.println("encrypted string:" + encrypted.length);

        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return encrypted;
    }

    public static  byte[]  decrypt(byte[] encrypted) {
         byte[] original = null;
         Cipher cipher = null;
        try {
            byte[] raw = new byte[]{'T', 'h', 'i', 's', 'I', 's', 'A', 'S', 'e', 'c', 'r', 'e', 't', 'K', 'e', 'y'};
            Key key = new SecretKeySpec(raw, "AES");
            cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            //the block size (in bytes), or 0 if the underlying algorithm is not a block cipher
            byte[] ivByte = new byte[cipher.getBlockSize()];
            //This class specifies an initialization vector (IV). Examples which use
            //IVs are ciphers in feedback mode, e.g., DES in CBC mode and RSA ciphers with OAEP encoding operation.
            IvParameterSpec ivParamsSpec = new IvParameterSpec(ivByte);
            cipher.init(Cipher.DECRYPT_MODE, key, ivParamsSpec);
            original= cipher.doFinal(encrypted);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return original;
    }