From ccc8386469f1a22957ddb51c7f7075544a0ec4c7 Mon Sep 17 00:00:00 2001 From: Ferdinand Jacobs Date: Mon, 25 Nov 2024 18:49:52 +0100 Subject: [PATCH 1/3] Replace BouncyCastle's deprecated AESFastEngine with the default AESEngine - Update AESEngine to use the default AES engine, following BouncyCastle's recommendations (see release-1-56 of changelog: https://www.bouncycastle.org/download/bouncy-castle-java/?filter=java%3Drelease-1-56). - Migrate to the latest API 'newInstance()' method to allow removal of @SuppressWarnings("deprecation") - Remove @SuppressWarnings("deprecation") --- .../crypto/encrypt/BouncyCastleAesCbcBytesEncryptor.java | 7 +++---- .../crypto/encrypt/BouncyCastleAesGcmBytesEncryptor.java | 7 +++---- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/crypto/src/main/java/org/springframework/security/crypto/encrypt/BouncyCastleAesCbcBytesEncryptor.java b/crypto/src/main/java/org/springframework/security/crypto/encrypt/BouncyCastleAesCbcBytesEncryptor.java index 3aa6e6eab35..3fde93d5ead 100644 --- a/crypto/src/main/java/org/springframework/security/crypto/encrypt/BouncyCastleAesCbcBytesEncryptor.java +++ b/crypto/src/main/java/org/springframework/security/crypto/encrypt/BouncyCastleAesCbcBytesEncryptor.java @@ -18,6 +18,7 @@ import org.bouncycastle.crypto.BufferedBlockCipher; import org.bouncycastle.crypto.InvalidCipherTextException; +import org.bouncycastle.crypto.engines.AESEngine; import org.bouncycastle.crypto.modes.CBCBlockCipher; import org.bouncycastle.crypto.paddings.PKCS7Padding; import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher; @@ -45,23 +46,21 @@ public BouncyCastleAesCbcBytesEncryptor(String password, CharSequence salt, Byte } @Override - @SuppressWarnings("deprecation") public byte[] encrypt(byte[] bytes) { byte[] iv = this.ivGenerator.generateKey(); PaddedBufferedBlockCipher blockCipher = new PaddedBufferedBlockCipher( - new CBCBlockCipher(new org.bouncycastle.crypto.engines.AESFastEngine()), new PKCS7Padding()); + CBCBlockCipher.newInstance(AESEngine.newInstance()), new PKCS7Padding()); blockCipher.init(true, new ParametersWithIV(this.secretKey, iv)); byte[] encrypted = process(blockCipher, bytes); return (iv != null) ? EncodingUtils.concatenate(iv, encrypted) : encrypted; } @Override - @SuppressWarnings("deprecation") public byte[] decrypt(byte[] encryptedBytes) { byte[] iv = EncodingUtils.subArray(encryptedBytes, 0, this.ivGenerator.getKeyLength()); encryptedBytes = EncodingUtils.subArray(encryptedBytes, this.ivGenerator.getKeyLength(), encryptedBytes.length); PaddedBufferedBlockCipher blockCipher = new PaddedBufferedBlockCipher( - new CBCBlockCipher(new org.bouncycastle.crypto.engines.AESFastEngine()), new PKCS7Padding()); + CBCBlockCipher.newInstance(AESEngine.newInstance()), new PKCS7Padding()); blockCipher.init(false, new ParametersWithIV(this.secretKey, iv)); return process(blockCipher, encryptedBytes); } diff --git a/crypto/src/main/java/org/springframework/security/crypto/encrypt/BouncyCastleAesGcmBytesEncryptor.java b/crypto/src/main/java/org/springframework/security/crypto/encrypt/BouncyCastleAesGcmBytesEncryptor.java index cce6dd6d995..20ccf9265ec 100644 --- a/crypto/src/main/java/org/springframework/security/crypto/encrypt/BouncyCastleAesGcmBytesEncryptor.java +++ b/crypto/src/main/java/org/springframework/security/crypto/encrypt/BouncyCastleAesGcmBytesEncryptor.java @@ -17,6 +17,7 @@ package org.springframework.security.crypto.encrypt; import org.bouncycastle.crypto.InvalidCipherTextException; +import org.bouncycastle.crypto.engines.AESEngine; import org.bouncycastle.crypto.modes.AEADBlockCipher; import org.bouncycastle.crypto.modes.GCMBlockCipher; import org.bouncycastle.crypto.params.AEADParameters; @@ -44,21 +45,19 @@ public BouncyCastleAesGcmBytesEncryptor(String password, CharSequence salt, Byte } @Override - @SuppressWarnings("deprecation") public byte[] encrypt(byte[] bytes) { byte[] iv = this.ivGenerator.generateKey(); - GCMBlockCipher blockCipher = new GCMBlockCipher(new org.bouncycastle.crypto.engines.AESFastEngine()); + GCMBlockCipher blockCipher = (GCMBlockCipher) GCMBlockCipher.newInstance(AESEngine.newInstance()); blockCipher.init(true, new AEADParameters(this.secretKey, 128, iv, null)); byte[] encrypted = process(blockCipher, bytes); return (iv != null) ? EncodingUtils.concatenate(iv, encrypted) : encrypted; } @Override - @SuppressWarnings("deprecation") public byte[] decrypt(byte[] encryptedBytes) { byte[] iv = EncodingUtils.subArray(encryptedBytes, 0, this.ivGenerator.getKeyLength()); encryptedBytes = EncodingUtils.subArray(encryptedBytes, this.ivGenerator.getKeyLength(), encryptedBytes.length); - GCMBlockCipher blockCipher = new GCMBlockCipher(new org.bouncycastle.crypto.engines.AESFastEngine()); + GCMBlockCipher blockCipher = (GCMBlockCipher) GCMBlockCipher.newInstance(AESEngine.newInstance()); blockCipher.init(false, new AEADParameters(this.secretKey, 128, iv, null)); return process(blockCipher, encryptedBytes); } From 85c00e800e6ee7247682275c8b4d218c70223e06 Mon Sep 17 00:00:00 2001 From: Steve Riesenberg <5248162+sjohnr@users.noreply.github.com> Date: Mon, 3 Feb 2025 12:33:00 -0600 Subject: [PATCH 2/3] Polish gh-16164 --- .../BouncyCastleAesCbcBytesEncryptor.java | 29 +++++-- .../BouncyCastleAesGcmBytesEncryptor.java | 25 +++++- ...stleAesBytesEncryptorEquivalencyTests.java | 85 ++++++++++++++++++- 3 files changed, 129 insertions(+), 10 deletions(-) diff --git a/crypto/src/main/java/org/springframework/security/crypto/encrypt/BouncyCastleAesCbcBytesEncryptor.java b/crypto/src/main/java/org/springframework/security/crypto/encrypt/BouncyCastleAesCbcBytesEncryptor.java index 3fde93d5ead..362f28c2557 100644 --- a/crypto/src/main/java/org/springframework/security/crypto/encrypt/BouncyCastleAesCbcBytesEncryptor.java +++ b/crypto/src/main/java/org/springframework/security/crypto/encrypt/BouncyCastleAesCbcBytesEncryptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2016 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,10 +16,14 @@ package org.springframework.security.crypto.encrypt; +import java.util.function.Supplier; + import org.bouncycastle.crypto.BufferedBlockCipher; import org.bouncycastle.crypto.InvalidCipherTextException; import org.bouncycastle.crypto.engines.AESEngine; +import org.bouncycastle.crypto.engines.AESFastEngine; import org.bouncycastle.crypto.modes.CBCBlockCipher; +import org.bouncycastle.crypto.modes.CBCModeCipher; import org.bouncycastle.crypto.paddings.PKCS7Padding; import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher; import org.bouncycastle.crypto.params.ParametersWithIV; @@ -37,6 +41,8 @@ */ public class BouncyCastleAesCbcBytesEncryptor extends BouncyCastleAesBytesEncryptor { + private Supplier cipherFactory = () -> CBCBlockCipher.newInstance(AESEngine.newInstance()); + public BouncyCastleAesCbcBytesEncryptor(String password, CharSequence salt) { super(password, salt); } @@ -48,8 +54,8 @@ public BouncyCastleAesCbcBytesEncryptor(String password, CharSequence salt, Byte @Override public byte[] encrypt(byte[] bytes) { byte[] iv = this.ivGenerator.generateKey(); - PaddedBufferedBlockCipher blockCipher = new PaddedBufferedBlockCipher( - CBCBlockCipher.newInstance(AESEngine.newInstance()), new PKCS7Padding()); + PaddedBufferedBlockCipher blockCipher = new PaddedBufferedBlockCipher(this.cipherFactory.get(), + new PKCS7Padding()); blockCipher.init(true, new ParametersWithIV(this.secretKey, iv)); byte[] encrypted = process(blockCipher, bytes); return (iv != null) ? EncodingUtils.concatenate(iv, encrypted) : encrypted; @@ -59,8 +65,8 @@ public byte[] encrypt(byte[] bytes) { public byte[] decrypt(byte[] encryptedBytes) { byte[] iv = EncodingUtils.subArray(encryptedBytes, 0, this.ivGenerator.getKeyLength()); encryptedBytes = EncodingUtils.subArray(encryptedBytes, this.ivGenerator.getKeyLength(), encryptedBytes.length); - PaddedBufferedBlockCipher blockCipher = new PaddedBufferedBlockCipher( - CBCBlockCipher.newInstance(AESEngine.newInstance()), new PKCS7Padding()); + PaddedBufferedBlockCipher blockCipher = new PaddedBufferedBlockCipher(this.cipherFactory.get(), + new PKCS7Padding()); blockCipher.init(false, new ParametersWithIV(this.secretKey, iv)); return process(blockCipher, encryptedBytes); } @@ -82,4 +88,17 @@ private byte[] process(BufferedBlockCipher blockCipher, byte[] in) { return out; } + /** + * Used to test compatibility with deprecated {@link AESFastEngine}. + */ + @SuppressWarnings("deprecation") + static BouncyCastleAesCbcBytesEncryptor withAESFastEngine(String password, CharSequence salt, + BytesKeyGenerator ivGenerator) { + BouncyCastleAesCbcBytesEncryptor bytesEncryptor = new BouncyCastleAesCbcBytesEncryptor(password, salt, + ivGenerator); + bytesEncryptor.cipherFactory = () -> new CBCBlockCipher(new AESFastEngine()); + + return bytesEncryptor; + } + } diff --git a/crypto/src/main/java/org/springframework/security/crypto/encrypt/BouncyCastleAesGcmBytesEncryptor.java b/crypto/src/main/java/org/springframework/security/crypto/encrypt/BouncyCastleAesGcmBytesEncryptor.java index 20ccf9265ec..52d6e6cc486 100644 --- a/crypto/src/main/java/org/springframework/security/crypto/encrypt/BouncyCastleAesGcmBytesEncryptor.java +++ b/crypto/src/main/java/org/springframework/security/crypto/encrypt/BouncyCastleAesGcmBytesEncryptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2016 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,8 +16,11 @@ package org.springframework.security.crypto.encrypt; +import java.util.function.Supplier; + import org.bouncycastle.crypto.InvalidCipherTextException; import org.bouncycastle.crypto.engines.AESEngine; +import org.bouncycastle.crypto.engines.AESFastEngine; import org.bouncycastle.crypto.modes.AEADBlockCipher; import org.bouncycastle.crypto.modes.GCMBlockCipher; import org.bouncycastle.crypto.params.AEADParameters; @@ -36,6 +39,9 @@ */ public class BouncyCastleAesGcmBytesEncryptor extends BouncyCastleAesBytesEncryptor { + private Supplier cipherFactory = () -> (GCMBlockCipher) GCMBlockCipher + .newInstance(AESEngine.newInstance()); + public BouncyCastleAesGcmBytesEncryptor(String password, CharSequence salt) { super(password, salt); } @@ -47,7 +53,7 @@ public BouncyCastleAesGcmBytesEncryptor(String password, CharSequence salt, Byte @Override public byte[] encrypt(byte[] bytes) { byte[] iv = this.ivGenerator.generateKey(); - GCMBlockCipher blockCipher = (GCMBlockCipher) GCMBlockCipher.newInstance(AESEngine.newInstance()); + AEADBlockCipher blockCipher = this.cipherFactory.get(); blockCipher.init(true, new AEADParameters(this.secretKey, 128, iv, null)); byte[] encrypted = process(blockCipher, bytes); return (iv != null) ? EncodingUtils.concatenate(iv, encrypted) : encrypted; @@ -57,7 +63,7 @@ public byte[] encrypt(byte[] bytes) { public byte[] decrypt(byte[] encryptedBytes) { byte[] iv = EncodingUtils.subArray(encryptedBytes, 0, this.ivGenerator.getKeyLength()); encryptedBytes = EncodingUtils.subArray(encryptedBytes, this.ivGenerator.getKeyLength(), encryptedBytes.length); - GCMBlockCipher blockCipher = (GCMBlockCipher) GCMBlockCipher.newInstance(AESEngine.newInstance()); + AEADBlockCipher blockCipher = this.cipherFactory.get(); blockCipher.init(false, new AEADParameters(this.secretKey, 128, iv, null)); return process(blockCipher, encryptedBytes); } @@ -79,4 +85,17 @@ private byte[] process(AEADBlockCipher blockCipher, byte[] in) { return out; } + /** + * Used to test compatibility with deprecated {@link AESFastEngine}. + */ + @SuppressWarnings("deprecation") + static BouncyCastleAesGcmBytesEncryptor withAESFastEngine(String password, CharSequence salt, + BytesKeyGenerator ivGenerator) { + BouncyCastleAesGcmBytesEncryptor bytesEncryptor = new BouncyCastleAesGcmBytesEncryptor(password, salt, + ivGenerator); + bytesEncryptor.cipherFactory = () -> new GCMBlockCipher(new AESFastEngine()); + + return bytesEncryptor; + } + } diff --git a/crypto/src/test/java/org/springframework/security/crypto/encrypt/BouncyCastleAesBytesEncryptorEquivalencyTests.java b/crypto/src/test/java/org/springframework/security/crypto/encrypt/BouncyCastleAesBytesEncryptorEquivalencyTests.java index a37c83092c2..0dda6ebfc52 100644 --- a/crypto/src/test/java/org/springframework/security/crypto/encrypt/BouncyCastleAesBytesEncryptorEquivalencyTests.java +++ b/crypto/src/test/java/org/springframework/security/crypto/encrypt/BouncyCastleAesBytesEncryptorEquivalencyTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2021 the original author or authors. + * Copyright 2011-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,10 +17,14 @@ package org.springframework.security.crypto.encrypt; import java.security.SecureRandom; +import java.time.Duration; +import java.time.temporal.ChronoUnit; import java.util.Random; import java.util.UUID; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.RepeatedTest; import org.junit.jupiter.api.Test; import org.springframework.security.crypto.codec.Hex; @@ -89,6 +93,64 @@ public void bouncyCastleAesGcmWithSecureIvCompatible() throws Exception { testCompatibility(bcEncryptor, jceEncryptor); } + @Test + public void bouncyCastleAesGcmWithAESFastEngineCompatible() throws Exception { + CryptoAssumptions.assumeGCMJCE(); + BytesEncryptor fastEngineEncryptor = BouncyCastleAesGcmBytesEncryptor.withAESFastEngine(this.password, + this.salt, KeyGenerators.secureRandom(16)); + BytesEncryptor defaultEngineEncryptor = new BouncyCastleAesGcmBytesEncryptor(this.password, this.salt, + KeyGenerators.secureRandom(16)); + testCompatibility(fastEngineEncryptor, defaultEngineEncryptor); + } + + @Test + public void bouncyCastleAesCbcWithAESFastEngineCompatible() throws Exception { + CryptoAssumptions.assumeCBCJCE(); + BytesEncryptor fastEngineEncryptor = BouncyCastleAesCbcBytesEncryptor.withAESFastEngine(this.password, + this.salt, KeyGenerators.secureRandom(16)); + BytesEncryptor defaultEngineEncryptor = new BouncyCastleAesCbcBytesEncryptor(this.password, this.salt, + KeyGenerators.secureRandom(16)); + testCompatibility(fastEngineEncryptor, defaultEngineEncryptor); + } + + /** + * Comment out @Disabled below to compare relative speed of deprecated AESFastEngine + * with the default AESEngine. + */ + @Disabled + @RepeatedTest(100) + public void bouncyCastleAesGcmWithAESFastEngineSpeedTest() throws Exception { + CryptoAssumptions.assumeGCMJCE(); + BytesEncryptor defaultEngineEncryptor = new BouncyCastleAesGcmBytesEncryptor(this.password, this.salt, + KeyGenerators.secureRandom(16)); + BytesEncryptor fastEngineEncryptor = BouncyCastleAesGcmBytesEncryptor.withAESFastEngine(this.password, + this.salt, KeyGenerators.secureRandom(16)); + long defaultNanos = testSpeed(defaultEngineEncryptor); + long fastNanos = testSpeed(fastEngineEncryptor); + System.out.println(nanosToReadableString("AES GCM w/Default Engine", defaultNanos)); + System.out.println(nanosToReadableString("AES GCM w/ Fast Engine", fastNanos)); + assertThat(fastNanos).isLessThan(defaultNanos); + } + + /** + * Comment out @Disabled below to compare relative speed of deprecated AESFastEngine + * with the default AESEngine. + */ + @Disabled + @RepeatedTest(100) + public void bouncyCastleAesCbcWithAESFastEngineSpeedTest() throws Exception { + CryptoAssumptions.assumeCBCJCE(); + BytesEncryptor defaultEngineEncryptor = new BouncyCastleAesCbcBytesEncryptor(this.password, this.salt, + KeyGenerators.secureRandom(16)); + BytesEncryptor fastEngineEncryptor = BouncyCastleAesCbcBytesEncryptor.withAESFastEngine(this.password, + this.salt, KeyGenerators.secureRandom(16)); + long defaultNanos = testSpeed(defaultEngineEncryptor); + long fastNanos = testSpeed(fastEngineEncryptor); + System.out.println(nanosToReadableString("AES CBC w/Default Engine", defaultNanos)); + System.out.println(nanosToReadableString("AES CBC w/ Fast Engine", fastNanos)); + assertThat(fastNanos).isLessThan(defaultNanos); + } + private void testEquivalence(BytesEncryptor left, BytesEncryptor right) { for (int size = 1; size < 2048; size++) { this.testData = new byte[size]; @@ -107,7 +169,7 @@ private void testEquivalence(BytesEncryptor left, BytesEncryptor right) { private void testCompatibility(BytesEncryptor left, BytesEncryptor right) { // tests that right can decrypt what left encrypted and vice versa - // and that the decypted data is the same as the original + // and that the decrypted data is the same as the original for (int size = 1; size < 2048; size++) { this.testData = new byte[size]; this.secureRandom.nextBytes(this.testData); @@ -120,6 +182,25 @@ private void testCompatibility(BytesEncryptor left, BytesEncryptor right) { } } + private long testSpeed(BytesEncryptor bytesEncryptor) { + long start = System.nanoTime(); + for (int size = 0; size < 2048; size++) { + this.testData = new byte[size]; + this.secureRandom.nextBytes(this.testData); + byte[] encrypted = bytesEncryptor.encrypt(this.testData); + byte[] decrypted = bytesEncryptor.decrypt(encrypted); + assertThat(decrypted).containsExactly(this.testData); + } + return System.nanoTime() - start; + } + + private String nanosToReadableString(String label, long nanos) { + Duration duration = Duration.ofNanos(nanos); + Duration millis = duration.truncatedTo(ChronoUnit.MILLIS); + Duration micros = duration.minus(millis).dividedBy(1000); + return "%s: %dms %dμs".formatted(label, duration.toMillis(), micros.toNanos()); + } + /** * A BytesKeyGenerator that always generates the same sequence of values */ From c9c064c508dab4b246035ebd2673c8bf558b8f01 Mon Sep 17 00:00:00 2001 From: Rob Winch <362503+rwinch@users.noreply.github.com> Date: Wed, 7 May 2025 10:54:55 -0500 Subject: [PATCH 3/3] Remove Unnecessary Backwards Compatability Since this is going to be merged into Spring Security 7 (a major release) and AESFastEngine is deprecated, we should no longer support it (as it will likely be removed from Bouncy Castle) --- .../BouncyCastleAesCbcBytesEncryptor.java | 26 ++------ .../BouncyCastleAesGcmBytesEncryptor.java | 23 +------ ...stleAesBytesEncryptorEquivalencyTests.java | 60 ------------------- 3 files changed, 6 insertions(+), 103 deletions(-) diff --git a/crypto/src/main/java/org/springframework/security/crypto/encrypt/BouncyCastleAesCbcBytesEncryptor.java b/crypto/src/main/java/org/springframework/security/crypto/encrypt/BouncyCastleAesCbcBytesEncryptor.java index 362f28c2557..565ec4d3c1f 100644 --- a/crypto/src/main/java/org/springframework/security/crypto/encrypt/BouncyCastleAesCbcBytesEncryptor.java +++ b/crypto/src/main/java/org/springframework/security/crypto/encrypt/BouncyCastleAesCbcBytesEncryptor.java @@ -16,12 +16,9 @@ package org.springframework.security.crypto.encrypt; -import java.util.function.Supplier; - import org.bouncycastle.crypto.BufferedBlockCipher; import org.bouncycastle.crypto.InvalidCipherTextException; import org.bouncycastle.crypto.engines.AESEngine; -import org.bouncycastle.crypto.engines.AESFastEngine; import org.bouncycastle.crypto.modes.CBCBlockCipher; import org.bouncycastle.crypto.modes.CBCModeCipher; import org.bouncycastle.crypto.paddings.PKCS7Padding; @@ -41,8 +38,6 @@ */ public class BouncyCastleAesCbcBytesEncryptor extends BouncyCastleAesBytesEncryptor { - private Supplier cipherFactory = () -> CBCBlockCipher.newInstance(AESEngine.newInstance()); - public BouncyCastleAesCbcBytesEncryptor(String password, CharSequence salt) { super(password, salt); } @@ -54,8 +49,8 @@ public BouncyCastleAesCbcBytesEncryptor(String password, CharSequence salt, Byte @Override public byte[] encrypt(byte[] bytes) { byte[] iv = this.ivGenerator.generateKey(); - PaddedBufferedBlockCipher blockCipher = new PaddedBufferedBlockCipher(this.cipherFactory.get(), - new PKCS7Padding()); + CBCModeCipher cbcModeCipher = CBCBlockCipher.newInstance(AESEngine.newInstance()); + PaddedBufferedBlockCipher blockCipher = new PaddedBufferedBlockCipher(cbcModeCipher, new PKCS7Padding()); blockCipher.init(true, new ParametersWithIV(this.secretKey, iv)); byte[] encrypted = process(blockCipher, bytes); return (iv != null) ? EncodingUtils.concatenate(iv, encrypted) : encrypted; @@ -63,10 +58,10 @@ public byte[] encrypt(byte[] bytes) { @Override public byte[] decrypt(byte[] encryptedBytes) { + CBCModeCipher cbcModeCipher = CBCBlockCipher.newInstance(AESEngine.newInstance()); byte[] iv = EncodingUtils.subArray(encryptedBytes, 0, this.ivGenerator.getKeyLength()); encryptedBytes = EncodingUtils.subArray(encryptedBytes, this.ivGenerator.getKeyLength(), encryptedBytes.length); - PaddedBufferedBlockCipher blockCipher = new PaddedBufferedBlockCipher(this.cipherFactory.get(), - new PKCS7Padding()); + PaddedBufferedBlockCipher blockCipher = new PaddedBufferedBlockCipher(cbcModeCipher, new PKCS7Padding()); blockCipher.init(false, new ParametersWithIV(this.secretKey, iv)); return process(blockCipher, encryptedBytes); } @@ -88,17 +83,4 @@ private byte[] process(BufferedBlockCipher blockCipher, byte[] in) { return out; } - /** - * Used to test compatibility with deprecated {@link AESFastEngine}. - */ - @SuppressWarnings("deprecation") - static BouncyCastleAesCbcBytesEncryptor withAESFastEngine(String password, CharSequence salt, - BytesKeyGenerator ivGenerator) { - BouncyCastleAesCbcBytesEncryptor bytesEncryptor = new BouncyCastleAesCbcBytesEncryptor(password, salt, - ivGenerator); - bytesEncryptor.cipherFactory = () -> new CBCBlockCipher(new AESFastEngine()); - - return bytesEncryptor; - } - } diff --git a/crypto/src/main/java/org/springframework/security/crypto/encrypt/BouncyCastleAesGcmBytesEncryptor.java b/crypto/src/main/java/org/springframework/security/crypto/encrypt/BouncyCastleAesGcmBytesEncryptor.java index 52d6e6cc486..15eb61ad70f 100644 --- a/crypto/src/main/java/org/springframework/security/crypto/encrypt/BouncyCastleAesGcmBytesEncryptor.java +++ b/crypto/src/main/java/org/springframework/security/crypto/encrypt/BouncyCastleAesGcmBytesEncryptor.java @@ -16,11 +16,8 @@ package org.springframework.security.crypto.encrypt; -import java.util.function.Supplier; - import org.bouncycastle.crypto.InvalidCipherTextException; import org.bouncycastle.crypto.engines.AESEngine; -import org.bouncycastle.crypto.engines.AESFastEngine; import org.bouncycastle.crypto.modes.AEADBlockCipher; import org.bouncycastle.crypto.modes.GCMBlockCipher; import org.bouncycastle.crypto.params.AEADParameters; @@ -39,9 +36,6 @@ */ public class BouncyCastleAesGcmBytesEncryptor extends BouncyCastleAesBytesEncryptor { - private Supplier cipherFactory = () -> (GCMBlockCipher) GCMBlockCipher - .newInstance(AESEngine.newInstance()); - public BouncyCastleAesGcmBytesEncryptor(String password, CharSequence salt) { super(password, salt); } @@ -53,7 +47,7 @@ public BouncyCastleAesGcmBytesEncryptor(String password, CharSequence salt, Byte @Override public byte[] encrypt(byte[] bytes) { byte[] iv = this.ivGenerator.generateKey(); - AEADBlockCipher blockCipher = this.cipherFactory.get(); + AEADBlockCipher blockCipher = GCMBlockCipher.newInstance(AESEngine.newInstance()); blockCipher.init(true, new AEADParameters(this.secretKey, 128, iv, null)); byte[] encrypted = process(blockCipher, bytes); return (iv != null) ? EncodingUtils.concatenate(iv, encrypted) : encrypted; @@ -63,7 +57,7 @@ public byte[] encrypt(byte[] bytes) { public byte[] decrypt(byte[] encryptedBytes) { byte[] iv = EncodingUtils.subArray(encryptedBytes, 0, this.ivGenerator.getKeyLength()); encryptedBytes = EncodingUtils.subArray(encryptedBytes, this.ivGenerator.getKeyLength(), encryptedBytes.length); - AEADBlockCipher blockCipher = this.cipherFactory.get(); + AEADBlockCipher blockCipher = GCMBlockCipher.newInstance(AESEngine.newInstance()); blockCipher.init(false, new AEADParameters(this.secretKey, 128, iv, null)); return process(blockCipher, encryptedBytes); } @@ -85,17 +79,4 @@ private byte[] process(AEADBlockCipher blockCipher, byte[] in) { return out; } - /** - * Used to test compatibility with deprecated {@link AESFastEngine}. - */ - @SuppressWarnings("deprecation") - static BouncyCastleAesGcmBytesEncryptor withAESFastEngine(String password, CharSequence salt, - BytesKeyGenerator ivGenerator) { - BouncyCastleAesGcmBytesEncryptor bytesEncryptor = new BouncyCastleAesGcmBytesEncryptor(password, salt, - ivGenerator); - bytesEncryptor.cipherFactory = () -> new GCMBlockCipher(new AESFastEngine()); - - return bytesEncryptor; - } - } diff --git a/crypto/src/test/java/org/springframework/security/crypto/encrypt/BouncyCastleAesBytesEncryptorEquivalencyTests.java b/crypto/src/test/java/org/springframework/security/crypto/encrypt/BouncyCastleAesBytesEncryptorEquivalencyTests.java index 0dda6ebfc52..7db08b3378b 100644 --- a/crypto/src/test/java/org/springframework/security/crypto/encrypt/BouncyCastleAesBytesEncryptorEquivalencyTests.java +++ b/crypto/src/test/java/org/springframework/security/crypto/encrypt/BouncyCastleAesBytesEncryptorEquivalencyTests.java @@ -23,8 +23,6 @@ import java.util.UUID; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.RepeatedTest; import org.junit.jupiter.api.Test; import org.springframework.security.crypto.codec.Hex; @@ -93,64 +91,6 @@ public void bouncyCastleAesGcmWithSecureIvCompatible() throws Exception { testCompatibility(bcEncryptor, jceEncryptor); } - @Test - public void bouncyCastleAesGcmWithAESFastEngineCompatible() throws Exception { - CryptoAssumptions.assumeGCMJCE(); - BytesEncryptor fastEngineEncryptor = BouncyCastleAesGcmBytesEncryptor.withAESFastEngine(this.password, - this.salt, KeyGenerators.secureRandom(16)); - BytesEncryptor defaultEngineEncryptor = new BouncyCastleAesGcmBytesEncryptor(this.password, this.salt, - KeyGenerators.secureRandom(16)); - testCompatibility(fastEngineEncryptor, defaultEngineEncryptor); - } - - @Test - public void bouncyCastleAesCbcWithAESFastEngineCompatible() throws Exception { - CryptoAssumptions.assumeCBCJCE(); - BytesEncryptor fastEngineEncryptor = BouncyCastleAesCbcBytesEncryptor.withAESFastEngine(this.password, - this.salt, KeyGenerators.secureRandom(16)); - BytesEncryptor defaultEngineEncryptor = new BouncyCastleAesCbcBytesEncryptor(this.password, this.salt, - KeyGenerators.secureRandom(16)); - testCompatibility(fastEngineEncryptor, defaultEngineEncryptor); - } - - /** - * Comment out @Disabled below to compare relative speed of deprecated AESFastEngine - * with the default AESEngine. - */ - @Disabled - @RepeatedTest(100) - public void bouncyCastleAesGcmWithAESFastEngineSpeedTest() throws Exception { - CryptoAssumptions.assumeGCMJCE(); - BytesEncryptor defaultEngineEncryptor = new BouncyCastleAesGcmBytesEncryptor(this.password, this.salt, - KeyGenerators.secureRandom(16)); - BytesEncryptor fastEngineEncryptor = BouncyCastleAesGcmBytesEncryptor.withAESFastEngine(this.password, - this.salt, KeyGenerators.secureRandom(16)); - long defaultNanos = testSpeed(defaultEngineEncryptor); - long fastNanos = testSpeed(fastEngineEncryptor); - System.out.println(nanosToReadableString("AES GCM w/Default Engine", defaultNanos)); - System.out.println(nanosToReadableString("AES GCM w/ Fast Engine", fastNanos)); - assertThat(fastNanos).isLessThan(defaultNanos); - } - - /** - * Comment out @Disabled below to compare relative speed of deprecated AESFastEngine - * with the default AESEngine. - */ - @Disabled - @RepeatedTest(100) - public void bouncyCastleAesCbcWithAESFastEngineSpeedTest() throws Exception { - CryptoAssumptions.assumeCBCJCE(); - BytesEncryptor defaultEngineEncryptor = new BouncyCastleAesCbcBytesEncryptor(this.password, this.salt, - KeyGenerators.secureRandom(16)); - BytesEncryptor fastEngineEncryptor = BouncyCastleAesCbcBytesEncryptor.withAESFastEngine(this.password, - this.salt, KeyGenerators.secureRandom(16)); - long defaultNanos = testSpeed(defaultEngineEncryptor); - long fastNanos = testSpeed(fastEngineEncryptor); - System.out.println(nanosToReadableString("AES CBC w/Default Engine", defaultNanos)); - System.out.println(nanosToReadableString("AES CBC w/ Fast Engine", fastNanos)); - assertThat(fastNanos).isLessThan(defaultNanos); - } - private void testEquivalence(BytesEncryptor left, BytesEncryptor right) { for (int size = 1; size < 2048; size++) { this.testData = new byte[size];