分享密码(一个char[]
)和盐(一个byte[]
- 由SecureRandom
选择的8个字节的盐是一个好的选择,不需要保密)给接收方。然后从这些信息中派生出一个好的密钥:
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
KeySpec spec = new PBEKeySpec(password, salt, 65536, 256);
SecretKey tmp = factory.generateSecret(spec);
SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");
这些神奇数字(可以在某个地方定义为常量)65536和256是密钥派生迭代次数和密钥大小。
密钥派生函数被迭代以需要大量的计算工作,这可以防止攻击者快速尝试许多不同的密码。迭代次数可以根据可用的计算资源进行更改。
密钥大小可以减小到128位,这仍然被认为是“强大”的加密,但如果发现削弱AES的攻击,则不会给出太多安全余地。
在适当的块链接模式下使用,相同的派生密钥可以用于加密许多消息。在
Cipher Block Chaining(CBC)中,为每个消息生成一个随机初始化向量(IV),即使明文相同,也会产生不同的密文。CBC可能不是您可用的最安全模式(请参见下面的AEAD);有许多其他具有不同安全属性的模式,但它们都使用类似的随机输入。无论如何,每个加密操作的输出都是密文
和初始化向量:
/* Encrypt the message. */
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
cipher.init(Cipher.ENCRYPT_MODE, secret)
AlgorithmParameters params = cipher.getParameters()
byte[] iv = params.getParameterSpec(IvParameterSpec.class).getIV()
byte[] ciphertext = cipher.doFinal("Hello, World!".getBytes(StandardCharsets.UTF_8))
存储ciphertext
和iv
。在解密时,使用相同的盐和迭代参数使用密码以完全相同的方式重新生成SecretKey
。使用此密钥和与消息一起存储的初始化向量初始化密码器:
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(iv));
String plaintext = new String(cipher.doFinal(ciphertext), StandardCharsets.UTF_8);
System.out.println(plaintext);
Java 7包含了
支持AEAD密码模式的API,而随OpenJDK和Oracle发行版一起提供的"SunJCE"提供程序从Java 8开始实现了这些功能。其中一个这些模式强烈建议用来替代CBC;它将保护数据的完整性以及隐私。
一个带有消息“非法密钥大小或默认参数”的
java.security.InvalidKeyException
表示密码学强度受到限制;无限制的强度管辖策略文件没有放置在正确的位置。在JDK中,它们应该放置在
${jdk}/jre/lib/security
下。
根据问题描述,似乎策略文件没有正确安装。系统可以轻松拥有多个Java运行时环境;请仔细检查以确保使用了正确的位置。