gpt4 book ai didi

java - BouncyCaSTLe 和 SunJCE 在 Cipher::update 和 Cipher::doFInal 中的不同结果

转载 作者:塔克拉玛干 更新时间:2023-11-02 08:04:41 25 4
gpt4 key购买 nike

我试图将安全提供程序从 SunJCE 切换到 Bouncy CaSTLe (BC),并在 Cipher 对象中偶然发现了这种特殊行为。据我所知,SunJCE 的 cipher.update(bytes) 返回的加密文本包括作为最后一个 block 的后续初始化 vector (IV)。对于 BC,我需要调用 cipher.doFinal() 并使用第一个 block 来获取 IV。我使用的算法是 AES/CBC/PKCS5Padding

为什么会发生这种情况,处理这种情况的最佳方法是什么?

这是我的代码

import org.bouncycastle.jce.provider.BouncyCastleProvider;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.Security;
import java.util.Arrays;
import java.util.Base64;

import static java.nio.charset.StandardCharsets.UTF_8;
import static javax.xml.bind.DatatypeConverter.printHexBinary;

public class CipherDebug {

private final String algorithm;

private final String provider;

private final String cryptographicAlgorithm;

public CipherDebug(String algorithm,
String cipherMode,
String paddingMode,
String provider) {
this.algorithm = algorithm;
this.provider = provider;
this.cryptographicAlgorithm = algorithm + "/" + cipherMode + "/" + paddingMode;
}

private Cipher createCipher(int encryptDecryptMode,
byte[] keyValue,
byte[] initializationVector) throws Exception {
SecretKeySpec keySpec = new SecretKeySpec(keyValue, algorithm);
IvParameterSpec ivSpec = new IvParameterSpec(initializationVector);
Cipher cipher = Cipher.getInstance(cryptographicAlgorithm, provider);
cipher.init(encryptDecryptMode, keySpec, ivSpec);
return cipher;
}

@Override
public String toString() {
return "CipherDebug{" +
"provider=\"" + provider + '\"' +
", cryptographicAlgorithm=\"" + cryptographicAlgorithm + '\"' +
'}';
}

private static String generateData(int length) {
char[] chars = new char[length];
Arrays.fill(chars, '0');
return new String(chars);
}

public static void main(String[] args) throws Exception {
Security.insertProviderAt(new BouncyCastleProvider(), 1);

int numberOfChunks = 3;
byte[] keyValue = Base64.getDecoder()
.decode("yY7flqEdx95dojF/yY7flqEdx95dojF/".getBytes(StandardCharsets.UTF_8));
byte[] initializationVector = "pjts4PzQIr9Pd2yb".getBytes(StandardCharsets.UTF_8);

CipherDebug bouncyCastle = new CipherDebug("AES", "CBC", "PKCS5Padding", "BC");

CipherDebug sunJCE = new CipherDebug("AES", "CBC", "PKCS5Padding", "SunJCE");

Cipher bouncyCastleCipher = bouncyCastle.createCipher(Cipher.ENCRYPT_MODE,
keyValue, initializationVector);

Cipher sunJCECipher = sunJCE.createCipher(Cipher.ENCRYPT_MODE,
keyValue, initializationVector);

assert bouncyCastleCipher.getBlockSize() == sunJCECipher.getBlockSize();

// blockSize = 16
int blockSize = bouncyCastleCipher.getBlockSize();

byte[] data = generateData(blockSize * numberOfChunks).getBytes(UTF_8);
byte[] bouncyCastleUpdate = bouncyCastleCipher.update(data);
byte[] sunJCEUpdate = sunJCECipher.update(data);

//303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030
System.out.println(printHexBinary(data));

// CipherDebug{provider="BC", cryptographicAlgorithm="AES/CBC/PKCS5Padding"}
// 1D4DE40480F0528D4F77E788817DA62902D98C9AE6DF9299F4F2D1836CC10924
// 0320B10C8646D17E0755F8BBA1214ABF24D2E6E7F06184A78559793B23A9A341
System.out.println(bouncyCastle.toString());
System.out.println(printHexBinary(bouncyCastleUpdate));
System.out.println(printHexBinary(bouncyCastleCipher.doFinal()));

System.out.println();

// CipherDebug{provider="SunJCE", cryptographicAlgorithm="AES/CBC/PKCS5Padding"}
// 1D4DE40480F0528D4F77E788817DA62902D98C9AE6DF9299F4F2D1836CC109240320B10C8646D17E0755F8BBA1214ABF
// 24D2E6E7F06184A78559793B23A9A341
System.out.println(sunJCE.toString());
System.out.println(printHexBinary(sunJCEUpdate));
System.out.println(printHexBinary(sunJCECipher.doFinal()));

// assertion fails
assert Arrays.equals(bouncyCastleUpdate, sunJCEUpdate);
}
}

输出:

// data
03030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030

// Bouncy Castle
CipherDebug{provider="BC", cryptographicAlgorithm="AES/CBC/PKCS5Padding"}
1D4DE40480F0528D4F77E788817DA62902D98C9AE6DF9299F4F2D1836CC10924
0320B10C8646D17E0755F8BBA1214ABF24D2E6E7F06184A78559793B23A9A341


// SunJCE
CipherDebug{provider="SunJCE", cryptographicAlgorithm="AES/CBC/PKCS5Padding"}
1D4DE40480F0528D4F77E788817DA62902D98C9AE6DF9299F4F2D1836CC109240320B10C8646D17E0755F8BBA1214ABF
24D2E6E7F06184A78559793B23A9A341

最佳答案

最后密文中的额外数据是填充,但在这两种情况下都需要调用 Cipher.doFinal() - 密码需要知道它拥有所有输入数据,然后才能添加或删除填充。

Cipher.getIV() 将返回 IV。虽然 IV 可能在加密时生成,但它从来不是实际流的一部分,通常作为参数传递或生成。

如果是输出分块的方式导致混淆,则对此没有“标准”——在 BC 的情况下,它始终保持在一个 block 上,直到 doFinal() 到达,对于某些密码,SunJCE provider 没有,对于 HSM,输入可能首先被缓冲以更好地利用 HSM,因此连续的更新可能不会产生任何结果,然后可能会突然出现大量已处理的数据。您需要依靠 updates 和 doFinals 的返回值来正确处理已处理的数据。

关于java - BouncyCaSTLe 和 SunJCE 在 Cipher::update 和 Cipher::doFInal 中的不同结果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40777770/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com