Skip to content

Commit 6820da3

Browse files
committed
Add support for AWS SDK v2.
1 parent d49f273 commit 6820da3

File tree

8 files changed

+393
-453
lines changed

8 files changed

+393
-453
lines changed

pom.xml

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
<groupId>com.amazonaws</groupId>
66
<artifactId>aws-encryption-sdk-java</artifactId>
7-
<version>2.3.3</version>
7+
<version>2.4.0</version>
88
<packaging>jar</packaging>
99

1010
<name>aws-encryption-sdk-java</name>
@@ -38,6 +38,20 @@
3838
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
3939
</properties>
4040

41+
<dependencyManagement>
42+
<dependencies>
43+
<!-- Support AWS SDK v2 -->
44+
<dependency>
45+
<groupId>software.amazon.awssdk</groupId>
46+
<artifactId>bom</artifactId>
47+
<version>2.17.110</version>
48+
<optional>true</optional>
49+
<type>pom</type>
50+
<scope>import</scope>
51+
</dependency>
52+
</dependencies>
53+
</dependencyManagement>
54+
4155
<dependencies>
4256
<!-- Support AWS SDK v1 -->
4357
<dependency>
@@ -48,15 +62,6 @@
4862
</dependency>
4963

5064
<!-- Support AWS SDK v2 -->
51-
<dependency>
52-
<groupId>software.amazon.awssdk</groupId>
53-
<artifactId>bom</artifactId>
54-
<version>2.17.110</version>
55-
<optional>true</optional>
56-
<type>pom</type>
57-
<scope>import</scope>
58-
</dependency>
59-
6065
<dependency>
6166
<groupId>software.amazon.awssdk</groupId>
6267
<artifactId>kms</artifactId>

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

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,29 @@ public class VersionInfo {
2424
* Loads the version of the library
2525
*/
2626
public static String loadUserAgent() {
27+
return USER_AGENT_PREFIX + versionNumber();
28+
}
29+
30+
/**
31+
* This returns the API name compatible with the AWS SDK v2
32+
*
33+
* @return the name of the library with a tag indicating intended for AWS SDK v2
34+
*/
35+
public static String apiName() {
36+
return USER_AGENT_PREFIX.substring(0, USER_AGENT_PREFIX.length() - 1);
37+
}
38+
39+
/*
40+
* String representation of the library version e.g. 2.3.3
41+
*/
42+
public static String versionNumber() {
2743
try {
2844
final Properties properties = new Properties();
2945
final ClassLoader loader = VersionInfo.class.getClassLoader();
3046
properties.load(loader.getResourceAsStream("project.properties"));
31-
return USER_AGENT_PREFIX + properties.getProperty("version");
47+
return properties.getProperty("version");
3248
} catch (final IOException ex) {
33-
return USER_AGENT_PREFIX + UNKNOWN_VERSION;
49+
return UNKNOWN_VERSION;
3450
}
3551
}
3652
}

src/main/java/com/amazonaws/encryptionsdk/kmsv2/AwsKmsMrkAwareMasterKey.java

Lines changed: 73 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,26 @@
33

44
package com.amazonaws.encryptionsdk.kmsv2;
55

6-
import com.amazonaws.AmazonServiceException;
7-
import com.amazonaws.AmazonWebServiceRequest;
6+
import static com.amazonaws.encryptionsdk.internal.AwsKmsCmkArnInfo.*;
7+
88
import com.amazonaws.encryptionsdk.*;
99
import com.amazonaws.encryptionsdk.exception.AwsCryptoException;
1010
import com.amazonaws.encryptionsdk.internal.AwsKmsCmkArnInfo;
1111
import com.amazonaws.encryptionsdk.internal.VersionInfo;
1212
import com.amazonaws.encryptionsdk.kms.KmsMethods;
13-
import com.amazonaws.services.kms.AWSKMS;
14-
import com.amazonaws.services.kms.model.*;
15-
16-
import javax.crypto.SecretKey;
17-
import javax.crypto.spec.SecretKeySpec;
1813
import java.nio.ByteBuffer;
1914
import java.nio.charset.StandardCharsets;
2015
import java.util.*;
16+
import java.util.function.Consumer;
2117
import java.util.function.Supplier;
22-
23-
import static com.amazonaws.encryptionsdk.internal.AwsKmsCmkArnInfo.*;
18+
import javax.crypto.SecretKey;
19+
import javax.crypto.spec.SecretKeySpec;
20+
import software.amazon.awssdk.awscore.AwsRequestOverrideConfiguration;
21+
import software.amazon.awssdk.awscore.exception.AwsServiceException;
22+
import software.amazon.awssdk.core.ApiName;
23+
import software.amazon.awssdk.core.SdkBytes;
24+
import software.amazon.awssdk.services.kms.KmsClient;
25+
import software.amazon.awssdk.services.kms.model.*;
2426

2527
// = compliance/framework/aws-kms/aws-kms-mrk-aware-master-key.txt#2.5
2628
// # MUST implement the Master Key Interface (../master-key-
@@ -38,18 +40,17 @@
3840
*/
3941
public final class AwsKmsMrkAwareMasterKey extends MasterKey<AwsKmsMrkAwareMasterKey>
4042
implements KmsMethods {
41-
private static final String USER_AGENT = VersionInfo.loadUserAgent();
42-
private final AWSKMS kmsClient_;
43+
44+
static final ApiName API_NAME =
45+
ApiName.builder().name(VersionInfo.apiName()).version(VersionInfo.versionNumber()).build();
46+
private static final Consumer<AwsRequestOverrideConfiguration.Builder> API_NAME_INTERCEPTOR =
47+
builder -> builder.addApiName(API_NAME);
48+
49+
private final KmsClient kmsClient_;
4350
private final List<String> grantTokens_ = new ArrayList<>();
4451
private final String awsKmsIdentifier_;
4552
private final MasterKeyProvider<AwsKmsMrkAwareMasterKey> sourceProvider_;
4653

47-
private static <T extends AmazonWebServiceRequest> T updateUserAgent(T request) {
48-
request.getRequestClientOptions().appendUserAgent(USER_AGENT);
49-
50-
return request;
51-
}
52-
5354
/**
5455
* A light builder method.
5556
*
@@ -58,7 +59,7 @@ private static <T extends AmazonWebServiceRequest> T updateUserAgent(T request)
5859
* @param awsKmsIdentifier An identifier for an AWS KMS key. May be a raw resource.
5960
*/
6061
static AwsKmsMrkAwareMasterKey getInstance(
61-
final AWSKMS kms,
62+
final KmsClient kms,
6263
final String awsKmsIdentifier,
6364
final MasterKeyProvider<AwsKmsMrkAwareMasterKey> provider) {
6465
return new AwsKmsMrkAwareMasterKey(awsKmsIdentifier, kms, provider);
@@ -68,7 +69,7 @@ static AwsKmsMrkAwareMasterKey getInstance(
6869
// # On initialization, the caller MUST provide:
6970
private AwsKmsMrkAwareMasterKey(
7071
final String awsKmsIdentifier,
71-
final AWSKMS kmsClient,
72+
final KmsClient kmsClient,
7273
final MasterKeyProvider<AwsKmsMrkAwareMasterKey> provider) {
7374

7475
// = compliance/framework/aws-kms/aws-kms-mrk-aware-master-key.txt#2.6
@@ -151,36 +152,40 @@ public DataKey<AwsKmsMrkAwareMasterKey> generateDataKey(
151152
// # master key MUST use the configured AWS KMS client to make an AWS KMS
152153
// # GenerateDatakey (https://docs.aws.amazon.com/kms/latest/APIReference/
153154
// # API_GenerateDataKey.html) request constructed as follows:
154-
final GenerateDataKeyResult gdkResult =
155+
final GenerateDataKeyResponse gdkResponse =
155156
kmsClient_.generateDataKey(
156-
updateUserAgent(
157-
new GenerateDataKeyRequest()
158-
.withKeyId(awsKmsIdentifier_)
159-
.withNumberOfBytes(algorithm.getDataKeyLength())
160-
.withEncryptionContext(encryptionContext)
161-
.withGrantTokens(grantTokens_)));
157+
GenerateDataKeyRequest.builder()
158+
.overrideConfiguration(API_NAME_INTERCEPTOR)
159+
.keyId(awsKmsIdentifier_)
160+
.numberOfBytes(algorithm.getDataKeyLength())
161+
.encryptionContext(encryptionContext)
162+
.grantTokens(grantTokens_)
163+
.build());
164+
165+
final ByteBuffer plaintextBuffer = gdkResponse.plaintext().asByteBuffer();
162166
// = compliance/framework/aws-kms/aws-kms-mrk-aware-master-key.txt#2.10
163167
// # If the call succeeds the AWS KMS Generate Data Key response's
164168
// # "Plaintext" MUST match the key derivation input length specified by
165169
// # the algorithm suite included in the input.
166-
if (gdkResult.getPlaintext().limit() != algorithm.getDataKeyLength()) {
170+
if (plaintextBuffer.limit() != algorithm.getDataKeyLength()) {
167171
throw new IllegalStateException("Received an unexpected number of bytes from KMS");
168172
}
169173

170174
final byte[] rawKey = new byte[algorithm.getDataKeyLength()];
171-
gdkResult.getPlaintext().get(rawKey);
175+
plaintextBuffer.get(rawKey);
172176

173177
// = compliance/framework/aws-kms/aws-kms-mrk-aware-master-key.txt#2.10
174178
// # The response's "KeyId"
175179
// # MUST be valid.
176-
final String gdkResultKeyId = gdkResult.getKeyId();
180+
final String gdkResponseKeyId = gdkResponse.keyId();
177181
/* Exceptional Postcondition: Must have an AWS KMS ARN from AWS KMS generateDataKey. */
178-
if (parseInfoFromKeyArn(gdkResultKeyId) == null) {
182+
if (parseInfoFromKeyArn(gdkResponseKeyId) == null) {
179183
throw new IllegalStateException("Received an empty or invalid keyId from KMS");
180184
}
181185

182-
final byte[] encryptedKey = new byte[gdkResult.getCiphertextBlob().remaining()];
183-
gdkResult.getCiphertextBlob().get(encryptedKey);
186+
final ByteBuffer ciphertextBlobBuffer = gdkResponse.ciphertextBlob().asByteBuffer();
187+
final byte[] encryptedKey = new byte[ciphertextBlobBuffer.remaining()];
188+
ciphertextBlobBuffer.get(encryptedKey);
184189

185190
final SecretKeySpec key = new SecretKeySpec(rawKey, algorithm.getDataKeyAlgo());
186191
// = compliance/framework/aws-kms/aws-kms-mrk-aware-master-key.txt#2.10
@@ -195,7 +200,7 @@ public DataKey<AwsKmsMrkAwareMasterKey> generateDataKey(
195200
// # The response's cipher text blob MUST be used as the
196201
// # returned as the ciphertext for the encrypted data key in the output.
197202
encryptedKey,
198-
gdkResultKeyId.getBytes(StandardCharsets.UTF_8),
203+
gdkResponseKeyId.getBytes(StandardCharsets.UTF_8),
199204
this);
200205
}
201206

@@ -220,18 +225,21 @@ public DataKey<AwsKmsMrkAwareMasterKey> encryptDataKey(
220225
// # key MUST use the configured AWS KMS client to make an AWS KMS Encrypt
221226
// # (https://docs.aws.amazon.com/kms/latest/APIReference/
222227
// # API_Encrypt.html) request constructed as follows:
223-
final EncryptResult encryptResult =
228+
final EncryptResponse encryptResponse =
224229
kmsClient_.encrypt(
225-
updateUserAgent(
226-
new EncryptRequest()
227-
.withKeyId(awsKmsIdentifier_)
228-
.withPlaintext(ByteBuffer.wrap(key.getEncoded()))
229-
.withEncryptionContext(encryptionContext)
230-
.withGrantTokens(grantTokens_)));
231-
232-
final byte[] edk = new byte[encryptResult.getCiphertextBlob().remaining()];
233-
encryptResult.getCiphertextBlob().get(edk);
234-
final String encryptResultKeyId = encryptResult.getKeyId();
230+
EncryptRequest.builder()
231+
.overrideConfiguration(API_NAME_INTERCEPTOR)
232+
.keyId(awsKmsIdentifier_)
233+
.plaintext(SdkBytes.fromByteArray(key.getEncoded()))
234+
.encryptionContext(encryptionContext)
235+
.grantTokens(grantTokens_)
236+
.build());
237+
238+
final ByteBuffer ciphertextBlobBuffer = encryptResponse.ciphertextBlob().asByteBuffer();
239+
final byte[] edk = new byte[ciphertextBlobBuffer.remaining()];
240+
ciphertextBlobBuffer.get(edk);
241+
242+
final String encryptResultKeyId = encryptResponse.keyId();
235243
// = compliance/framework/aws-kms/aws-kms-mrk-aware-master-key.txt#2.11
236244
// # The AWS KMS Encrypt response MUST contain a valid "KeyId".
237245
/* Postcondition: Must have an AWS KMS ARN from AWS KMS encrypt. */
@@ -251,16 +259,16 @@ public DataKey<AwsKmsMrkAwareMasterKey> encryptDataKey(
251259
edk,
252260
encryptResultKeyId.getBytes(StandardCharsets.UTF_8),
253261
this);
254-
} catch (final AmazonServiceException asex) {
262+
} catch (final AwsServiceException asex) {
255263
throw new AwsCryptoException(asex);
256264
}
257265
}
258266

259267
/**
260268
* Will attempt to decrypt if awsKmsArnMatchForDecrypt returns true in {@link
261-
* AwsKmsMrkAwareMasterKey#filterEncryptedDataKeys(String, AwsKmsCmkArnInfo, EncryptedDataKey)}.
262-
* An extension of {@link KmsMasterKey#decryptDataKey(CryptoAlgorithm, Collection, Map)} but with
263-
* an awareness of the properties of multi-Region keys.
269+
* AwsKmsMrkAwareMasterKey#filterEncryptedDataKeys(String, String, EncryptedDataKey)}. An
270+
* extension of {@link KmsMasterKey#decryptDataKey(CryptoAlgorithm, Collection, Map)} but with an
271+
* awareness of the properties of multi-Region keys.
264272
*/
265273
@Override
266274
// = compliance/framework/aws-kms/aws-kms-mrk-aware-master-key.txt#2.9
@@ -293,7 +301,7 @@ public DataKey<AwsKmsMrkAwareMasterKey> decryptDataKey(
293301
algorithm,
294302
edk,
295303
encryptionContext);
296-
} catch (final AmazonServiceException amazonServiceException) {
304+
} catch (final AwsServiceException amazonServiceException) {
297305
// = compliance/framework/aws-kms/aws-kms-mrk-aware-master-key.txt#2.9
298306
// # If this attempt
299307
// # results in an error, then these errors MUST be collected.
@@ -333,7 +341,7 @@ public DataKey<AwsKmsMrkAwareMasterKey> decryptDataKey(
333341
*/
334342
static DataKey<AwsKmsMrkAwareMasterKey> decryptSingleEncryptedDataKey(
335343
final AwsKmsMrkAwareMasterKey masterKey,
336-
final AWSKMS client,
344+
final KmsClient client,
337345
final String awsKmsIdentifier,
338346
final List<String> grantTokens,
339347
final CryptoAlgorithm algorithm,
@@ -345,39 +353,41 @@ static DataKey<AwsKmsMrkAwareMasterKey> decryptSingleEncryptedDataKey(
345353
// # configured AWS KMS client to make an AWS KMS Decrypt
346354
// # (https://docs.aws.amazon.com/kms/latest/APIReference/
347355
// # API_Decrypt.html) request constructed as follows:
348-
final DecryptResult decryptResult =
356+
final DecryptResponse decryptResponse =
349357
client.decrypt(
350-
updateUserAgent(
351-
new DecryptRequest()
352-
.withCiphertextBlob(ByteBuffer.wrap(edk.getEncryptedDataKey()))
353-
.withEncryptionContext(encryptionContext)
354-
.withGrantTokens(grantTokens)
355-
.withKeyId(awsKmsIdentifier)));
356-
357-
final String decryptResultKeyId = decryptResult.getKeyId();
358+
DecryptRequest.builder()
359+
.overrideConfiguration(API_NAME_INTERCEPTOR)
360+
.ciphertextBlob(SdkBytes.fromByteArray(edk.getEncryptedDataKey()))
361+
.encryptionContext(encryptionContext)
362+
.grantTokens(grantTokens)
363+
.keyId(awsKmsIdentifier)
364+
.build());
365+
366+
final String decryptResponseKeyId = decryptResponse.keyId();
358367
/* Exceptional Postcondition: Must have a CMK ARN from AWS KMS to match. */
359-
if (decryptResultKeyId == null) {
368+
if (decryptResponseKeyId == null) {
360369
throw new IllegalStateException("Received an empty keyId from KMS");
361370
}
362371
// = compliance/framework/aws-kms/aws-kms-mrk-aware-master-key.txt#2.9
363372
// # If the call succeeds then the response's "KeyId" MUST be equal to the
364373
// # configured AWS KMS key identifier otherwise the function MUST collect
365374
// # an error.
366-
if (!awsKmsIdentifier.equals(decryptResultKeyId)) {
375+
if (!awsKmsIdentifier.equals(decryptResponseKeyId)) {
367376
throw new IllegalStateException(
368377
"Received an invalid response from KMS Decrypt call: Unexpected keyId.");
369378
}
370379

380+
final ByteBuffer plaintextBuffer = decryptResponse.plaintext().asByteBuffer();
371381
// = compliance/framework/aws-kms/aws-kms-mrk-aware-master-key.txt#2.9
372382
// # The response's "Plaintext"'s length MUST equal the length
373383
// # required by the requested algorithm suite otherwise the function MUST
374384
// # collect an error.
375-
if (decryptResult.getPlaintext().limit() != algorithm.getDataKeyLength()) {
385+
if (plaintextBuffer.limit() != algorithm.getDataKeyLength()) {
376386
throw new IllegalStateException("Received an unexpected number of bytes from KMS");
377387
}
378388

379389
final byte[] rawKey = new byte[algorithm.getDataKeyLength()];
380-
decryptResult.getPlaintext().get(rawKey);
390+
plaintextBuffer.get(rawKey);
381391

382392
return new DataKey<>(
383393
new SecretKeySpec(rawKey, algorithm.getDataKeyAlgo()),

0 commit comments

Comments
 (0)