【问题标题】:Java AES / GCM decryption failsJava AES/GCM 解密失败
【发布时间】:2018-03-29 10:28:13
【问题描述】:

我正在尝试使用 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);
    }
}

【问题讨论】:

  • 请点击上面的“edited X ago”查看我的编辑,以查看我所做的更改。注意 keyGen.init 周围的内联代码使用反引号、代码的额外缩进和额外的空格。

标签: java encryption aes encryption-symmetric aes-gcm


【解决方案1】:

您使用了两次KeyGenerator;一次用于加密,一次用于解密。此类生成一个新的随机密钥。使用对称密码,您需要使用相同的密钥进行加密和解密(因此得名)。

一般来说,您应该将以下类用于以下目的:

对于对称密钥(例如 AES、HMAC):

  • KeyGenerator:全新的秘密(对称)密钥;
  • SecretKeyFactory:解码秘密(对称)密钥,例如由大多数密钥类实现的 Key#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 对于每个加密应该是唯一的,所以你必须使用那里的随机数生成器。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-10-21
    • 2015-03-29
    • 1970-01-01
    • 1970-01-01
    • 2015-02-18
    • 1970-01-01
    • 2021-09-30
    相关资源
    最近更新 更多