Java AES 加密和解密

2022-09-01 22:52:06

我想使用128位AES加密和16字节密钥加密和解密密码。我在解密值时遇到错误。我在解密时是否遗漏了任何内容?javax.crypto.BadPaddingException

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

public 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");
        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 (NoSuchAlgorithmException ex) {
        Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
    } catch (IllegalBlockSizeException ex) {
        Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
    } catch (BadPaddingException ex) {
        Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
    } catch (InvalidKeyException ex) {
        Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
    } catch (NoSuchPaddingException ex) {
        Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
    }
    return null;
}

public String decrypt(String key, String encrypted) {
    try {
        SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes(), "AES");
        Cipher cipher = Cipher.getInstance("AES");
        cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(skeySpec.getEncoded(), "AES"));
        //getting error here
        byte[] original = cipher.doFinal(encrypted.getBytes());
        return new String(original);
    } catch (IllegalBlockSizeException ex) {
        Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
    } catch (BadPaddingException ex) {
        Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
    } catch (InvalidKeyException ex) {
        Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
    } catch (NoSuchAlgorithmException ex) {
        Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
    } catch (NoSuchPaddingException ex) {
        Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
    }
    return null;
}  

错误信息

encrypted string:�Bj�.�Ntk�F�`�
encrypted key:ThisIsASecretKey
decrypted value:null
May 25, 2012 12:54:02 PM bean.Test decrypt
SEVERE: null
javax.crypto.BadPaddingException: Given final block not properly padded
at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..)
at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..)
at com.sun.crypto.provider.AESCipher.engineDoFinal(DashoA13*..)
at javax.crypto.Cipher.doFinal(DashoA13*..)
at bean.Test.decrypt(Test.java:55)
at bean.Test.main(Test.java:24)

最后,我正在使用基于@QuantumMechanic答案的以下解决方案

public class Test {

public String encryptionKey;

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

public String encrypt(String value) {
    try {
        // Get the KeyGenerator
        KeyGenerator kgen = KeyGenerator.getInstance("AES");
        kgen.init(256);
        // Generate the secret key specs.
        SecretKey skey = kgen.generateKey();
        byte[] raw = skey.getEncoded();
        String key = new Base64().encodeAsString(raw);
        this.encryptionKey = key;
        System.out.println("------------------Key------------------");
        System.out.println(key);
        System.out.println("--------------End of Key---------------");
        SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
        Cipher cipher = Cipher.getInstance("AES");
        cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
        String encrypt = (new Base64()).encodeAsString(cipher.doFinal(value.getBytes()));
        System.out.println("encrypted string:" + encrypt);
        return encrypt;
    } catch (NoSuchAlgorithmException ex) {
        Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
    } catch (IllegalBlockSizeException ex) {
        Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
    } catch (BadPaddingException ex) {
        Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
    } catch (InvalidKeyException ex) {
        Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
    } catch (NoSuchPaddingException ex) {
        Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
    }
    return null;
}

public String decrypt(String key, String encrypted) {
    try {
        Key k = new SecretKeySpec(Base64.getDecoder().decode(key), "AES");
        Cipher c = Cipher.getInstance("AES");
        c.init(Cipher.DECRYPT_MODE, k);
        byte[] decodedValue = Base64.getDecoder().decode(encrypted);
        byte[] decValue = c.doFinal(decodedValue);
        String decryptedValue = new String(decValue);
        return decryptedValue;
    } catch (IllegalBlockSizeException ex) {
        Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
    } catch (BadPaddingException ex) {
        Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
    } catch (InvalidKeyException ex) {
        Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
    } catch (NoSuchAlgorithmException ex) {
        Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
    } catch (NoSuchPaddingException ex) {
        Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
    }
    return null;
}

}


答案 1

如果对于块密码,您不打算使用包含填充方案的转换,则需要使明文中的字节数是密码块大小的整数倍。Cipher

因此,要么将纯文本填充到 16 字节的倍数(即 AES 块大小),要么在创建对象时指定填充方案。例如,您可以使用:Cipher

Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");

除非你有充分的理由不这样做,否则请使用已经是JCE实现一部分的填充方案。他们已经想出了许多微妙之处和角落案例,否则你必须自己意识到和处理。


好的,你的第二个问题是你用来保存密文。String

通常

String s = new String(someBytes);
byte[] retrievedBytes = s.getBytes();

不会有和相同。someBytesretrievedBytes

如果你想/必须将密文保存在 中,base64 首先对密文字节进行编码,然后从 base64 编码的字节构造 。然后,当您解密时,您将从 中获取 base64 编码的字节,然后 base64 解码它们以获取真正的密文,然后对其进行解密。StringStringgetBytes()String

此问题的原因是大多数(所有?)字符编码无法将任意字节映射到有效字符。因此,当您从密文创建时,构造函数(应用字符编码将字节转换为字符)基本上必须丢弃一些字节,因为它无法理解它们。因此,当您从字符串中获取字节时,它们与您放入字符串中的字节不同。StringString

在Java中(以及一般的现代编程中),你不能假设一个字符=一个字节,除非你绝对知道你正在处理ASCII。这就是为什么如果你想从任意字节构建字符串,你需要使用base64(或类似的东西)。


答案 2
import javax.crypto.*;    
import java.security.*;  
public class Java {

private static SecretKey key = null;         
   private static Cipher cipher = null; 

   public static void main(String[] args) throws Exception
   {

      Security.addProvider(new com.sun.crypto.provider.SunJCE());

      KeyGenerator keyGenerator =
         KeyGenerator.getInstance("DESede");
      keyGenerator.init(168);
      SecretKey secretKey = keyGenerator.generateKey();
      cipher = Cipher.getInstance("DESede");

      String clearText = "I am an Employee";
      byte[] clearTextBytes = clearText.getBytes("UTF8");

      cipher.init(Cipher.ENCRYPT_MODE, secretKey);
      byte[] cipherBytes = cipher.doFinal(clearTextBytes);
      String cipherText = new String(cipherBytes, "UTF8");

      cipher.init(Cipher.DECRYPT_MODE, secretKey);
      byte[] decryptedBytes = cipher.doFinal(cipherBytes);
      String decryptedText = new String(decryptedBytes, "UTF8");

      System.out.println("Before encryption: " + clearText);
      System.out.println("After encryption: " + cipherText);
      System.out.println("After decryption: " + decryptedText);
   }
}


// Output

/*
Before encryption: I am an Employee  
After encryption: }?ス?スj6?スm?スZyc?ス?ス*?ス?スl#l?スdV  
After decryption: I am an Employee  
*/

推荐