-
Notifications
You must be signed in to change notification settings - Fork 19
Expand file tree
/
Copy pathS3Keyring.java
More file actions
174 lines (144 loc) · 7.94 KB
/
S3Keyring.java
File metadata and controls
174 lines (144 loc) · 7.94 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package software.amazon.encryption.s3.materials;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import software.amazon.encryption.s3.S3EncryptionClientException;
import javax.crypto.SecretKey;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* This serves as the base class for all the keyrings in the S3 encryption client.
* Shared functionality is all performed here.
*/
abstract public class S3Keyring implements Keyring {
public static final String KEY_PROVIDER_ID = "S3Keyring";
protected final DataKeyGenerator _dataKeyGenerator;
private final boolean _enableLegacyWrappingAlgorithms;
private final SecureRandom _secureRandom;
protected S3Keyring(Builder<?, ?> builder) {
_enableLegacyWrappingAlgorithms = builder._enableLegacyWrappingAlgorithms;
_secureRandom = builder._secureRandom;
_dataKeyGenerator = builder._dataKeyGenerator;
}
/**
* @return true if legacy wrapping algorithms are enabled, false otherwise
*/
public boolean areLegacyWrappingAlgorithmsEnabled() { return _enableLegacyWrappingAlgorithms;}
/**
* Generates a data key using the provided EncryptionMaterials and the configured DataKeyGenerator.
* <p>
* This method is intended for extension by customers who need to customize key generation within their Keyring
* implementation. It generates a data key for encryption using the algorithm suite and cryptographic provider
* configured in the provided EncryptionMaterials object.
*
* @param materials The EncryptionMaterials containing information about the algorithm suite and cryptographic
* provider to be used for data key generation.
* @return An updated EncryptionMaterials object with the generated plaintext data key.
*/
public EncryptionMaterials defaultGenerateDataKey(EncryptionMaterials materials) {
SecretKey dataKey = _dataKeyGenerator.generateDataKey(materials.algorithmSuite(), materials.cryptoProvider());
return materials.toBuilder()
.plaintextDataKey(dataKey.getEncoded())
.build();
}
@Override
public EncryptionMaterials onEncrypt(EncryptionMaterials materials) {
EncryptDataKeyStrategy encryptStrategy = encryptDataKeyStrategy();
// Allow encrypt strategy to modify the materials if necessary
materials = encryptStrategy.modifyMaterials(materials);
if (materials.plaintextDataKey() == null) {
materials = generateDataKeyStrategy().generateDataKey(materials);
}
// Return materials if they already have an encrypted data key.
if (!materials.encryptedDataKeys().isEmpty()) {
return materials;
}
try {
byte[] encryptedDataKeyCiphertext = encryptStrategy.encryptDataKey(_secureRandom, materials);
EncryptedDataKey encryptedDataKey = EncryptedDataKey.builder()
.keyProviderId(S3Keyring.KEY_PROVIDER_ID)
.keyProviderInfo(encryptStrategy.keyProviderInfo().getBytes(StandardCharsets.UTF_8))
.encryptedDataKey(encryptedDataKeyCiphertext)
.build();
List<EncryptedDataKey> encryptedDataKeys = new ArrayList<>(materials.encryptedDataKeys());
encryptedDataKeys.add(encryptedDataKey);
return materials.toBuilder()
.encryptedDataKeys(encryptedDataKeys)
.build();
} catch (Exception e) {
throw new S3EncryptionClientException("Unable to " + encryptStrategy.keyProviderInfo() + " wrap", e);
}
}
abstract protected GenerateDataKeyStrategy generateDataKeyStrategy();
abstract protected EncryptDataKeyStrategy encryptDataKeyStrategy();
@Override
public DecryptionMaterials onDecrypt(final DecryptionMaterials materials, List<EncryptedDataKey> encryptedDataKeys) {
if (materials.plaintextDataKey() != null) {
throw new S3EncryptionClientException("Decryption materials already contains a plaintext data key.");
}
if (encryptedDataKeys.size() != 1) {
throw new S3EncryptionClientException("Only one encrypted data key is supported, found: " + encryptedDataKeys.size());
}
EncryptedDataKey encryptedDataKey = encryptedDataKeys.get(0);
final String keyProviderId = encryptedDataKey.keyProviderId();
if (!KEY_PROVIDER_ID.equals(keyProviderId)) {
throw new S3EncryptionClientException("Unknown key provider: " + keyProviderId);
}
String keyProviderInfo = new String(encryptedDataKey.keyProviderInfo(), StandardCharsets.UTF_8);
DecryptDataKeyStrategy decryptStrategy = decryptDataKeyStrategies().get(keyProviderInfo);
if (decryptStrategy == null) {
throw new S3EncryptionClientException("The keyring does not support the object's key wrapping algorithm: " + keyProviderInfo);
}
//= specification/s3-encryption/client.md#enable-legacy-wrapping-algorithms
//= type=implication
//# When enabled, the S3EC MUST be able to decrypt objects encrypted with all supported wrapping algorithms (both legacy and fully supported).
if (decryptStrategy.isLegacy() && !_enableLegacyWrappingAlgorithms) {
//= specification/s3-encryption/client.md#enable-legacy-wrapping-algorithms
//= type=exception
//# When disabled, the S3EC MUST NOT decrypt objects encrypted using legacy wrapping algorithms; it MUST throw an exception when attempting to decrypt an object encrypted with a legacy wrapping algorithm.
throw new S3EncryptionClientException("Enable legacy wrapping algorithms to use legacy key wrapping algorithm: " + keyProviderInfo);
}
try {
byte[] plaintext = decryptStrategy.decryptDataKey(materials, encryptedDataKey.encryptedDatakey());
return materials.toBuilder().plaintextDataKey(plaintext).build();
} catch (GeneralSecurityException e) {
throw new S3EncryptionClientException("Unable to " + keyProviderInfo + " unwrap", e);
}
}
abstract protected Map<String, DecryptDataKeyStrategy> decryptDataKeyStrategies();
abstract public static class Builder<KeyringT extends S3Keyring, BuilderT extends Builder<KeyringT, BuilderT>> {
private boolean _enableLegacyWrappingAlgorithms = false;
private SecureRandom _secureRandom = new SecureRandom();
private DataKeyGenerator _dataKeyGenerator = new DefaultDataKeyGenerator();
protected Builder() {}
protected abstract BuilderT builder();
public BuilderT enableLegacyWrappingAlgorithms(boolean shouldEnableLegacyWrappingAlgorithms) {
this._enableLegacyWrappingAlgorithms = shouldEnableLegacyWrappingAlgorithms;
return builder();
}
/**
* Note that this does NOT create a defensive copy of the SecureRandom object. Any modifications to the
* object will be reflected in this Builder.
*/
@SuppressFBWarnings(value = "EI_EXPOSE_REP")
public BuilderT secureRandom(final SecureRandom secureRandom) {
if (secureRandom == null) {
throw new S3EncryptionClientException("SecureRandom provided to S3Keyring cannot be null");
}
_secureRandom = secureRandom;
return builder();
}
public BuilderT dataKeyGenerator(final DataKeyGenerator dataKeyGenerator) {
if (dataKeyGenerator == null) {
throw new S3EncryptionClientException("DataKeyGenerator cannot be null!");
}
_dataKeyGenerator = dataKeyGenerator;
return builder();
}
abstract public KeyringT build();
}
}