1
1
/*
2
- * Copyright 2011-2021 the original author or authors.
2
+ * Copyright 2011-2025 the original author or authors.
3
3
*
4
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
5
* you may not use this file except in compliance with the License.
17
17
package org .springframework .security .crypto .encrypt ;
18
18
19
19
import java .security .SecureRandom ;
20
+ import java .time .Duration ;
21
+ import java .time .temporal .ChronoUnit ;
20
22
import java .util .Random ;
21
23
import java .util .UUID ;
22
24
23
25
import org .junit .jupiter .api .BeforeEach ;
26
+ import org .junit .jupiter .api .Disabled ;
27
+ import org .junit .jupiter .api .RepeatedTest ;
24
28
import org .junit .jupiter .api .Test ;
25
29
26
30
import org .springframework .security .crypto .codec .Hex ;
@@ -89,6 +93,64 @@ public void bouncyCastleAesGcmWithSecureIvCompatible() throws Exception {
89
93
testCompatibility (bcEncryptor , jceEncryptor );
90
94
}
91
95
96
+ @ Test
97
+ public void bouncyCastleAesGcmWithAESFastEngineCompatible () throws Exception {
98
+ CryptoAssumptions .assumeGCMJCE ();
99
+ BytesEncryptor fastEngineEncryptor = BouncyCastleAesGcmBytesEncryptor .withAESFastEngine (this .password ,
100
+ this .salt , KeyGenerators .secureRandom (16 ));
101
+ BytesEncryptor defaultEngineEncryptor = new BouncyCastleAesGcmBytesEncryptor (this .password , this .salt ,
102
+ KeyGenerators .secureRandom (16 ));
103
+ testCompatibility (fastEngineEncryptor , defaultEngineEncryptor );
104
+ }
105
+
106
+ @ Test
107
+ public void bouncyCastleAesCbcWithAESFastEngineCompatible () throws Exception {
108
+ CryptoAssumptions .assumeCBCJCE ();
109
+ BytesEncryptor fastEngineEncryptor = BouncyCastleAesCbcBytesEncryptor .withAESFastEngine (this .password ,
110
+ this .salt , KeyGenerators .secureRandom (16 ));
111
+ BytesEncryptor defaultEngineEncryptor = new BouncyCastleAesCbcBytesEncryptor (this .password , this .salt ,
112
+ KeyGenerators .secureRandom (16 ));
113
+ testCompatibility (fastEngineEncryptor , defaultEngineEncryptor );
114
+ }
115
+
116
+ /**
117
+ * Comment out @Disabled below to compare relative speed of deprecated AESFastEngine
118
+ * with the default AESEngine.
119
+ */
120
+ @ Disabled
121
+ @ RepeatedTest (100 )
122
+ public void bouncyCastleAesGcmWithAESFastEngineSpeedTest () throws Exception {
123
+ CryptoAssumptions .assumeGCMJCE ();
124
+ BytesEncryptor defaultEngineEncryptor = new BouncyCastleAesGcmBytesEncryptor (this .password , this .salt ,
125
+ KeyGenerators .secureRandom (16 ));
126
+ BytesEncryptor fastEngineEncryptor = BouncyCastleAesGcmBytesEncryptor .withAESFastEngine (this .password ,
127
+ this .salt , KeyGenerators .secureRandom (16 ));
128
+ long defaultNanos = testSpeed (defaultEngineEncryptor );
129
+ long fastNanos = testSpeed (fastEngineEncryptor );
130
+ System .out .println (nanosToReadableString ("AES GCM w/Default Engine" , defaultNanos ));
131
+ System .out .println (nanosToReadableString ("AES GCM w/ Fast Engine" , fastNanos ));
132
+ assertThat (fastNanos ).isLessThan (defaultNanos );
133
+ }
134
+
135
+ /**
136
+ * Comment out @Disabled below to compare relative speed of deprecated AESFastEngine
137
+ * with the default AESEngine.
138
+ */
139
+ @ Disabled
140
+ @ RepeatedTest (100 )
141
+ public void bouncyCastleAesCbcWithAESFastEngineSpeedTest () throws Exception {
142
+ CryptoAssumptions .assumeCBCJCE ();
143
+ BytesEncryptor defaultEngineEncryptor = new BouncyCastleAesCbcBytesEncryptor (this .password , this .salt ,
144
+ KeyGenerators .secureRandom (16 ));
145
+ BytesEncryptor fastEngineEncryptor = BouncyCastleAesCbcBytesEncryptor .withAESFastEngine (this .password ,
146
+ this .salt , KeyGenerators .secureRandom (16 ));
147
+ long defaultNanos = testSpeed (defaultEngineEncryptor );
148
+ long fastNanos = testSpeed (fastEngineEncryptor );
149
+ System .out .println (nanosToReadableString ("AES CBC w/Default Engine" , defaultNanos ));
150
+ System .out .println (nanosToReadableString ("AES CBC w/ Fast Engine" , fastNanos ));
151
+ assertThat (fastNanos ).isLessThan (defaultNanos );
152
+ }
153
+
92
154
private void testEquivalence (BytesEncryptor left , BytesEncryptor right ) {
93
155
for (int size = 1 ; size < 2048 ; size ++) {
94
156
this .testData = new byte [size ];
@@ -107,7 +169,7 @@ private void testEquivalence(BytesEncryptor left, BytesEncryptor right) {
107
169
108
170
private void testCompatibility (BytesEncryptor left , BytesEncryptor right ) {
109
171
// tests that right can decrypt what left encrypted and vice versa
110
- // and that the decypted data is the same as the original
172
+ // and that the decrypted data is the same as the original
111
173
for (int size = 1 ; size < 2048 ; size ++) {
112
174
this .testData = new byte [size ];
113
175
this .secureRandom .nextBytes (this .testData );
@@ -120,6 +182,25 @@ private void testCompatibility(BytesEncryptor left, BytesEncryptor right) {
120
182
}
121
183
}
122
184
185
+ private long testSpeed (BytesEncryptor bytesEncryptor ) {
186
+ long start = System .nanoTime ();
187
+ for (int size = 0 ; size < 2048 ; size ++) {
188
+ this .testData = new byte [size ];
189
+ this .secureRandom .nextBytes (this .testData );
190
+ byte [] encrypted = bytesEncryptor .encrypt (this .testData );
191
+ byte [] decrypted = bytesEncryptor .decrypt (encrypted );
192
+ assertThat (decrypted ).containsExactly (this .testData );
193
+ }
194
+ return System .nanoTime () - start ;
195
+ }
196
+
197
+ private String nanosToReadableString (String label , long nanos ) {
198
+ Duration duration = Duration .ofNanos (nanos );
199
+ Duration millis = duration .truncatedTo (ChronoUnit .MILLIS );
200
+ Duration micros = duration .minus (millis ).dividedBy (1000 );
201
+ return "%s: %dms %dμs" .formatted (label , duration .toMillis (), micros .toNanos ());
202
+ }
203
+
123
204
/**
124
205
* A BytesKeyGenerator that always generates the same sequence of values
125
206
*/
0 commit comments