31
31
import java .security .GeneralSecurityException ;
32
32
import java .security .Key ;
33
33
import java .security .SecureRandom ;
34
+ import java .util .Random ;
34
35
35
36
/**
36
37
* An extension of {@link TransformedRecordSerializer} to use JCE to encrypt and decrypt records.
37
38
* @param <M> type of {@link Message} that underlying records will use
38
39
*/
39
40
@ API (API .Status .UNSTABLE )
40
41
public class TransformedRecordSerializerJCE <M extends Message > extends TransformedRecordSerializer <M > {
41
-
42
- @ Nullable
43
- protected final String cipherName ;
44
- @ Nullable
45
- protected final Key encryptionKey ;
46
42
@ Nullable
47
- protected final SecureRandom secureRandom ;
43
+ protected final TransformedRecordSerializerKeyManager keyManager ;
48
44
49
45
protected TransformedRecordSerializerJCE (@ Nonnull RecordSerializer <M > inner ,
50
46
boolean compressWhenSerializing ,
51
47
int compressionLevel ,
52
48
boolean encryptWhenSerializing ,
53
49
double writeValidationRatio ,
54
- @ Nullable String cipherName ,
55
- @ Nullable Key encryptionKey ,
56
- @ Nullable SecureRandom secureRandom ) {
50
+ @ Nullable TransformedRecordSerializerKeyManager keyManager ) {
57
51
super (inner , compressWhenSerializing , compressionLevel , encryptWhenSerializing , writeValidationRatio );
58
- this .cipherName = cipherName ;
59
- this .encryptionKey = encryptionKey ;
60
- this .secureRandom = secureRandom ;
52
+ this .keyManager = keyManager ;
61
53
}
62
54
63
55
@ Override
64
56
protected void encrypt (@ Nonnull TransformedRecordSerializerState state , @ Nullable StoreTimer timer ) throws GeneralSecurityException {
65
- if (cipherName == null || encryptionKey == null || secureRandom == null ) {
66
- throw new RecordSerializationException ("attempted to encrypt without setting cipher name and key" );
57
+ if (keyManager == null ) {
58
+ throw new RecordSerializationException ("attempted to encrypt without setting key manager ( cipher name and key) " );
67
59
}
68
60
long startTime = System .nanoTime ();
69
61
62
+ int keyNumber = keyManager .getSerializationKey ();
63
+ state .keyNumber = keyNumber ;
64
+
70
65
byte [] ivData = new byte [CipherPool .IV_SIZE ];
71
- secureRandom .nextBytes (ivData );
66
+ keyManager . getRandom ( keyNumber ) .nextBytes (ivData );
72
67
IvParameterSpec iv = new IvParameterSpec (ivData );
73
- Cipher cipher = CipherPool .borrowCipher (cipherName );
68
+ Cipher cipher = CipherPool .borrowCipher (keyManager . getCipher ( keyNumber ) );
74
69
try {
75
- cipher .init (Cipher .ENCRYPT_MODE , encryptionKey , iv );
70
+ cipher .init (Cipher .ENCRYPT_MODE , keyManager . getKey ( keyNumber ) , iv );
76
71
77
72
byte [] plainText = state .getDataArray ();
78
73
byte [] cipherText = cipher .doFinal (plainText );
@@ -93,7 +88,7 @@ protected void encrypt(@Nonnull TransformedRecordSerializerState state, @Nullabl
93
88
94
89
@ Override
95
90
protected void decrypt (@ Nonnull TransformedRecordSerializerState state , @ Nullable StoreTimer timer ) throws GeneralSecurityException {
96
- if (cipherName == null || encryptionKey == null || secureRandom == null ) {
91
+ if (keyManager == null ) {
97
92
throw new RecordSerializationException ("missing encryption key or provider during decryption" );
98
93
}
99
94
long startTime = System .nanoTime ();
@@ -104,9 +99,9 @@ protected void decrypt(@Nonnull TransformedRecordSerializerState state, @Nullabl
104
99
105
100
byte [] cipherText = new byte [state .length - CipherPool .IV_SIZE ];
106
101
System .arraycopy (state .data , state .offset + CipherPool .IV_SIZE , cipherText , 0 , cipherText .length );
107
- Cipher cipher = CipherPool .borrowCipher (cipherName );
102
+ Cipher cipher = CipherPool .borrowCipher (keyManager . getCipher ( state . keyNumber ) );
108
103
try {
109
- cipher .init (Cipher .DECRYPT_MODE , encryptionKey , iv );
104
+ cipher .init (Cipher .DECRYPT_MODE , keyManager . getKey ( state . keyNumber ) , iv );
110
105
111
106
byte [] plainText = cipher .doFinal (cipherText );
112
107
state .setDataArray (plainText );
@@ -155,6 +150,8 @@ public static <M extends Message> Builder<M> newBuilder(@Nonnull RecordSerialize
155
150
* @param <M> type of {@link Message} that underlying records will use
156
151
*/
157
152
public static class Builder <M extends Message > extends TransformedRecordSerializer .Builder <M > {
153
+ @ Nullable
154
+ protected TransformedRecordSerializerKeyManager keyManager ;
158
155
@ Nullable
159
156
protected String cipherName ;
160
157
@ Nullable
@@ -272,6 +269,26 @@ public Builder<M> clearSecureRandom() {
272
269
return this ;
273
270
}
274
271
272
+ /**
273
+ * Sets the key manager used during cryptographic operations.
274
+ * @param keyManager key manager to use for encrypting and decrypting
275
+ * @return this <code>Builder</code>
276
+ */
277
+ public Builder <M > setKeyManager (@ Nonnull TransformedRecordSerializerKeyManager keyManager ) {
278
+ this .keyManager = keyManager ;
279
+ return this ;
280
+ }
281
+
282
+ /**
283
+ * Clears a previously set key manager
284
+ * that might have been passed to this <code>Builder</code>.
285
+ * @return this <code>Builder</code>
286
+ */
287
+ public Builder <M > clearKeyManager () {
288
+ this .keyManager = null ;
289
+ return this ;
290
+ }
291
+
275
292
/**
276
293
* Construct a {@link TransformedRecordSerializerJCE} from the
277
294
* parameters specified by this builder. If one has enabled
@@ -282,17 +299,18 @@ public Builder<M> clearSecureRandom() {
282
299
*/
283
300
@ Override
284
301
public TransformedRecordSerializerJCE <M > build () {
285
- if (encryptWhenSerializing ) {
286
- if (encryptionKey == null ) {
302
+ if (keyManager == null ) {
303
+ if (encryptionKey != null ) {
304
+ keyManager = new FixedZeroKeyManager (encryptionKey , cipherName , secureRandom );
305
+ } else if (encryptWhenSerializing ) {
287
306
throw new RecordCoreArgumentException ("cannot encrypt when serializing if encryption key is not set" );
288
307
}
289
- }
290
- if (encryptionKey != null ) {
291
- if (cipherName == null ) {
292
- cipherName = CipherPool .DEFAULT_CIPHER ;
308
+ } else {
309
+ if (encryptionKey != null ) {
310
+ throw new RecordCoreArgumentException ("cannot specify both key manager and encryption key" );
293
311
}
294
- if (secureRandom = = null ) {
295
- secureRandom = new SecureRandom ( );
312
+ if (cipherName ! = null ) {
313
+ throw new RecordCoreArgumentException ( "cannot specify both key manager and cipher name" );
296
314
}
297
315
}
298
316
return new TransformedRecordSerializerJCE <>(
@@ -301,10 +319,56 @@ public TransformedRecordSerializerJCE<M> build() {
301
319
compressionLevel ,
302
320
encryptWhenSerializing ,
303
321
writeValidationRatio ,
304
- cipherName ,
305
- encryptionKey ,
306
- secureRandom
322
+ keyManager
307
323
);
308
324
}
325
+
326
+ }
327
+
328
+ static class FixedZeroKeyManager implements TransformedRecordSerializerKeyManager {
329
+ private final Key encryptionKey ;
330
+ private final String cipherName ;
331
+ private final SecureRandom secureRandom ;
332
+
333
+ public FixedZeroKeyManager (@ Nonnull Key encryptionKey , @ Nullable String cipherName , @ Nullable SecureRandom secureRandom ) {
334
+ if (cipherName == null ) {
335
+ cipherName = CipherPool .DEFAULT_CIPHER ;
336
+ }
337
+ if (secureRandom == null ) {
338
+ secureRandom = new SecureRandom ();
339
+ }
340
+ this .encryptionKey = encryptionKey ;
341
+ this .cipherName = cipherName ;
342
+ this .secureRandom = secureRandom ;
343
+ }
344
+
345
+ @ Override
346
+ public int getSerializationKey () {
347
+ return 0 ;
348
+ }
349
+
350
+ @ Override
351
+ public Key getKey (int keyNumber ) {
352
+ if (keyNumber != 0 ) {
353
+ throw new RecordCoreArgumentException ("only provide key number 0" );
354
+ }
355
+ return encryptionKey ;
356
+ }
357
+
358
+ @ Override
359
+ public String getCipher (int keyNumber ) {
360
+ if (keyNumber != 0 ) {
361
+ throw new RecordCoreArgumentException ("only provide key number 0" );
362
+ }
363
+ return cipherName ;
364
+ }
365
+
366
+ @ Override
367
+ public Random getRandom (int keyNumber ) {
368
+ if (keyNumber != 0 ) {
369
+ throw new RecordCoreArgumentException ("only provide key number 0" );
370
+ }
371
+ return secureRandom ;
372
+ }
309
373
}
310
374
}
0 commit comments