我有一个代表对称密钥的字符串,通过使用 Hashicorp Vault 获得(这实际上可能并不重要)。我需要此密钥来加密大文件,因此我无法将文件直接发送到 Vault 要求其加密数据。我想在本地执行此操作,因此我要求 Vault 为我创建一个对称密钥(通过使用 transit/datakey/plaintext/ 端点)。我现在有一个 44 字节长的对称密钥(及其密文),由 aes256_gcm96 算法生成。因此,据我所知,我的 32 字节密钥是用 96 位(12 字节)gcm 块包装的。 现在我想使用这个密钥来加密我的数据,但是密钥太长而无法做到这一点,所以我需要以某种方式解开它或调用一些接受输入这样的密钥的函数。我试图使用 Cipher 来加密我的数据。这就是我到目前为止所做的(错误的)
byte[] datakeyByteArray = mySymmetricKey.getBytes();
SecretKey secretKey = new SecretKeySpec(datakeyByteArray, "AES_256");
Cipher cipher = Cipher.getInstance("AES_256/GCM/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);`
调用init函数时,显然抛出异常:java.security.InvalidKeyException: The key must be 32 bytes
通过什么操作可以获得有效的密钥?
谢谢你。
您已经从 @Saptarshi Basu 获得了一个链接,该链接一般显示了如何使用 AES GCM 加密数据。正如您在我的代码中看到的那样,这样做并没有什么真正“神秘”的地方,但有一些陷阱可以运行。
让我们从最重要的信息开始 - 什么是加密密钥?您从 Hashicorp 收到了一个 44 字节长的字符串,它是纯 32 字节长的 AES GCM 密钥,但采用 Base64 编码。要获得可用于 Java 加密的密钥,您需要将密钥解码为字节数组,如下所示:
String keyBase64 = "VxJWkOYm2F5z1nF1th9zreS6ZAZMFkCq0c/Ik460ayw=";
byte[] key = Base64.getDecoder().decode(keyBase64);
我们需要的第二个信息是 AES 模式 - 您将其正确命名为 AES GCM 模式,并且当您向 Java 提供 32 字节 = 256 位长密钥时,它就是所请求的 AES GCM 256 算法/模式。
AES GCM 加密需要第三个参数,它是 nonce (或有时称为初始化向量)。 Hashicorp 告诉您使用 96 位 = 12 字节长的随机数。出于安全原因,每次加密时使用不同的随机数非常重要,因此最好使用(安全)随机生成的随机数:
byte[] nonceRandom = new byte[12];
SecureRandom secureRandom = new SecureRandom();
secureRandom.nextBytes(nonceRandom);
现在我们已准备好加密并将所有数据放在一起,执行“.doFinal”步骤,我们会收到一个带有密文的字节数组。但是停下来 - 我们需要以这种方式将使用的随机数和密文连接到更大的密文WithNonce:
nonce | ciphertext
只需将随机数和密文复制到新的字节数组即可。然后将此“ciphertextWithNonce”进行 Base64 编码为最终的 ciphertextBase64,并出于上传原因写入文件。
如果您将自己的密钥粘贴到程序的开头并运行它,您将收到一个名为“hashicorp_test.enc”的文件,该文件已准备好上传到您的故障。
这是一个示例输出(您的输出会有所不同,因为存在随机元素):
Hashicorp Vault AES GCM encryption
ciphertext: /YB+kfVlIhMowLrsnndD737o2CcyWMfr4xnAADnCBSNCSvMG25aR8UzU2ta8wLwdnHfcago/25KFJ2ky95wpFtsCNE63xRs=
ciphertext written to file: hashicorp_test.enc
used key: VxJWkOYm2F5z1nF1th9zreS6ZAZMFkCq0c/Ik460ayw=
如果您想查看此代码在在线编译器中运行,请点击以下链接: https://repl.it/@javacrypto/SoHashicorpVaultAesGcmEncryption
这是一个“概念证明”,一般展示如何执行加密,但它缺少一些关键点,我懒得做你的工作:-)。
安全警告:此代码没有异常处理,仅用于教育目的。
代码:
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Base64;
public class Hashicorp_Aes_Gcm_encryption {
public static void main(String[] args) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, IOException {
System.out.println("Hashicorp Vault AES GCM encryption");
// https://stackoverflow.com/questions/64714527/how-can-i-encrypt-data-with-an-already-generated-aes-256-gcm-96-key-coming-from
// paste your key here:
String keyBase64 = "VxJWkOYm2F5z1nF1th9zreS6ZAZMFkCq0c/Ik460ayw=";
// filename with ciphertext for upload
String filename = "hashicorp_test.enc";
// my sample plaintext
String plaintext = "The quick brown fox jumps over the lazy dog";
// aes gcm encryption
// decode key
byte[] key = Base64.getDecoder().decode(keyBase64);
// generate random nonce
byte[] nonceRandom = new byte[12];
SecureRandom secureRandom = new SecureRandom();
secureRandom.nextBytes(nonceRandom);
// calculate specs
SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(16 * 8, nonceRandom);
// initialize cipher
Cipher cipher = Cipher.getInstance("AES/GCM/PKCS5Padding");//NOPadding
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, gcmParameterSpec);
// encrypt
byte[] ciphertext = cipher.doFinal(plaintext.getBytes(StandardCharsets.UTF_8));
// concentenate iv + ciphertext
int ciphertextWithNonceLength = nonceRandom.length + ciphertext.length;
byte[] ciphertextWithNonce = new byte[ciphertextWithNonceLength];
System.arraycopy(nonceRandom, 0, ciphertextWithNonce, 0, nonceRandom.length);
System.arraycopy(ciphertext, 0, ciphertextWithNonce, nonceRandom.length, ciphertext.length);
String ciphertextBase64 = Base64.getEncoder().encodeToString(ciphertextWithNonce);
System.out.println("ciphertext: " + ciphertextBase64);
// save encrypted data to a file
Files.write(Paths.get(filename), ciphertextBase64.getBytes(StandardCharsets.UTF_8));
System.out.println("ciphertext written to file: " + filename);
System.out.println("used key: " + keyBase64);
}
}