Java中的矢量不会随着“ AES / CBC / pkcs7padding”而更改

时间:2018-07-20 14:05:30

标签: java aes bouncycastle cbc-mode

我在Java中使用模式“ AES / CBC / pkcs7padding”在2个设备之间进行通信。

建立通信后,两个设备之一将分配一个随机IV并将其发送给另一台设备。接收者将使用此IV实例化一个cryptoCipher和一个解密Cipher(请参见下面的代码)。

注意:在这里,我只输入用于加密的代码,但是我们具有用于解密的类似代码。

IV矢量已在通信开始时发送。然后,我们希望2个设备交换加密的消息而不再发送IV。 如果我们的理解是正确的,只要没有消息丢失,两个设备都应该知道在XOR中使用的当前“向量”是什么。

但是,Java代码无法正常运行: 如果我连续两次调用crypto(),它将产生相同的加密数据。 这不是我们期望的,因为我们认为第一次加密的结果将是第二次加密的“向量”(此向量与https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#CBC上所示的plainText之间的XOR)。

我们的理解错误吗?我们在实现过程中错过了什么吗?

private Cipher mEncryptionCipher;

private void createEncryptionCipher(byte[] iv) {

    mEncryptionCipher = null;

    try {
        IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
        mEncryptionCipher = Cipher.getInstance("AES/CBC/pkcs7padding", "BC");
        mEncryptionCipher.init(Cipher.ENCRYPT_MODE, mAESKey, ivParameterSpec);

    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    } catch (NoSuchProviderException e) {
        e.printStackTrace();
    } catch (NoSuchPaddingException e) {
        e.printStackTrace();
    } catch (InvalidKeyException e) {
        e.printStackTrace();
    } catch (InvalidAlgorithmParameterException e) {
        e.printStackTrace();
    }

}


private byte[] encrypt(byte[] data) {

    if (mEncryptionCipher == null) {
        Log.e(TAG, "Invalid mEncryptionCipher!");
        return null;
    }

    try {
        int sizeOfEncryptedData = computeLengthAfterPKCS7Padding(data.length);
        byte[] encodedData = new byte[sizeOfEncryptedData];

        int cipherBytes = mEncryptionCipher.update(data, 0, data.length, encodedData, 0);

        //allways call doFinal
        cipherBytes += mEncryptionCipher.doFinal(encodedData, cipherBytes);

        return encodedData;

    } catch (BadPaddingException e) {
        e.printStackTrace();
    } catch (IllegalBlockSizeException e) {
        e.printStackTrace();
    } catch (ShortBufferException e) {
        e.printStackTrace();
    } catch (STException e) {
        e.printStackTrace();
    }

    return null;
}

2 个答案:

答案 0 :(得分:2)

正如其他人所建议的那样,请尽可能使用TLS。

否则,至少要从TLS的设计中汲取教训(错误)。在TLS 1.0(https://tools.ietf.org/html/rfc2246)中,CBC密码状态是在单独的记录中维护的,我认为这是您正在尝试对“数据包”进行的操作。在TLS 1.1(https://tools.ietf.org/html/rfc5246)中对此进行了更改,以便每个记录都包含一个明确的IV。每条记录的IV“应该随机选择,并且必须不可预测”。因此,每条记录都是独立加密的。

请特别注意RFC 5246中的安全分析这一部分:

  

F.3。显式静脉注射

     

[CBCATT]描述了针对TLS的选定明文攻击,具体取决于   知道IV的记录。使用旧版TLS [TLS1.0]
  先前记录的CBC残基作为IV,因此
  启用了此攻击。此版本使用显式IV,以
  防范这种攻击。

还有其他陷阱在等待您,所以我回到我的第一个建议:尽可能使用TLS。

编辑:糟糕,TLS 1.1 RFC实际上是https://tools.ietf.org/html/rfc4346,它具有相同的F.3部分。

答案 1 :(得分:0)

如蜘蛛侠鲍里斯(Boris)和詹姆斯·K·波克(James K Polk)所指出的,doFinal()重置密码对象的状态,因此,如果我们进行新的加密,它将从原始IV重新开始,这不是CBC想要的行为。

我已经尝试过以下解决方案,并且可以使用。我正在检索上一个加密的块,并将其用作下一次加密的IV。

private byte[] mEncryptionIV = new byte[INITIALIZATION_VECTOR_SIZE];

private byte[] encrypt(byte[] data) {

    try {
        int sizeOfEncryptedData = computeLengthAfterPKCS7Padding(data.length);
        byte[] encodedData = new byte[sizeOfEncryptedData];

        IvParameterSpec ivParameterSpec = new IvParameterSpec(mEncryptionIV);
        Cipher encryptionCipher = Cipher.getInstance("AES/CBC/pkcs7padding", "BC");
        encryptionCipher.init(Cipher.ENCRYPT_MODE, mAESKey, ivParameterSpec);

        int cipherBytes = encryptionCipher.update(data, 0, data.length, encodedData, 0);

        //always call doFinal
        cipherBytes += encryptionCipher.doFinal(encodedData, cipherBytes);

        // The last encrypted block will be used as IV for the next encryption
        System.arraycopy(encodedData, encodedData.length-AES_BLOCK_LENGTH, mEncryptionIV, 0, mEncryptionIV.length);

        return encodedData;

    } catch (BadPaddingException e) {
        e.printStackTrace();
    } catch (IllegalBlockSizeException e) {
        e.printStackTrace();
    } catch (ShortBufferException e) {
        e.printStackTrace();
    } catch (STException e) {
        e.printStackTrace();
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    } catch (InvalidKeyException e) {
        e.printStackTrace();
    } catch (InvalidAlgorithmParameterException e) {
        e.printStackTrace();
    } catch (NoSuchPaddingException e) {
        e.printStackTrace();
    } catch (NoSuchProviderException e) {
        e.printStackTrace();
    }

    return null;
}