Skip to content

Commit 33e08bd

Browse files
Merge branch 'keyring' into mkp
2 parents cfad6d1 + 77f46c2 commit 33e08bd

19 files changed

+2105
-81
lines changed

pom.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242
<dependency>
4343
<groupId>com.amazonaws</groupId>
4444
<artifactId>aws-java-sdk</artifactId>
45-
<version>1.11.561</version>
45+
<version>1.11.677</version>
4646
<optional>true</optional>
4747
</dependency>
4848

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
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.exception;
15+
16+
/**
17+
* This exception is thrown when an Amazon Resource Name is provided that does not
18+
* match the CMK Alias or ARN format.
19+
*/
20+
public class MalformedArnException extends AwsCryptoException {
21+
22+
private static final long serialVersionUID = -1L;
23+
24+
public MalformedArnException() {
25+
super();
26+
}
27+
28+
public MalformedArnException(final String message) {
29+
super(message);
30+
}
31+
32+
public MalformedArnException(final Throwable cause) {
33+
super(cause);
34+
}
35+
36+
public MalformedArnException(final String message, final Throwable cause) {
37+
super(message, cause);
38+
}
39+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
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.exception;
15+
16+
/**
17+
* This exception is thrown when the key used by KMS to decrypt a data key does not
18+
* match the provider information contained within the encrypted data key.
19+
*/
20+
public class MismatchedDataKeyException extends AwsCryptoException {
21+
22+
private static final long serialVersionUID = -1L;
23+
24+
public MismatchedDataKeyException() {
25+
super();
26+
}
27+
28+
public MismatchedDataKeyException(final String message) {
29+
super(message);
30+
}
31+
32+
public MismatchedDataKeyException(final Throwable cause) {
33+
super(cause);
34+
}
35+
36+
public MismatchedDataKeyException(final String message, final Throwable cause) {
37+
super(message, cause);
38+
}
39+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
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.exception;
15+
16+
/**
17+
* This exception is thrown when a region that is not allowed to be used by
18+
* a given KmsClientSupplier is specified.
19+
*/
20+
public class UnsupportedRegionException extends AwsCryptoException {
21+
22+
private static final long serialVersionUID = -1L;
23+
24+
public UnsupportedRegionException() {
25+
super();
26+
}
27+
28+
public UnsupportedRegionException(final String message) {
29+
super(message);
30+
}
31+
32+
public UnsupportedRegionException(final Throwable cause) {
33+
super(cause);
34+
}
35+
36+
public UnsupportedRegionException(final String message, final Throwable cause) {
37+
super(message, cause);
38+
}
39+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
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.EncryptedDataKey;
17+
import com.amazonaws.encryptionsdk.exception.AwsCryptoException;
18+
import com.amazonaws.encryptionsdk.exception.CannotUnwrapDataKeyException;
19+
import com.amazonaws.encryptionsdk.exception.MalformedArnException;
20+
import com.amazonaws.encryptionsdk.kms.DataKeyEncryptionDao;
21+
import com.amazonaws.encryptionsdk.kms.DataKeyEncryptionDao.DecryptDataKeyResult;
22+
import com.amazonaws.encryptionsdk.kms.DataKeyEncryptionDao.GenerateDataKeyResult;
23+
import com.amazonaws.encryptionsdk.kms.KmsUtils;
24+
25+
import java.util.ArrayList;
26+
import java.util.HashSet;
27+
import java.util.List;
28+
import java.util.Set;
29+
30+
import static com.amazonaws.encryptionsdk.EncryptedDataKey.PROVIDER_ENCODING;
31+
import static com.amazonaws.encryptionsdk.kms.KmsUtils.KMS_PROVIDER_ID;
32+
import static com.amazonaws.encryptionsdk.kms.KmsUtils.isArnWellFormed;
33+
import static java.util.Collections.emptyList;
34+
import static java.util.Collections.unmodifiableList;
35+
import static java.util.Objects.requireNonNull;
36+
37+
/**
38+
* A keyring which interacts with AWS Key Management Service (KMS) to create,
39+
* encrypt, and decrypt data keys using KMS defined Customer Master Keys (CMKs).
40+
*/
41+
class KmsKeyring implements Keyring {
42+
43+
private final DataKeyEncryptionDao dataKeyEncryptionDao;
44+
private final List<String> keyIds;
45+
private final String generatorKeyId;
46+
private final boolean isDiscovery;
47+
48+
KmsKeyring(DataKeyEncryptionDao dataKeyEncryptionDao, List<String> keyIds, String generatorKeyId) {
49+
requireNonNull(dataKeyEncryptionDao, "dataKeyEncryptionDao is required");
50+
this.dataKeyEncryptionDao = dataKeyEncryptionDao;
51+
this.keyIds = keyIds == null ? emptyList() : unmodifiableList(new ArrayList<>(keyIds));
52+
this.generatorKeyId = generatorKeyId;
53+
this.isDiscovery = this.generatorKeyId == null && this.keyIds.isEmpty();
54+
55+
if (!this.keyIds.stream().allMatch(KmsUtils::isArnWellFormed)) {
56+
throw new MalformedArnException("keyIds must contain only CMK aliases and well formed ARNs");
57+
}
58+
59+
if (generatorKeyId != null) {
60+
if (!isArnWellFormed(generatorKeyId)) {
61+
throw new MalformedArnException("generatorKeyId must be either a CMK alias or a well formed ARN");
62+
}
63+
if (this.keyIds.contains(generatorKeyId)) {
64+
throw new IllegalArgumentException("KeyIds should not contain the generatorKeyId");
65+
}
66+
}
67+
}
68+
69+
@Override
70+
public void onEncrypt(EncryptionMaterials encryptionMaterials) {
71+
requireNonNull(encryptionMaterials, "encryptionMaterials are required");
72+
73+
// If this keyring is a discovery keyring, OnEncrypt MUST return the input encryption materials unmodified.
74+
if (isDiscovery) {
75+
return;
76+
}
77+
78+
// If the input encryption materials do not contain a plaintext data key and this keyring does not
79+
// have a generator defined, OnEncrypt MUST not modify the encryption materials and MUST fail.
80+
if (!encryptionMaterials.hasPlaintextDataKey() && generatorKeyId == null) {
81+
throw new AwsCryptoException("Encryption materials must contain either a plaintext data key or a generator");
82+
}
83+
84+
final List<String> keyIdsToEncrypt = new ArrayList<>(keyIds);
85+
86+
// If the input encryption materials do not contain a plaintext data key and a generator is defined onEncrypt
87+
// MUST attempt to generate a new plaintext data key and encrypt that data key by calling KMS GenerateDataKey.
88+
if (!encryptionMaterials.hasPlaintextDataKey()) {
89+
generateDataKey(encryptionMaterials);
90+
} else if (generatorKeyId != null) {
91+
// If this keyring's generator is defined and was not used to generate a data key, OnEncrypt
92+
// MUST also attempt to encrypt the plaintext data key using the CMK specified by the generator.
93+
keyIdsToEncrypt.add(generatorKeyId);
94+
}
95+
96+
// Given a plaintext data key in the encryption materials, OnEncrypt MUST attempt
97+
// to encrypt the plaintext data key using each CMK specified in it's key IDs list.
98+
for (String keyId : keyIdsToEncrypt) {
99+
encryptDataKey(keyId, encryptionMaterials);
100+
}
101+
}
102+
103+
private void generateDataKey(final EncryptionMaterials encryptionMaterials) {
104+
final GenerateDataKeyResult result = dataKeyEncryptionDao.generateDataKey(generatorKeyId,
105+
encryptionMaterials.getAlgorithmSuite(), encryptionMaterials.getEncryptionContext());
106+
107+
encryptionMaterials.setPlaintextDataKey(result.getPlaintextDataKey(),
108+
new KeyringTraceEntry(KMS_PROVIDER_ID, generatorKeyId, KeyringTraceFlag.GENERATED_DATA_KEY));
109+
encryptionMaterials.addEncryptedDataKey(result.getEncryptedDataKey(),
110+
new KeyringTraceEntry(KMS_PROVIDER_ID, generatorKeyId, KeyringTraceFlag.ENCRYPTED_DATA_KEY, KeyringTraceFlag.SIGNED_ENCRYPTION_CONTEXT));
111+
}
112+
113+
private void encryptDataKey(final String keyId, final EncryptionMaterials encryptionMaterials) {
114+
final EncryptedDataKey encryptedDataKey = dataKeyEncryptionDao.encryptDataKey(keyId,
115+
encryptionMaterials.getPlaintextDataKey(), encryptionMaterials.getEncryptionContext());
116+
117+
encryptionMaterials.addEncryptedDataKey(encryptedDataKey,
118+
new KeyringTraceEntry(KMS_PROVIDER_ID, keyId, KeyringTraceFlag.ENCRYPTED_DATA_KEY, KeyringTraceFlag.SIGNED_ENCRYPTION_CONTEXT));
119+
}
120+
121+
@Override
122+
public void onDecrypt(DecryptionMaterials decryptionMaterials, List<? extends EncryptedDataKey> encryptedDataKeys) {
123+
requireNonNull(decryptionMaterials, "decryptionMaterials are required");
124+
requireNonNull(encryptedDataKeys, "encryptedDataKeys are required");
125+
126+
if (decryptionMaterials.hasPlaintextDataKey() || encryptedDataKeys.isEmpty()) {
127+
return;
128+
}
129+
130+
final Set<String> configuredKeyIds = new HashSet<>(keyIds);
131+
132+
if (generatorKeyId != null) {
133+
configuredKeyIds.add(generatorKeyId);
134+
}
135+
136+
for (EncryptedDataKey encryptedDataKey : encryptedDataKeys) {
137+
if (okToDecrypt(encryptedDataKey, configuredKeyIds)) {
138+
try {
139+
final DecryptDataKeyResult result = dataKeyEncryptionDao.decryptDataKey(encryptedDataKey,
140+
decryptionMaterials.getAlgorithmSuite(), decryptionMaterials.getEncryptionContext());
141+
142+
decryptionMaterials.setPlaintextDataKey(result.getPlaintextDataKey(),
143+
new KeyringTraceEntry(KMS_PROVIDER_ID, result.getKeyArn(),
144+
KeyringTraceFlag.DECRYPTED_DATA_KEY, KeyringTraceFlag.VERIFIED_ENCRYPTION_CONTEXT));
145+
return;
146+
} catch (CannotUnwrapDataKeyException e) {
147+
continue;
148+
}
149+
}
150+
}
151+
}
152+
153+
private boolean okToDecrypt(EncryptedDataKey encryptedDataKey, Set<String> configuredKeyIds) {
154+
// Only attempt to decrypt keys provided by KMS
155+
if (!encryptedDataKey.getProviderId().equals(KMS_PROVIDER_ID)) {
156+
return false;
157+
}
158+
159+
// If the key ARN cannot be parsed, skip it
160+
if(!isArnWellFormed(new String(encryptedDataKey.getProviderInformation(), PROVIDER_ENCODING)))
161+
{
162+
return false;
163+
}
164+
165+
// If this keyring is a discovery keyring, OnDecrypt MUST attempt to
166+
// decrypt every encrypted data key in the input encrypted data key list
167+
if (isDiscovery) {
168+
return true;
169+
}
170+
171+
// OnDecrypt MUST attempt to decrypt each input encrypted data key in the input
172+
// encrypted data key list where the key provider info has a value equal to one
173+
// of the ARNs in this keyring's key IDs or the generator
174+
return configuredKeyIds.contains(new String(encryptedDataKey.getProviderInformation(), PROVIDER_ENCODING));
175+
}
176+
}

0 commit comments

Comments
 (0)