当前位置: 首页 > 面试题库 >

Java AES加密和解密

齐招
2023-03-14
问题内容

我想使用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;
}

}


问题答案:

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

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

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

除非您有充分的理由不这样做,否则请使用JCE实现中已包含的填充方案。他们考虑了许多微妙和极端的情况,否则您将不得不自己意识到和应对这些情况。

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

一般来说,

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

不会someBytesretrievedBytes是相同的。

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

出现此问题的原因是,大多数(全部?)字符编码都无法将任意字节映射到有效字符。因此,当您String从密文创建密码时,String构造函数(使用字符编码将字节转换为字符)本质上必须丢弃一些字节,因为它对它们毫无意义。因此,当您从字符串中获取字节时,它们与您放入字符串中的字节不同。

在Java(以及通常在现代编程中)中,除非绝对知道要处理ASCII,否则不能假定一个字符=一个字节。这就是为什么要从任意字节构建字符串时需要使用base64(或类似的东西)的原因。



 类似资料:
  • 我有一个应用程序,需要在配置文件中存储一些秘密密码,如数据库和ftp密码/详细信息。我环顾四周,发现了许多使用AES的加密/解密解决方案,但我似乎不知道如何在不改变密钥的情况下使其工作。这意味着我可以加密和解密(使用相同的秘密密钥),但在重启等过程中保持持久性。我似乎无法让秘密钥匙保持不变。下面的示例显示了我的工作方法: 到目前为止还不错。然而,如果我运行它一次,我可能会得到'2Vhht/L80U

  • 以下示例有什么问题? 问题是解密字符串的第一部分是无意义的。不过,其余的都很好,我明白了...

  • 问题内容: 我迅速编写了一个应用程序,我需要AES加密和解密功能,我从另一个.Net解决方案中接收了加密数据,但是我找不到解决办法。 这是我的.net加密: 我需要迅速解密功能。 问题答案: 我找到了解决方案,它是一个很好的库。 跨平台256位AES加密/解密。 此项目包含在所有平台(C#,iOS,Android)上均可使用的256位AES加密的实现。关键目标之一是通过简单的实现使AES在所有平台

  • 问题内容: 我想将加密的密码存储在Java文件中。我在使用 javax.crypto 的解决方案中看到了一个问题,但是问题在于密钥是动态生成的,并且是随机的。 然后将在运行时在Java程序中获取并解密该密码。鉴于我要在文件中存储一个已经加密的密码-解密时我想要正确的文本。 有没有办法告诉javax.crypto方法: 可以将其替换为基于某个私钥生成的我自己的密钥吗? 谁能指出一些有关执行此操作的资

  • 我有这段代码,它基本上对两条纯文本消息进行加密,然后尝试解密,然后打印。问题是第一条消息恢复得很好,但第二条消息是垃圾。我从本教程下载了这段代码,然后将其修改为使用字符串而不是文件,因为我需要它通过套接字发送加密文本。所以其他endpoint不知道明文的长度,有没有办法找到长度,或者我必须以某种方式将明文的长度与密码一起发送? 现在,我认为解密的中断条件有问题。 另外,main()代码在概念上是否

  • 问题内容: 我正在使用JavaScript加密用户密码,如下所示: 它工作正常,但现在我正尝试在服务器端的PHP中像这样解密: 它根本不起作用,解密后的密码字符串看起来很奇怪: 有用的注释后,这是我的JavaScript代码的当前状态: 我正在将saltHex和CipherTextHex发送到PHP服务器,并且正在使用mcrypt_decrypt(),如下所示: 仍然无法使用此更新的代码。 有人可