Skip to content

Commit a6893b6

Browse files
Define the MasterKeyProvider Keyring. (#149)
1 parent 77f46c2 commit a6893b6

File tree

11 files changed

+530
-54
lines changed

11 files changed

+530
-54
lines changed

src/main/java/com/amazonaws/encryptionsdk/internal/AesGcmJceKeyCipher.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ class AesGcmJceKeyCipher extends JceKeyCipher {
3232
private static final int SPEC_LENGTH = Integer.BYTES + Integer.BYTES + NONCE_LENGTH;
3333

3434
AesGcmJceKeyCipher(SecretKey key) {
35-
super(key, key);
35+
super(key, key, true);
3636
}
3737

3838
private static byte[] specToBytes(final GCMParameterSpec spec) {

src/main/java/com/amazonaws/encryptionsdk/internal/JceKeyCipher.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ public abstract class JceKeyCipher {
3636
private final Key wrappingKey;
3737
private final Key unwrappingKey;
3838
private static final Charset KEY_NAME_ENCODING = StandardCharsets.UTF_8;
39+
private final boolean encryptionContextSigned;
3940

4041
/**
4142
* Returns a new instance of a JceKeyCipher based on the
@@ -60,16 +61,26 @@ public static JceKeyCipher rsa(PublicKey wrappingKey, PrivateKey unwrappingKey,
6061
return new RsaJceKeyCipher(wrappingKey, unwrappingKey, transformation);
6162
}
6263

63-
JceKeyCipher(Key wrappingKey, Key unwrappingKey) {
64+
JceKeyCipher(Key wrappingKey, Key unwrappingKey, boolean encryptionContextSigned) {
6465
this.wrappingKey = wrappingKey;
6566
this.unwrappingKey = unwrappingKey;
67+
this.encryptionContextSigned = encryptionContextSigned;
6668
}
6769

6870
abstract WrappingData buildWrappingCipher(Key key, Map<String, String> encryptionContext) throws GeneralSecurityException;
6971

7072
abstract Cipher buildUnwrappingCipher(Key key, byte[] extraInfo, int offset,
7173
Map<String, String> encryptionContext) throws GeneralSecurityException;
7274

75+
/**
76+
* Returns true if this key cipher supports signing and verification
77+
* of the encryption context.
78+
*
79+
* @return True if encryption context signing/verification is supported.
80+
*/
81+
public boolean isEncryptionContextSigned() {
82+
return encryptionContextSigned;
83+
}
7384

7485
/**
7586
* Encrypts the given key, incorporating the given keyName and encryptionContext.

src/main/java/com/amazonaws/encryptionsdk/internal/RsaJceKeyCipher.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ class RsaJceKeyCipher extends JceKeyCipher {
4444
private final String transformation_;
4545

4646
RsaJceKeyCipher(PublicKey wrappingKey, PrivateKey unwrappingKey, String transformation) {
47-
super(wrappingKey, unwrappingKey);
47+
super(wrappingKey, unwrappingKey, false);
4848

4949
final Matcher matcher = SUPPORTED_TRANSFORMATIONS.matcher(transformation);
5050
if (matcher.matches()) {

src/main/java/com/amazonaws/encryptionsdk/jce/JceMasterKey.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,16 @@ public String getKeyId() {
104104
return keyId_;
105105
}
106106

107+
/**
108+
* Returns true if the underlying key cipher supports signing and
109+
* verification of the encryption context.
110+
*
111+
* @return True if encryption context signing/verification is supported.
112+
*/
113+
public boolean isEncryptionContextSigned() {
114+
return jceKeyCipher_.isEncryptionContextSigned();
115+
}
116+
107117
@Override
108118
public DataKey<JceMasterKey> generateDataKey(final CryptoAlgorithm algorithm,
109119
final Map<String, String> encryptionContext) {
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
/*
2+
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except
5+
* in compliance with the License. A copy of the License is located at
6+
*
7+
* http://aws.amazon.com/apache2.0
8+
*
9+
* or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS,
10+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
11+
* specific language governing permissions and limitations under the License.
12+
*/
13+
14+
package com.amazonaws.encryptionsdk.keyrings;
15+
16+
import com.amazonaws.encryptionsdk.DataKey;
17+
import com.amazonaws.encryptionsdk.EncryptedDataKey;
18+
import com.amazonaws.encryptionsdk.MasterKey;
19+
import com.amazonaws.encryptionsdk.MasterKeyProvider;
20+
import com.amazonaws.encryptionsdk.MasterKeyRequest;
21+
import com.amazonaws.encryptionsdk.exception.AwsCryptoException;
22+
import com.amazonaws.encryptionsdk.exception.CannotUnwrapDataKeyException;
23+
import com.amazonaws.encryptionsdk.jce.JceMasterKey;
24+
import com.amazonaws.encryptionsdk.kms.KmsMasterKey;
25+
26+
import java.util.ArrayList;
27+
import java.util.List;
28+
29+
import static com.amazonaws.encryptionsdk.keyrings.KeyringTraceFlag.DECRYPTED_DATA_KEY;
30+
import static com.amazonaws.encryptionsdk.keyrings.KeyringTraceFlag.ENCRYPTED_DATA_KEY;
31+
import static com.amazonaws.encryptionsdk.keyrings.KeyringTraceFlag.SIGNED_ENCRYPTION_CONTEXT;
32+
import static com.amazonaws.encryptionsdk.keyrings.KeyringTraceFlag.VERIFIED_ENCRYPTION_CONTEXT;
33+
import static java.util.Objects.requireNonNull;
34+
import static org.apache.commons.lang3.ArrayUtils.EMPTY_BYTE_ARRAY;
35+
36+
/**
37+
* A keyring which wraps a legacy MasterKeyProvider to
38+
* facilitate transition to keyrings.
39+
*/
40+
class MasterKeyProviderKeyring<K extends MasterKey<K>> implements Keyring {
41+
42+
private final MasterKeyProvider<K> masterKeyProvider;
43+
44+
MasterKeyProviderKeyring(MasterKeyProvider<K> masterKeyProvider) {
45+
requireNonNull(masterKeyProvider, "masterKeyProvider is required");
46+
47+
this.masterKeyProvider = masterKeyProvider;
48+
}
49+
50+
@Override
51+
public void onEncrypt(EncryptionMaterials encryptionMaterials) {
52+
requireNonNull(encryptionMaterials, "encryptionMaterials are required");
53+
54+
final List<K> masterKeys = masterKeyProvider.getMasterKeysForEncryption(MasterKeyRequest.newBuilder()
55+
.setEncryptionContext(encryptionMaterials.getEncryptionContext()).build());
56+
57+
if (masterKeys == null || masterKeys.isEmpty()) {
58+
throw new AwsCryptoException("No master keys available from the master key provider.");
59+
}
60+
61+
final K primaryMasterKey = masterKeys.get(0);
62+
final List<K> masterKeysToEncryptWith = new ArrayList<>(masterKeys);
63+
64+
if (!encryptionMaterials.hasPlaintextDataKey()) {
65+
final DataKey<K> dataKey = primaryMasterKey.generateDataKey(
66+
encryptionMaterials.getAlgorithmSuite(), encryptionMaterials.getEncryptionContext());
67+
encryptionMaterials.setPlaintextDataKey(dataKey.getKey(), new KeyringTraceEntry(
68+
primaryMasterKey.getProviderId(), primaryMasterKey.getKeyId(), KeyringTraceFlag.GENERATED_DATA_KEY));
69+
encryptionMaterials.addEncryptedDataKey(dataKey, encryptTraceEntry(primaryMasterKey));
70+
// The primary master key has already been used for encryption, so remove it from the list to encrypt with
71+
masterKeysToEncryptWith.remove(primaryMasterKey);
72+
}
73+
74+
final DataKey<K> dataKey = new DataKey<>(encryptionMaterials.getPlaintextDataKey(), EMPTY_BYTE_ARRAY,
75+
EMPTY_BYTE_ARRAY, primaryMasterKey);
76+
77+
for (K masterKey : masterKeysToEncryptWith) {
78+
final EncryptedDataKey encryptedDataKey = masterKey.encryptDataKey(encryptionMaterials.getAlgorithmSuite(),
79+
encryptionMaterials.getEncryptionContext(), dataKey);
80+
encryptionMaterials.addEncryptedDataKey(encryptedDataKey, encryptTraceEntry(masterKey));
81+
}
82+
}
83+
84+
@Override
85+
public void onDecrypt(DecryptionMaterials decryptionMaterials, List<? extends EncryptedDataKey> encryptedDataKeys) {
86+
requireNonNull(decryptionMaterials, "decryptionMaterials are required");
87+
requireNonNull(encryptedDataKeys, "encryptedDataKeys are required");
88+
89+
if (decryptionMaterials.hasPlaintextDataKey()) {
90+
return;
91+
}
92+
93+
final DataKey<K> dataKey;
94+
try {
95+
dataKey = masterKeyProvider.decryptDataKey(decryptionMaterials.getAlgorithmSuite(), encryptedDataKeys,
96+
decryptionMaterials.getEncryptionContext());
97+
} catch (CannotUnwrapDataKeyException e) {
98+
return;
99+
}
100+
101+
decryptionMaterials.setPlaintextDataKey(dataKey.getKey(), decryptTraceEntry(dataKey.getMasterKey()));
102+
}
103+
104+
private boolean signedEncryptionContext(MasterKey<K> masterKey) {
105+
if (masterKey instanceof KmsMasterKey) {
106+
return true;
107+
}
108+
109+
if (masterKey instanceof JceMasterKey) {
110+
return ((JceMasterKey) masterKey).isEncryptionContextSigned();
111+
}
112+
113+
return false;
114+
}
115+
116+
private KeyringTraceEntry encryptTraceEntry(MasterKey<K> masterKey) {
117+
final List<KeyringTraceFlag> flags = new ArrayList<>();
118+
flags.add(ENCRYPTED_DATA_KEY);
119+
120+
if (signedEncryptionContext(masterKey)) {
121+
flags.add(SIGNED_ENCRYPTION_CONTEXT);
122+
}
123+
124+
return new KeyringTraceEntry(masterKey.getProviderId(), masterKey.getKeyId(), flags.toArray(new KeyringTraceFlag[]{}));
125+
}
126+
127+
private KeyringTraceEntry decryptTraceEntry(MasterKey<K> masterKey) {
128+
final List<KeyringTraceFlag> flags = new ArrayList<>();
129+
flags.add(DECRYPTED_DATA_KEY);
130+
131+
if (signedEncryptionContext(masterKey)) {
132+
flags.add(VERIFIED_ENCRYPTION_CONTEXT);
133+
}
134+
135+
return new KeyringTraceEntry(masterKey.getProviderId(), masterKey.getKeyId(), flags.toArray(new KeyringTraceFlag[]{}));
136+
}
137+
}

src/main/java/com/amazonaws/encryptionsdk/keyrings/RawAesKeyring.java

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -48,18 +48,4 @@ boolean validToDecrypt(EncryptedDataKey encryptedDataKey) {
4848

4949
return true;
5050
}
51-
52-
@Override
53-
KeyringTraceEntry traceOnEncrypt() {
54-
return new KeyringTraceEntry(keyNamespace, keyName,
55-
KeyringTraceFlag.ENCRYPTED_DATA_KEY,
56-
KeyringTraceFlag.SIGNED_ENCRYPTION_CONTEXT);
57-
}
58-
59-
@Override
60-
KeyringTraceEntry traceOnDecrypt() {
61-
return new KeyringTraceEntry(keyNamespace, keyName,
62-
KeyringTraceFlag.DECRYPTED_DATA_KEY,
63-
KeyringTraceFlag.VERIFIED_ENCRYPTION_CONTEXT);
64-
}
6551
}

src/main/java/com/amazonaws/encryptionsdk/keyrings/RawKeyring.java

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@
2424
import java.util.logging.Logger;
2525

2626
import static com.amazonaws.encryptionsdk.EncryptedDataKey.PROVIDER_ENCODING;
27+
import static com.amazonaws.encryptionsdk.keyrings.KeyringTraceFlag.DECRYPTED_DATA_KEY;
28+
import static com.amazonaws.encryptionsdk.keyrings.KeyringTraceFlag.ENCRYPTED_DATA_KEY;
29+
import static com.amazonaws.encryptionsdk.keyrings.KeyringTraceFlag.GENERATED_DATA_KEY;
30+
import static com.amazonaws.encryptionsdk.keyrings.KeyringTraceFlag.SIGNED_ENCRYPTION_CONTEXT;
31+
import static com.amazonaws.encryptionsdk.keyrings.KeyringTraceFlag.VERIFIED_ENCRYPTION_CONTEXT;
2732
import static java.util.Objects.requireNonNull;
2833
import static org.apache.commons.lang3.Validate.notBlank;
2934

@@ -57,20 +62,6 @@ abstract class RawKeyring implements Keyring {
5762
*/
5863
abstract boolean validToDecrypt(EncryptedDataKey encryptedDataKey);
5964

60-
/**
61-
* Gets the trace entry to add the the keyring trace upon successful encryption.
62-
*
63-
* @return The keyring trace entry.
64-
*/
65-
abstract KeyringTraceEntry traceOnEncrypt();
66-
67-
/**
68-
* Gets the trace entry to add to the keyring trace upon successful decryption.
69-
*
70-
* @return The keyring trace entry.
71-
*/
72-
abstract KeyringTraceEntry traceOnDecrypt();
73-
7465
@Override
7566
public void onEncrypt(EncryptionMaterials encryptionMaterials) {
7667
requireNonNull(encryptionMaterials, "encryptionMaterials are required");
@@ -82,7 +73,8 @@ public void onEncrypt(EncryptionMaterials encryptionMaterials) {
8273
final EncryptedDataKey encryptedDataKey = jceKeyCipher.encryptKey(
8374
encryptionMaterials.getPlaintextDataKey().getEncoded(),
8475
keyName, keyNamespace, encryptionMaterials.getEncryptionContext());
85-
encryptionMaterials.addEncryptedDataKey(encryptedDataKey, traceOnEncrypt());
76+
encryptionMaterials.addEncryptedDataKey(encryptedDataKey,
77+
new KeyringTraceEntry(keyNamespace, keyName, encryptTraceFlags()));
8678
}
8779

8880
@Override
@@ -101,7 +93,7 @@ public void onDecrypt(DecryptionMaterials decryptionMaterials, List<? extends En
10193
encryptedDataKey, keyName, decryptionMaterials.getEncryptionContext());
10294
decryptionMaterials.setPlaintextDataKey(
10395
new SecretKeySpec(decryptedKey, decryptionMaterials.getAlgorithmSuite().getDataKeyAlgo()),
104-
traceOnDecrypt());
96+
new KeyringTraceEntry(keyNamespace, keyName, decryptTraceFlags()));
10597
return;
10698
} catch (Exception e) {
10799
LOGGER.info("Could not decrypt key due to: " + e.getMessage());
@@ -117,6 +109,22 @@ private void generateDataKey(EncryptionMaterials encryptionMaterials) {
117109
Utils.getSecureRandom().nextBytes(rawKey);
118110
final SecretKey key = new SecretKeySpec(rawKey, encryptionMaterials.getAlgorithmSuite().getDataKeyAlgo());
119111

120-
encryptionMaterials.setPlaintextDataKey(key, new KeyringTraceEntry(keyNamespace, keyName, KeyringTraceFlag.GENERATED_DATA_KEY));
112+
encryptionMaterials.setPlaintextDataKey(key, new KeyringTraceEntry(keyNamespace, keyName, GENERATED_DATA_KEY));
113+
}
114+
115+
private KeyringTraceFlag[] encryptTraceFlags() {
116+
if(jceKeyCipher.isEncryptionContextSigned()) {
117+
return new KeyringTraceFlag[]{ENCRYPTED_DATA_KEY, SIGNED_ENCRYPTION_CONTEXT} ;
118+
} else {
119+
return new KeyringTraceFlag[]{ENCRYPTED_DATA_KEY};
120+
}
121+
}
122+
123+
private KeyringTraceFlag[] decryptTraceFlags() {
124+
if(jceKeyCipher.isEncryptionContextSigned()) {
125+
return new KeyringTraceFlag[]{DECRYPTED_DATA_KEY, VERIFIED_ENCRYPTION_CONTEXT} ;
126+
} else {
127+
return new KeyringTraceFlag[]{DECRYPTED_DATA_KEY};
128+
}
121129
}
122130
}

src/main/java/com/amazonaws/encryptionsdk/keyrings/RawRsaKeyring.java

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -49,14 +49,4 @@ boolean validToDecrypt(EncryptedDataKey encryptedDataKey) {
4949

5050
return true;
5151
}
52-
53-
@Override
54-
KeyringTraceEntry traceOnEncrypt() {
55-
return new KeyringTraceEntry(keyNamespace, keyName, KeyringTraceFlag.ENCRYPTED_DATA_KEY);
56-
}
57-
58-
@Override
59-
KeyringTraceEntry traceOnDecrypt() {
60-
return new KeyringTraceEntry(keyNamespace, keyName, KeyringTraceFlag.DECRYPTED_DATA_KEY);
61-
}
6252
}

src/main/java/com/amazonaws/encryptionsdk/keyrings/StandardKeyrings.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313

1414
package com.amazonaws.encryptionsdk.keyrings;
1515

16+
import com.amazonaws.encryptionsdk.MasterKey;
17+
import com.amazonaws.encryptionsdk.MasterKeyProvider;
1618
import com.amazonaws.encryptionsdk.kms.DataKeyEncryptionDao;
1719
import com.amazonaws.encryptionsdk.kms.KmsClientSupplier;
1820

@@ -60,6 +62,16 @@ public static Keyring rawRsa(String keyNamespace, String keyName, PublicKey publ
6062
}
6163

6264
/**
65+
* Constructs a {@code Keyring} which wraps a {@code MasterKeyProvider} to facilitate transitioning to keyrings.
66+
*
67+
* @param masterKeyProvider The master key provider.
68+
* @return The {@link Keyring}
69+
*/
70+
public static Keyring masterKeyProvider(MasterKeyProvider<? extends MasterKey> masterKeyProvider) {
71+
return new MasterKeyProviderKeyring<>(masterKeyProvider);
72+
}
73+
74+
/**
6375
* Constructs a {@code Keyring} which interacts with AWS Key Management Service (KMS) to create,
6476
* encrypt, and decrypt data keys using KMS defined Customer Master Keys (CMKs).
6577
*

0 commit comments

Comments
 (0)