Java AES / GCM解密失败



我正在尝试使用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对于每个加密应该是唯一的,因此您必须在那里使用随机数生成器。

最新更新