环境:Java 8/SpringBoot v2.0.2
我正试图通过从"AES"更改密码实例来修复代码中的安全问题。",AES/GCM/NoPadding"。但是,对REST端点的现有测试失败,该测试具有使用此加密方法的加密路径参数值。
这是我的加密方法,
public String encrypt(final Long transactionId) {
Assert.notNull(transactionId, "Transaction Id Should Not Be null");
String encryptedText = "";
try
{ final byte[] encodedSecretKey = Base64.decodeBase64(encryptKey);
final SecretKeySpec secretKey = new SecretKeySpec(encodedSecretKey, "AES");
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(1, secretKey);
final byte[] contentAsBytes = transactionId.toString().getBytes();
byte[] contentAsByteCypherText = cipher.doFinal(contentAsBytes);
byte[] iv = cipher.getIV();
byte[] message = new byte[NUMBER_OF_IV_BYTES + contentAsByteCypherText.length];
System.arraycopy(iv, SRC_POSITION, message, DEST_POSITION, NUMBER_OF_IV_BYTES);
System.arraycopy(contentAsByteCypherText, SRC_POSITION, message, NUMBER_OF_IV_BYTES, contentAsByteCypherText.length);
encryptedText = Base64.encodeBase64URLSafeString(message);
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException e) {
LOGGER.error("Failed to encrypt Transaction ID ", e);
throw new BlahException("Failed to encrypt transaction id", e);
}
return encryptedText;
}
我的测试
public void testJourney(final Long transactionId) throws Exception {
final String request = loadExpectedContent(transactionId);
mockRestServiceServer.reset();
mockRestServiceServer.expect(requestTo("/spring/rest/transaction/" + webClient.encrypt(transactionId)))
.andExpect(method(HttpMethod.GET))
.andRespond(withSuccess().contentType(MediaType.APPLICATION_JSON).body(request));
final CompletableFuture<String> completable = webClient.getWebJourney(transactionId);
mockRestServiceServer.verify();
final String response = completable.get();
Assert.assertNotNull(response);
LOGGER.info(LoggingUtils.format(LoggingUtils.keyValue("Request", transactionId),
LoggingUtils.keyValue("Response", LoggingUtils.parse(response))));
}
在这个测试中,加密方法被击中两次。
- 当尝试创建期望
- 里面webClient.getWebJourney (transactionId)
不同之处在于两次使用Cipher.getInstance("AES/GCM/NoPadding");
时,它返回两个不同的值,但测试失败。但是当使用Cipher.getInstance("AES");
时(不使用IV),它两次返回相同的值。
我的问题是我如何用"AES/GCM/NoPadding"测试这个REST端点?
要测试您是否不想获得相同的密文,您希望在解密后获得相同的明文。使用GCM,您需要一个对某些内容加密两次并且不会得到相同结果的测试。
你不应该重复使用IV值(对于GCM,相同的IV和明文会产生相同的密文)。
:
byte[] iv = cipher.getIV();
将返回一个安全的随机生成的12字节数组,因此密文每次都是不同的。
在没有IV的情况下调用Cipher.getInstance("AES")
绝对不是你想做的:
- 它很可能默认为欧洲央行,这是旧的,严重损坏(看看企鹅在"加密";形象。
- 不使用IV是你得到相同结果的原因- IV的想法是从一些随机性开始,以避免可用于寻找密钥的重复模式。
我将更改测试以执行加密,然后解密(在测试中)并确保密文相同。