为什么 AES 产生不同的结果而 DES 不产生

Why AES Produce Different result and Why DES not Produce

我正在尝试更改现有项目的加密算法。但我有点困惑。当我使用 "PBEWithHmacSHA512AndAES_256" 作为参数时,它会产生不同的结果,但是当我使用 "PBEWithMD5AndDES" 作为参数时,它会产生相同的结果。我的职能是:

 public static synchronized String encrypt1(final String textToEncrypt, final String pathPublicKey) throws Exception {
    final KeySpec pbeKeySpec = new PBEKeySpec(DbKeyHandler.getDbKey(pathPublicKey).toCharArray());
    final SecretKey pbeKey = SecretKeyFactory.getInstance("PBEWithMD5AndDES").generateSecret(pbeKeySpec);
    // Prepare the parameter to the ciphers
    final AlgorithmParameterSpec paramSpec = new PBEParameterSpec(salt, iterationCount);
    final Cipher cipher = Cipher.getInstance(pbeKey.getAlgorithm());

    // Create the ciphers
    cipher.init(Cipher.ENCRYPT_MODE, pbeKey, paramSpec);

    // Encode the string into bytes using utf-8
    final byte[] utf8 = textToEncrypt.getBytes("UTF8");

    // Encrypt
    final byte[] enc = cipher.doFinal(utf8);

    // Encode bytes to base64 to get a string
    return new sun.misc.BASE64Encoder().encode(enc);
}


public static synchronized String encrypt2 (final String textToEncrypt, final String pathPublicKey) throws Exception {
    final KeySpec pbeKeySpec = new PBEKeySpec(DbKeyHandler.getDbKey(pathPublicKey).toCharArray());
    final SecretKey pbeKey = SecretKeyFactory.getInstance("PBEWithHmacSHA512AndAES_256").generateSecret(pbeKeySpec);
    // Prepare the parameter to the ciphers
    final AlgorithmParameterSpec paramSpec = new PBEParameterSpec(salt, iterationCount);
    final Cipher cipher = Cipher.getInstance(pbeKey.getAlgorithm());

    // Create the ciphers
    cipher.init(Cipher.ENCRYPT_MODE, pbeKey, paramSpec);

    // Encode the string into bytes using utf-8
    final byte[] utf8 = textToEncrypt.getBytes("UTF8");

    // Encrypt
    final byte[] enc = cipher.doFinal(utf8);

    // Encode bytes to base64 to get a string
    return new sun.misc.BASE64Encoder().encode(enc);
}

任何建议和想法都会帮助我弄清楚这里发生了什么。

这也会产生不同的结果:

    KeyStore keyStore = KeyStore.getInstance("JCEKS");
    keyStore.load(new FileInputStream((pathOfJKSfile)), password.toCharArray());
    Key key = keyStore.getKey(keyName, keyPass.toCharArray());
    byte[] raw = key.getEncoded();
    SecretKeySpec secretKeySpec = new SecretKeySpec(raw, "PBEWithHmacSHA512AndAES_256");
    final AlgorithmParameterSpec paramSpec = new PBEParameterSpec(salt, ITERATIONS);

    final Cipher cipherEncrypt = Cipher.getInstance(ALGORITHM);
    cipherEncrypt.init(Cipher.ENCRYPT_MODE, secretKeySpec, paramSpec);

    final byte[] enc = cipherEncrypt.doFinal(messageBytes);
    System.out.println( new sun.misc.BASE64Encoder().encode(enc));

而且我知道 cipher.init() 使用 "JceSecurity.RANDOM" 来产生不同的结果。

  • 这两种算法,PBEWithHmacSHA512AndAES_256PBEWithMD5AndDES,首先通过处理密码、salt 和迭代计数(使用 HmacSHA512MD5,然后使用此密钥加密纯文本(分别使用 AES-256DES,并生成 CBC-mode. When the Cipher-instance is initialized, a pseudo-random initialization vector (IV),这是 CBC-模式.

  • PBEWithHmacSHA512AndAES_256 的上下文中,IV 是 使用最高优先级已安装提供程序 SecureRandom 实现生成的,至少对于代码中使用的 Cipher#init() 方法(请注意,Cipher#init() 方法有多个重载,并且 SecureRandom 实例也可以显式传递)。 IE。每次 Cipher 初始化都会生成一个 new (随机)IV,因此加密文本总是不同的,即使对于相同的纯文本也是如此。因此,您的示例中的加密文本在此上下文中发生了变化。

  • PBEWithMD5AndDES的上下文中,IV 由密码、salt、迭代次数(当然还有MD5-哈希算法本身)。因此,在重复的情况下,IV 和密文不会改变(前提是密码、salt、迭代次数等相同)。因此,您的示例中的加密文本在此上下文中不会更改。

  • 在密码初始化期间生成一个新的随机 IV 对于 IV 的以下要求是有意义的:出于安全原因,CBC 模式下的 IV (顺便说一句,这也适用于其他模式)may only be used once under the same key。此外,IV 必须是不可预测的。

  • PBEWithMD5AndDES.

编辑:

  • 现在使用 IV 是标准的(出于安全原因)。可以在 Internet 上找到有关此主题的大量信息,例如here。下面我只介绍一些基本的东西。

  • 用于加密的 IV 必须以某种方式存储,因为解密需要它。 IV 不必保密,因此它通常与加密数据连接(例如,在加密数据之前)并与它们一起存储。在解密过程中,这两部分可以分开,因为 IV 的长度是已知的(AES 为 16 字节)。例如,对于加密方法中的连接,使用了类似以下的东西(让 ivenc 分别是带有 IV 和加密数据的字节数组):

    byte[] result = new byte[enc.length + iv.length];
    System.arraycopy(iv, 0, result, 0, iv.length);
    System.arraycopy(enc, 0, result, iv.length, enc.length);
    

    并且在解密方法中对应的对应物(请记住,AES 中 IV 的长度为 16 字节)。

  • 在加密方法中,IV 可以用 Cipher#getIV() 确定(这当然必须在调用 Cipher#init() 之后发生)。

  • 在解密方法中,您必须将 IV 传递给 PBEParameterSpec-ctor(例如,让 iv 成为带有 IV 的字节数组):

    IvParameterSpec ivSpec = new IvParameterSpec(iv); 
    AlgorithmParameterSpec paramSpec = new PBEParameterSpec(salt, iterationCount, ivSpec);
    
  • IV 的生成也可以发生在 Cipher-class 之外,参见例如Generating random IV for AES in Java。然后,您必须以与上述解密方法相同的方式在加密方法中传递该 IV。

  • 请注意,与 IV 相关时,必须考虑一些要点,例如使用没有 IV 的模式(例如 ECB),使用仅由 0 值组成的 IV,使用可预测的 IV 或在同一密钥下多次使用 IV 等。通常会大大降低安全性,参见例如here!