如何允许用错误的密钥解密而不抛出BadPaddingException?

时间:2014-11-28 04:36:41

标签: java encryption aes padding ecb

我在ECB中需要一个简单的AES密码系统。我有一个工作,在某种意义上说,连续两次给出相同的密钥,它将正确地加密和解密消息。

但是,如果我使用两个不同的密钥进行加密/解密,程序会抛出javax.crypto.BadPaddingException: Given final block not properly padded。我需要程序提供不正确的解密,大概看起来像一些加密的字符串。这是我的代码:

    public static byte[] encrypt(byte[] plaintext, String key) throws Exception {
        char[] password = key.toCharArray();
        byte[] salt = "12345678".getBytes();
        SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
        KeySpec spec = new PBEKeySpec(password, salt, 65536, 128);
        SecretKey tmp = factory.generateSecret(spec);
        SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, secret);
        byte[] ciphertext = cipher.doFinal(plaintext);  
        return ciphertext;
    }

    public static byte[] decrypt(byte[] ciphertext, String key) throws Exception {
        char[] password = key.toCharArray();
        byte[] salt = "12345678".getBytes();
        SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
        KeySpec spec = new PBEKeySpec(password, salt, 65536, 128);
        SecretKey tmp = factory.generateSecret(spec);
        SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, secret);
        byte[] plaintext = cipher.doFinal(ciphertext);  
        return plaintext;
    }

(注意:我已经意识到使用ECB的缺点,盐=" 12345678"等等,但目前我不关心它。)感谢任何人和所有帮助。

1 个答案:

答案 0 :(得分:1)

PKCS#5填充具有非常特定的结构,因此如果您希望使用错误的密钥进行解密而无错误,则无法继续使用它。

实现目标的一个好方法可能是使用流操作模式,而不是块模式。在流模式中,输入密钥用于产生看似随机数据的永不停止的流,其与密文进行异或以产生明文(反之亦然)。如果使用了错误的密钥,则会得到与原始明文大小相同的无意义数据。

这是一个基于原始代码的简单示例。我使用全零的IV,但您可能希望在适当的时候将其改进为随机值(注意:您需要将该值与密文一起存储)。

public static void main(String[] args) throws Exception {  
  byte[] plaintext = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
  byte[] ciphertext = encrypt(plaintext, "foo");

  byte[] goodDecryption = decrypt(ciphertext, "foo");
  byte[] badDecryption = decrypt(ciphertext, "bar");  

  System.out.println(DatatypeConverter.printHexBinary(goodDecryption));
  System.out.println(DatatypeConverter.printHexBinary(badDecryption));
}

public static SecretKey makeKey(String key) throws GeneralSecurityException {
  char[] password = key.toCharArray();
  byte[] salt = "12345678".getBytes();
  SecretKeyFactory factory =
      SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
  KeySpec spec = new PBEKeySpec(password, salt, 65536, 128);
  SecretKey tmp = factory.generateSecret(spec);
  return new SecretKeySpec(tmp.getEncoded(), "AES");
}

public static byte[] encrypt(byte[] plaintext, String key) throws Exception {
  SecretKey secret = makeKey(key);
  Cipher cipher = Cipher.getInstance("AES/OFB8/NoPadding");
  cipher.init(Cipher.ENCRYPT_MODE, secret, new IvParameterSpec(new byte[16]));
  return cipher.doFinal(plaintext);
}

public static byte[] decrypt(byte[] ciphertext, String key) throws Exception {
  SecretKey secret = makeKey(key);
  Cipher cipher = Cipher.getInstance("AES/OFB8/NoPadding");
  cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(new byte[16]));
  return cipher.doFinal(ciphertext);
}

输出:

00010203040506070809
5F524D4A8D977593D34C