我正在尝试使用GCM模式进行加密和解密。不幸的是解密行不通。
我是否必须将相同的初始化向量用于加密和解密类别?我已经尝试过,失败了...
keyGen.init(128, random)
中的随机参数可以是问题吗?
加密代码:
public class AES128SymmetricEncryption {
private static final int GCM_NONCE_LENGTH = 12; // in bytes
private static final int GCM_TAG_LENGTH = 16; // in bytes
public static void encode (FileInputStream ciphertextSource, FileOutputStream plaintextDestination)
{
try {
int numRead;
SecureRandom random = SecureRandom.getInstanceStrong();
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(128, random);
SecretKey key = keyGen.generateKey();
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding", "BC");
GCMParameterSpec spec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, getIV(random));
cipher.init(Cipher.ENCRYPT_MODE, key, spec);
byte[] buf = new byte[2048];
while ((numRead = ciphertextSource.read(buf)) > 0) {
byte[] decryptedBlock = cipher.update(buf, 0, numRead);
plaintextDestination.write(decryptedBlock);
}
}
catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (plaintextDestination != null) {
ciphertextSource.close();
}
if (plaintextDestination != null) {
plaintextDestination.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static byte[] getIV(SecureRandom random) {
final byte[] nonce = new byte[GCM_NONCE_LENGTH];
random.nextBytes(nonce);
System.out.println(nonce);
return nonce;
}
public static void main(String[] args) throws GeneralSecurityException, IOException
{
Security.addProvider(new BouncyCastleProvider());
FileInputStream fis = new FileInputStream("C:/Users/roehrlef/Desktop/Test Data/Source Data/100KB.jpg");
FileOutputStream fos = new FileOutputStream("C:/Users/roehrlef/Desktop/Test Data/Encrypted Data/encrypted.jpg");
encode(fis, fos);
}
}
解密代码:
public class AES128SymmetricDecryption {
private static final int GCM_NONCE_LENGTH = 12; // in bytes
private static final int GCM_TAG_LENGTH = 16; // in bytes
public static void decode (FileInputStream ciphertextSource, FileOutputStream plaintextDestination)
{
try {
int numRead = 0;
SecureRandom random = SecureRandom.getInstanceStrong();
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(128, random);
SecretKey key = keyGen.generateKey();
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding", "BC");
GCMParameterSpec spec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, getIV(random));
cipher.init(Cipher.DECRYPT_MODE, key, spec);
CipherInputStream cis = new CipherInputStream(ciphertextSource, cipher);
byte[] buf = new byte[2048];
while ((numRead = cis.read(buf)) > 0) {
byte[] decryptedBlock = cipher.update(buf, 0, numRead);
plaintextDestination.write(decryptedBlock);
}
}
catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (plaintextDestination != null) {
ciphertextSource.close();
}
if (plaintextDestination != null) {
plaintextDestination.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static byte[] getIV(SecureRandom random) {
final byte[] nonce = new byte[GCM_NONCE_LENGTH];
random.nextBytes(nonce);
System.out.println(nonce);
return nonce;
}
public static void main(String[] args) throws GeneralSecurityException, IOException
{
Security.addProvider(new BouncyCastleProvider());
FileInputStream fis = new FileInputStream("C:/Users/roehrlef/Desktop/Test Data/Encrypted Data/encrypted.jpg");
FileOutputStream fos = new FileOutputStream("C:/Users/roehrlef/Desktop/Test Data/Decrypted Data/decrypted.jpg");
decode(fis, fos);
}
}
您正在使用KeyGenerator
两次;一次进行加密,一次进行解密。该类生成一个新的随机密钥。使用对称密码,您需要使用相同的密钥进行加密和解密(因此名称(。
通常,您应该将以下类用于以下目的:
对于对称键(例如AES,HMAC(:
-
KeyGenerator
:全新秘密(对称(键; -
SecretKeyFactory
:解码秘密(对称(键,例如由大多数密钥类实现的方法键#getEncoded((生成的;
以及不对称的公共/私钥对(例如RSA(:
-
KeyPairGenerator
:全新的公共/私人非对称密钥对; -
KeyFactory
:从存储的密钥格式解码公共/私有(非对称(键,例如,大多数密钥类实现的方法Key#getEncoded()
生成;
对称密钥和不对称键可以存储在密钥存储中:
-
KeyStore
:在键容器中存储密钥/证书,例如PKCS#12键存储;
最后还有其他一些创建密钥的选项:
-
KeyAgreement
:通过键协议功能建立钥匙,例如Diffie-Hellman密钥交换; -
Cipher#unwrap
:取消包装(解密(使用Cipher#wrap
(或其他平台上的类似功能(创建的密钥。
您可能应该存储并在KeyStore
中检索键 - 您可以将其加载/保存到文件。请注意,并非所有关键存储都相等地创建;Java 9扩展了PKCS#12密钥存储的功能,并使它们成为默认设置。您的代码还编码键并使用SecretKeyFactory
再次解码。
,或者您可以作弊并重用您在加密过程中生成的SecretKey
实例,然后以后实现密钥存储。这将是对测试目的的好方法。最后,您需要共享对称加密的密钥。
是的,双方都需要相同的IV。通常,它只是存储在密文的前面。IV对于每个加密应该是唯一的,因此您必须在那里使用随机数生成器。