JCE 密码通常非常基础。如果您需要包括完整性和密钥测试在内的全功能保护,则需要将它们结合起来。和往常一样,最好不要自己设备。所以最好选择更高级的格式,如 PKCS7/12 或 PGP。
根据使用的填充,当您尝试使用错误的密钥对其进行解密时,某些密码会给您一个 PaddingException。为了更强大的完整性检查,我将使用由 HMAC 字节组成的填充。
JCE 中包含一个相当完整的方法,它是 AESWrap 算法。它需要填充数据,但会确保完整性。它最好与 RFC 3537 中描述的长度字节结合使用。请注意,这仅适用于较小数量的机密(如对称密钥)。 RFC3537 填充限制为 255 个字节。
要将其与密码派生密钥一起使用,您可以使用:
char[] pass = ... // your password
byte[] codeBytes = ... // up to 255 bytes you want to protect
// generate wrapping key from password
SecretKeyFactory f = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
SecureRandom rand = SecureRandom.getInstance("SHA1PRNG");
byte[] salt = new byte[16]; rand.nextBytes(salt);
SecretKey kek = f.generateSecret(new PBEKeySpec(pass, salt, 1000, 128));
kek = new SecretKeySpec(password.getEncoded(), "AES"); // convert into AES
// RFC3537 padding (lengthbyte)
byte[] wrappedCodeBytes = new byte[codeBytes + 1 % 8];
System.arraycopy(codeBytes,0,wrappedCodeBytes,1,wrappedCodeBytes.length);
paddedCodeBytes[0]=(byte)codeBytes.length;
byte[] pad = new byte[paddedCodeBytes.length - codeBytes.length -1]; rand.nextBytes(pad);
System.arraycopy(pad,0,paddedCodeBytes,codeBytes.length+1,pad.length);
// AESWrap is WRAP_MODE:needs a SecretKey
SecretKey paddedCodeKey = new SecretKeySpec(paddedCodeBytes, "RAW");
// now wrap the password with AESWrap kek is 128 bit
Cipher c = Cipher.getInstance("AESWrap"); // default IV
c.init(Cipher.WRAP_MODE, kek);
byte[] result = c.warp(paddedCodeKey);
解包留给读者作为练习 :) 示例代码使用 128 位密钥大小,因为无论如何不能从 PBKDF2 获得更多熵。
请注意,这很可能会检测到错误的密码,一些批评者会认为这是 AESWrap 的一个弱点。