├── .gitignore ├── README.md ├── pom.xml └── src ├── main └── java │ ├── AWSSignerHttpClient.java │ ├── DynamoDBUtil.java │ └── KMSUtil.java └── test ├── java ├── DynamoDBTest.java ├── ElasticSearchTest.java └── S3PresignTest.java └── resources └── log4j2.xml /.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | .settings 3 | .classpath 4 | .project 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # aws-java-sdk-v2-utils 2 | 3 | A few Java utility classes that extends the capabilities of the new aws-java-sdk-v2 library to handle use cases that are not supported by the new library yet. 4 | 5 | ## S3Presigner 6 | Presigning S3 download URLS is common requirement for sharing download links to S3 content. 7 | 8 | 9 | ``` 10 | AwsCredentialsProviderChain credentials = AwsCredentialsProviderChain.of(InstanceProfileCredentialsProvider.builder().build(), DefaultCredentialsProvider.create()); 11 | S3Presigner s3Presigner = S3Presigner.builder().awsCredentials(credentials).build(); 12 | URI presigned = s3Presigner.presignS3UploadLink("some-bucket", "some-directory/some-file.txt"); 13 | ``` 14 | 15 | ## Elasticsearch 16 | the V2 SDK provides an API to manage the instances but not query the Elasticsearch endpoint. Since Elasticsearch is secured with standard AWS security a signed request is required. This utility uses JSON-P to handle the JSON response data. 17 | 18 | 19 | ``` 20 | AWSSignerHttpClient client = AWSSignerHttpClient.builder().serviceName("es").awsCredentials(AwsCredentialsProviderChain.of(InstanceProfileCredentialsProvider.builder().build(), DefaultCredentialsProvider.create())).build(); 21 | SdkHttpFullRequest httpRequest = SdkHttpFullRequest.builder().method(SdkHttpMethod.GET).protocol("https").host("search-some-aws-elasticsearch-domain.us-west-1.es.amazonaws.com").build(); 22 | JsonObject obj = client.execute(httpRequest); 23 | ``` 24 | 25 | ##DynamoDB to JSON 26 | A utility for converting DynamoDB AttributeValue structures into JSON-P objects and vice versa. Support for compressing JSON into AttributeValue byte storage is also available to avoid exceeding the 400k entry size limit. 27 | 28 | ``` 29 | Map map = new HashMap<>(); 30 | map.put("string", AttributeValue.builder().s("test").build()); 31 | AttributeValue test = AttributeValue.builder().m(map).build(); 32 | JsonObject json = (JsonObject) DynamoDBUtil.toJson(test); 33 | 34 | JsonObjectBuilder builder = Json.createObjectBuilder(); 35 | builder.add("string", "test"); 36 | Map attrValue = DynamoDBUtil.toAttribute(builder.build()); 37 | ``` 38 | 39 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 4 | 4.0.0 5 | aws-java-sdk-v2-utils 6 | aws-java-sdk-v2-utils 7 | 1.0-SNAPSHOT 8 | 9 | 10 | 11 | UTF-8 12 | 1.8 13 | 1.8 14 | 2.15.7 15 | 16 | 17 | 18 | 19 | 20 | jakarta.json 21 | jakarta.json-api 22 | 1.1.6 23 | provided 24 | 25 | 26 | 27 | 28 | 29 | 30 | software.amazon.awssdk 31 | core 32 | ${aws.version} 33 | pom 34 | provided 35 | 36 | 37 | 38 | 39 | software.amazon.awssdk 40 | s3 41 | ${aws.version} 42 | provided 43 | 44 | 45 | 46 | 47 | software.amazon.awssdk 48 | dynamodb 49 | ${aws.version} 50 | provided 51 | 52 | 53 | 54 | 55 | software.amazon.awssdk 56 | kms 57 | ${aws.version} 58 | provided 59 | 60 | 61 | 62 | 63 | org.apache.johnzon 64 | johnzon-jaxrs 65 | 1.2.8 66 | test 67 | 68 | 69 | 70 | 71 | 72 | org.apache.cxf 73 | cxf-rt-frontend-jaxrs 74 | 3.4.0 75 | test 76 | 77 | 78 | 79 | org.apache.cxf 80 | cxf-rt-rs-client 81 | 3.4.0 82 | test 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | software.amazon.awssdk 91 | apache-client 92 | ${aws.version} 93 | test 94 | 95 | 96 | 97 | 98 | 99 | org.apache.logging.log4j 100 | log4j-core 101 | 2.13.3 102 | 103 | 104 | org.apache.logging.log4j 105 | log4j-api 106 | 2.13.3 107 | 108 | 109 | org.apache.logging.log4j 110 | log4j-slf4j-impl 111 | 2.13.3 112 | 113 | 114 | org.apache.logging.log4j 115 | log4j-1.2-api 116 | 2.13.3 117 | 118 | 119 | 120 | 121 | 122 | 123 | org.junit.jupiter 124 | junit-jupiter-engine 125 | 5.7.0 126 | test 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | org.apache.maven.plugins 135 | maven-surefire-plugin 136 | 2.22.2 137 | 138 | 139 | 140 | 141 | -------------------------------------------------------------------------------- /src/main/java/AWSSignerHttpClient.java: -------------------------------------------------------------------------------- 1 | import java.time.Duration; 2 | import java.util.Collections; 3 | import java.util.LinkedHashMap; 4 | import java.util.List; 5 | import java.util.Optional; 6 | 7 | import javax.json.Json; 8 | import javax.json.JsonStructure; 9 | 10 | import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider; 11 | import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider; 12 | import software.amazon.awssdk.auth.signer.Aws4Signer; 13 | import software.amazon.awssdk.auth.signer.AwsSignerExecutionAttribute; 14 | import software.amazon.awssdk.core.RequestOverrideConfiguration; 15 | import software.amazon.awssdk.core.SdkField; 16 | import software.amazon.awssdk.core.SdkRequest; 17 | import software.amazon.awssdk.core.client.config.SdkClientConfiguration; 18 | import software.amazon.awssdk.core.client.config.SdkClientOption; 19 | import software.amazon.awssdk.core.exception.SdkException; 20 | import software.amazon.awssdk.core.http.ExecutionContext; 21 | import software.amazon.awssdk.core.http.HttpResponseHandler; 22 | import software.amazon.awssdk.core.interceptor.ExecutionAttributes; 23 | import software.amazon.awssdk.core.interceptor.ExecutionInterceptorChain; 24 | import software.amazon.awssdk.core.interceptor.InterceptorContext; 25 | import software.amazon.awssdk.core.internal.http.AmazonSyncHttpClient; 26 | import software.amazon.awssdk.core.internal.http.CombinedResponseHandler; 27 | import software.amazon.awssdk.core.internal.http.loader.DefaultSdkHttpClientBuilder; 28 | import software.amazon.awssdk.core.retry.RetryPolicy; 29 | import software.amazon.awssdk.core.sync.RequestBody; 30 | import software.amazon.awssdk.http.SdkHttpClient; 31 | import software.amazon.awssdk.http.SdkHttpConfigurationOption; 32 | import software.amazon.awssdk.http.SdkHttpFullRequest; 33 | import software.amazon.awssdk.http.SdkHttpFullResponse; 34 | import software.amazon.awssdk.metrics.NoOpMetricCollector; 35 | import software.amazon.awssdk.regions.Region; 36 | import software.amazon.awssdk.regions.providers.DefaultAwsRegionProviderChain; 37 | import software.amazon.awssdk.utils.AttributeMap; 38 | import software.amazon.awssdk.utils.IoUtils; 39 | 40 | public class AWSSignerHttpClient implements AutoCloseable { 41 | 42 | private String serviceName; 43 | private Region region; 44 | private AwsCredentialsProvider awsCredentialsProvider; 45 | private Duration readTimeOut; 46 | private SdkHttpClient sdkClient; 47 | // required by client to avoid NPE 48 | private SdkRequest sdkRequest = new ServiceSDKRequest(); 49 | private ExecutionInterceptorChain execInterceptorChain = new ExecutionInterceptorChain(Collections.emptyList()); 50 | private AmazonSyncHttpClient awsClient; 51 | private Aws4Signer signer; 52 | 53 | private AWSSignerHttpClient() { 54 | 55 | } 56 | 57 | public static Builder builder() { 58 | return new Builder(); 59 | } 60 | 61 | public T execute(SdkHttpFullRequest httpRequest, HttpResponseHandler responseHandler) { 62 | return execute(httpRequest, responseHandler, new ErrorHandler()); 63 | } 64 | 65 | public T execute(SdkHttpFullRequest httpRequest) { 66 | return execute(httpRequest, new JsonHandler(), new ErrorHandler()); 67 | } 68 | 69 | public T execute(SdkHttpFullRequest httpRequest, HttpResponseHandler responseHandler, HttpResponseHandler errorHandler) { 70 | InterceptorContext incerceptorContext = InterceptorContext.builder().request(sdkRequest).httpRequest(httpRequest).build(); 71 | ExecutionContext.Builder execContextBuilder = ExecutionContext.builder(); 72 | execContextBuilder.signer(signer); 73 | execContextBuilder.interceptorChain(execInterceptorChain); 74 | execContextBuilder.metricCollector(NoOpMetricCollector.create()); 75 | ExecutionAttributes executionAttributes = new ExecutionAttributes(); 76 | executionAttributes.putAttribute(AwsSignerExecutionAttribute.AWS_CREDENTIALS, awsCredentialsProvider.resolveCredentials()); 77 | executionAttributes.putAttribute(AwsSignerExecutionAttribute.SERVICE_SIGNING_NAME, serviceName); 78 | executionAttributes.putAttribute(AwsSignerExecutionAttribute.SIGNING_REGION, region); 79 | execContextBuilder.executionAttributes(executionAttributes); 80 | execContextBuilder.interceptorContext(incerceptorContext).build(); 81 | ExecutionContext execContext = execContextBuilder.build(); 82 | return awsClient.requestExecutionBuilder().executionContext(execContext).originalRequest(sdkRequest).request(httpRequest).execute(new CombinedResponseHandler(responseHandler, errorHandler)); 83 | } 84 | 85 | public static class Builder { 86 | AWSSignerHttpClient client = new AWSSignerHttpClient(); 87 | 88 | public AWSSignerHttpClient build() { 89 | if (client.awsCredentialsProvider == null) { 90 | DefaultCredentialsProvider provider = DefaultCredentialsProvider.create(); 91 | client.awsCredentialsProvider = provider; 92 | } 93 | if (client.sdkClient == null) { 94 | AttributeMap options = client.readTimeOut != null ? AttributeMap.builder().put(SdkHttpConfigurationOption.READ_TIMEOUT, client.readTimeOut).build() : AttributeMap.empty(); 95 | client.sdkClient = new DefaultSdkHttpClientBuilder().buildWithDefaults(options); 96 | } 97 | 98 | if (client.region == null) { 99 | client.region = new DefaultAwsRegionProviderChain().getRegion(); 100 | } 101 | client.signer = Aws4Signer.create(); 102 | // signer.setRegionName(client.region.value()); 103 | // signer.setServiceName(client.serviceName); 104 | // client.signingProvider = StaticSignerProvider.create(signer); 105 | SdkClientConfiguration clientConfiguration = SdkClientConfiguration.builder().option(SdkClientOption.ADDITIONAL_HTTP_HEADERS, new LinkedHashMap<>()).option(SdkClientOption.CRC32_FROM_COMPRESSED_DATA_ENABLED, true).option(SdkClientOption.SYNC_HTTP_CLIENT, client.sdkClient).option(SdkClientOption.RETRY_POLICY, RetryPolicy.none()).build(); 106 | client.awsClient = new AmazonSyncHttpClient(clientConfiguration); 107 | return client; 108 | } 109 | 110 | public Builder sdkClient(SdkHttpClient sdkClient) { 111 | client.sdkClient = sdkClient; 112 | return this; 113 | } 114 | 115 | public Builder serviceName(String serviceName) { 116 | client.serviceName = serviceName; 117 | return this; 118 | } 119 | 120 | public Builder region(Region region) { 121 | client.region = region; 122 | return this; 123 | } 124 | 125 | public Builder awsCredentials(AwsCredentialsProvider awsCredentialsProvider) { 126 | client.awsCredentialsProvider = awsCredentialsProvider; 127 | return this; 128 | } 129 | 130 | public Builder readTimeout(Duration readTimeOut) { 131 | client.readTimeOut = readTimeOut; 132 | return this; 133 | } 134 | } 135 | 136 | public static class ServiceSDKRequest extends SdkRequest { 137 | 138 | @Override 139 | public Optional overrideConfiguration() { 140 | return Optional.empty(); 141 | } 142 | 143 | @Override 144 | public Builder toBuilder() { 145 | return new Builder() { 146 | 147 | @Override 148 | public RequestOverrideConfiguration overrideConfiguration() { 149 | return null; 150 | } 151 | 152 | @Override 153 | public SdkRequest build() { 154 | return new ServiceSDKRequest(); 155 | } 156 | 157 | }; 158 | } 159 | 160 | @Override 161 | public List> sdkFields() { 162 | return Collections.unmodifiableList(Collections.emptyList()); 163 | } 164 | 165 | } 166 | 167 | public static class ErrorHandler implements HttpResponseHandler { 168 | 169 | @Override 170 | public SdkException handle(SdkHttpFullResponse response, ExecutionAttributes executionAttributes) throws Exception { 171 | String responseMsg = response.content().isPresent() ? IoUtils.toUtf8String(response.content().get()) : ""; 172 | return SdkException.builder().message(String.format("%d: %s", response.statusCode(), responseMsg)).build(); 173 | } 174 | 175 | } 176 | 177 | public static class JsonHandler implements HttpResponseHandler { 178 | 179 | @Override 180 | public T handle(SdkHttpFullResponse response, ExecutionAttributes executionAttributes) throws Exception { 181 | return response.content().isPresent() ? (T) Json.createReader(response.content().get()).read() : null; 182 | } 183 | 184 | } 185 | 186 | @Override 187 | public void close() { 188 | awsClient.close(); 189 | 190 | } 191 | } -------------------------------------------------------------------------------- /src/main/java/DynamoDBUtil.java: -------------------------------------------------------------------------------- 1 | import java.io.ByteArrayInputStream; 2 | import java.io.ByteArrayOutputStream; 3 | import java.io.IOException; 4 | import java.math.BigDecimal; 5 | import java.util.HashMap; 6 | import java.util.LinkedList; 7 | import java.util.List; 8 | import java.util.Map; 9 | import java.util.zip.DataFormatException; 10 | import java.util.zip.Deflater; 11 | import java.util.zip.Inflater; 12 | 13 | import javax.json.Json; 14 | import javax.json.JsonArray; 15 | import javax.json.JsonArrayBuilder; 16 | import javax.json.JsonNumber; 17 | import javax.json.JsonObject; 18 | import javax.json.JsonObjectBuilder; 19 | import javax.json.JsonString; 20 | import javax.json.JsonStructure; 21 | import javax.json.JsonValue; 22 | 23 | import software.amazon.awssdk.core.SdkBytes; 24 | import software.amazon.awssdk.services.dynamodb.model.AttributeValue; 25 | 26 | /** This is a utility for converting DynamoDB AttributeValues to and from Java JSON-P objects */ 27 | public class DynamoDBUtil { 28 | 29 | public static void addList(String key, JsonObjectBuilder objectBuilder, List items) { 30 | if (!items.isEmpty()) { 31 | JsonArrayBuilder builder = Json.createArrayBuilder(); 32 | items.forEach(i -> builder.add(i)); 33 | objectBuilder.add(key, builder.build()); 34 | } 35 | 36 | } 37 | 38 | public static JsonArray toJson(List attributeValues) { 39 | if (attributeValues == null) { 40 | return null; 41 | } 42 | JsonArrayBuilder valueBuilder = Json.createArrayBuilder(); 43 | for (AttributeValue a : attributeValues) { 44 | add(toJson(a), valueBuilder); 45 | } 46 | return valueBuilder.build(); 47 | } 48 | 49 | public static JsonObject toJson(Map attributeValues) { 50 | if (attributeValues == null) { 51 | return null; 52 | } 53 | JsonObjectBuilder valueBuilder = Json.createObjectBuilder(); 54 | for (Map.Entry a : attributeValues.entrySet()) { 55 | add(a.getKey(), toJson(a.getValue()), valueBuilder); 56 | } 57 | return valueBuilder.build(); 58 | } 59 | 60 | public static void add(String key, Object value, JsonObjectBuilder object) { 61 | if (value instanceof JsonValue) { 62 | object.add(key, (JsonValue) value); 63 | // with json-p 1.0 can't create JsonString or JsonNumber so simply setting JsonValue not an option. 64 | } else if (value instanceof String) { 65 | object.add(key, (String) value); 66 | } else if (value instanceof BigDecimal) { 67 | object.add(key, (BigDecimal) value); 68 | } else if (value instanceof Boolean) { 69 | object.add(key, (Boolean) value); 70 | } else if (value == null || value.equals(JsonValue.NULL)) { 71 | object.addNull(key); 72 | } 73 | 74 | } 75 | 76 | public static void add(Object value, JsonArrayBuilder array) { 77 | if (value instanceof JsonValue) { 78 | array.add((JsonValue) value); 79 | } else if (value instanceof String) { 80 | array.add((String) value); 81 | } else if (value instanceof BigDecimal) { 82 | array.add((BigDecimal) value); 83 | } else if (value instanceof Boolean) { 84 | array.add((Boolean) value); 85 | } else if (value.equals(JsonValue.NULL)) { 86 | array.addNull(); 87 | } 88 | 89 | } 90 | 91 | public static Object toJson(AttributeValue attributeValue) { 92 | // with json-p 1.1 Json.createValue() can be used. 93 | 94 | if (attributeValue == null) { 95 | return null; 96 | } 97 | if (attributeValue.s() != null) { 98 | return attributeValue.s(); 99 | } 100 | if (attributeValue.n() != null) { 101 | return new BigDecimal(attributeValue.n()); 102 | } 103 | if (attributeValue.bool() != null) { 104 | // return attributeValue.bool() ? JsonValue.TRUE : JsonValue.FALSE; 105 | return attributeValue.bool(); 106 | } 107 | 108 | if (attributeValue.b() != null) { 109 | // return Base64.getEncoder().encodeToString(attributeValue.b().array()); 110 | return null; 111 | } 112 | 113 | if (attributeValue.nul() != null && attributeValue.nul()) { 114 | return JsonValue.NULL; 115 | } 116 | 117 | if (!attributeValue.m().isEmpty()) { 118 | return toJson(attributeValue.m()); 119 | } 120 | if (!attributeValue.l().isEmpty()) { 121 | return toJson(attributeValue.l()); 122 | } 123 | 124 | if (!attributeValue.ss().isEmpty()) { 125 | return attributeValue.ss(); 126 | } 127 | 128 | if (!attributeValue.ns().isEmpty()) { 129 | return attributeValue.ns(); 130 | } 131 | 132 | if (!attributeValue.bs().isEmpty()) { 133 | //return attributeValue.bs(); 134 | return null; 135 | } 136 | return null; 137 | } 138 | 139 | public static Map toAttribute(JsonObject jsonObject) { 140 | Map attribute = new HashMap<>(); 141 | jsonObject.entrySet().forEach(e -> { 142 | attribute.put(e.getKey(), toAttribute(e.getValue())); 143 | }); 144 | return attribute; 145 | } 146 | 147 | public static List toAttribute(JsonArray jsonArray) { 148 | List attributes = new LinkedList<>(); 149 | jsonArray.forEach(e -> { 150 | attributes.add(toAttribute(e)); 151 | }); 152 | return attributes; 153 | } 154 | 155 | public static AttributeValue toAttribute(JsonValue jsonValue) { 156 | if (jsonValue == null) { 157 | return null; 158 | } 159 | switch (jsonValue.getValueType()) { 160 | case STRING: 161 | return AttributeValue.builder().s(((JsonString) jsonValue).getString()).build(); 162 | case OBJECT: 163 | return AttributeValue.builder().m(toAttribute((JsonObject) jsonValue)).build(); 164 | case ARRAY: 165 | return AttributeValue.builder().l(toAttribute((JsonArray) jsonValue)).build(); 166 | case NUMBER: 167 | return AttributeValue.builder().n(((JsonNumber) jsonValue).toString()).build(); 168 | case TRUE: 169 | return AttributeValue.builder().bool(true).build(); 170 | case FALSE: 171 | return AttributeValue.builder().bool(false).build(); 172 | case NULL: 173 | return AttributeValue.builder().nul(true).build(); 174 | } 175 | 176 | return null; 177 | } 178 | 179 | public static AttributeValue compress(Map attributeValues) throws IOException { 180 | return compress(toJson(attributeValues)); 181 | } 182 | 183 | public static AttributeValue compress(List attributeValues) throws IOException { 184 | return compress(toJson(attributeValues)); 185 | } 186 | 187 | public static AttributeValue compress(JsonStructure jsonStructure) throws IOException { 188 | ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); 189 | Json.createWriter(outputStream).write(jsonStructure); 190 | outputStream.close(); 191 | byte[] jsonBinary = outputStream.toByteArray(); 192 | 193 | outputStream = new ByteArrayOutputStream(); 194 | Deflater deflater = new Deflater(); 195 | deflater.setInput(jsonBinary); 196 | deflater.finish(); 197 | byte[] buffer = new byte[1024]; 198 | while (!deflater.finished()) { 199 | int count = deflater.deflate(buffer); // returns the generated code... index 200 | outputStream.write(buffer, 0, count); 201 | } 202 | outputStream.close(); 203 | jsonBinary = outputStream.toByteArray(); 204 | 205 | return AttributeValue.builder().b(SdkBytes.fromByteArray(jsonBinary)).build(); 206 | } 207 | 208 | public static JsonStructure decompress(AttributeValue attributeValue) throws IOException, DataFormatException { 209 | Inflater inflater = new Inflater(); 210 | byte[] jsonBinary = attributeValue.b().asByteArray(); 211 | inflater.setInput(jsonBinary); 212 | ByteArrayOutputStream outputStream = new ByteArrayOutputStream(jsonBinary.length); 213 | byte[] buffer = new byte[1024]; 214 | while (!inflater.finished()) { 215 | int count = inflater.inflate(buffer); 216 | outputStream.write(buffer, 0, count); 217 | } 218 | outputStream.close(); 219 | byte[] output = outputStream.toByteArray(); 220 | ByteArrayInputStream bis = new ByteArrayInputStream(output); 221 | return Json.createReader(bis).read(); 222 | } 223 | 224 | } 225 | -------------------------------------------------------------------------------- /src/main/java/KMSUtil.java: -------------------------------------------------------------------------------- 1 | import java.nio.ByteBuffer; 2 | import java.util.Base64; 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | import javax.crypto.Cipher; 7 | import javax.crypto.SecretKey; 8 | import javax.crypto.spec.SecretKeySpec; 9 | 10 | import software.amazon.awssdk.core.SdkBytes; 11 | import software.amazon.awssdk.services.dynamodb.DynamoDbClient; 12 | import software.amazon.awssdk.services.dynamodb.model.AttributeValue; 13 | import software.amazon.awssdk.services.dynamodb.model.GetItemRequest; 14 | import software.amazon.awssdk.services.dynamodb.model.GetItemResponse; 15 | import software.amazon.awssdk.services.kms.KmsClient; 16 | import software.amazon.awssdk.services.kms.model.DecryptRequest; 17 | import software.amazon.awssdk.services.kms.model.DecryptResponse; 18 | 19 | //Utility class used to lazy load an AWS data key from some type of text file or optionally retrieved from DynamoDB 20 | //https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#data-keys 21 | public class KMSUtil { 22 | private static SecretKey secret; 23 | private static KmsClient kmsClient; 24 | private static DynamoDbClient dynamdDBClient; 25 | private static String dynamoDBTable; 26 | private static AttributeValue dynamoDBID; 27 | private static String dynamoDBAttr; 28 | 29 | public static void initialize(KmsClient kmsClient) { 30 | KMSUtil.kmsClient = kmsClient; 31 | } 32 | 33 | public static void initialize(KmsClient kmsClient, DynamoDbClient dynamdDBClient, String dynamoDBTable, AttributeValue dynamoDBID, String dynamoDBAttr) { 34 | initialize(kmsClient); 35 | KMSUtil.kmsClient = kmsClient; 36 | KMSUtil.dynamdDBClient = dynamdDBClient; 37 | KMSUtil.dynamoDBTable = dynamoDBTable; 38 | KMSUtil.dynamoDBID = dynamoDBID; 39 | KMSUtil.dynamoDBAttr = dynamoDBAttr; 40 | } 41 | 42 | private static synchronized void loadEncryptionKey() throws Exception { 43 | if (secret == null) { 44 | if (dynamdDBClient == null) { 45 | throw new Exception("Uninitialized"); 46 | } 47 | Map repoQueryValues = new HashMap<>(); 48 | repoQueryValues.put("ID", dynamoDBID); 49 | GetItemResponse repoResponse = dynamdDBClient.getItem(GetItemRequest.builder().tableName(dynamoDBTable).key(repoQueryValues).projectionExpression(dynamoDBAttr).build()); 50 | Map repoItemValues = repoResponse.item(); 51 | if (repoItemValues == null) { 52 | throw new Exception(String.format("Unable to locate %s in DynamoDB table %s", dynamoDBID, dynamoDBTable)); 53 | } 54 | if (!repoItemValues.containsKey((dynamoDBAttr))) { 55 | throw new Exception(String.format("Unable to locate attribute %s for item ID %s in table %s", dynamoDBAttr, dynamoDBID, dynamoDBTable)); 56 | } 57 | String encrypted = repoItemValues.get(dynamoDBAttr).s(); 58 | loadEncryptionKey(encrypted); 59 | } 60 | } 61 | 62 | private static synchronized void loadEncryptionKey(String encrypted) throws Exception { 63 | if (secret == null) { 64 | if (kmsClient == null) { 65 | throw new Exception("Uninitialized"); 66 | } 67 | byte[] encryptedBytes = Base64.getDecoder().decode(encrypted); 68 | DecryptResponse kmsResponse = kmsClient.decrypt((DecryptRequest.builder().ciphertextBlob(SdkBytes.fromByteArray(encryptedBytes))).build()); 69 | final byte[] decBytes = kmsResponse.plaintext().asByteArray(); 70 | 71 | secret = new SecretKeySpec(decBytes, "AES"); 72 | } 73 | 74 | // String encS3Key = Base64.getEncoder().encodeToString(encBytes); 75 | // EncryptResponse kmsResponse = kmsClient.encrypt(EncryptRequest.builder().keyId(kmsAlias).plaintext(ByteBuffer.wrap(s3Key.getBytes())).build()); 76 | // final byte[] encBytes = new byte[kmsResponse.ciphertextBlob().remaining()]; 77 | // kmsResponse.ciphertextBlob().get(encBytes); 78 | // String encS3Key = Base64.getEncoder().encodeToString(encBytes); 79 | } 80 | 81 | public static final String encrypt(String value) throws Exception { 82 | loadEncryptionKey(); 83 | Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); 84 | cipher.init(Cipher.ENCRYPT_MODE, secret); 85 | byte[] secureReturnURL = cipher.doFinal(value.getBytes()); 86 | return Base64.getEncoder().encodeToString(secureReturnURL); 87 | } 88 | 89 | public static final String decrypt(String value) throws Exception { 90 | loadEncryptionKey(); 91 | byte[] secureReturnURL = Base64.getDecoder().decode(value); 92 | Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); 93 | cipher.init(Cipher.DECRYPT_MODE, secret); 94 | secureReturnURL = cipher.doFinal(secureReturnURL); 95 | return new String(secureReturnURL); 96 | } 97 | 98 | } 99 | -------------------------------------------------------------------------------- /src/test/java/DynamoDBTest.java: -------------------------------------------------------------------------------- 1 | import static org.junit.jupiter.api.Assertions.assertEquals; 2 | import static org.junit.jupiter.api.Assertions.assertNotNull; 3 | 4 | import java.math.BigInteger; 5 | import java.util.HashMap; 6 | import java.util.LinkedList; 7 | import java.util.List; 8 | import java.util.Map; 9 | 10 | import javax.json.Json; 11 | import javax.json.JsonArray; 12 | import javax.json.JsonArrayBuilder; 13 | import javax.json.JsonObject; 14 | import javax.json.JsonObjectBuilder; 15 | 16 | import org.junit.jupiter.api.Test; 17 | 18 | import software.amazon.awssdk.services.dynamodb.model.AttributeValue; 19 | 20 | public class DynamoDBTest { 21 | 22 | @Test 23 | public void attributeValueToJSON() throws Exception { 24 | Map map = new HashMap<>(); 25 | map.put("string", AttributeValue.builder().s("test").build()); 26 | map.put("boolean", AttributeValue.builder().bool(true).build()); 27 | map.put("number", AttributeValue.builder().n("123").build()); 28 | Map subMap = new HashMap<>(); 29 | subMap.put("key", AttributeValue.builder().s("value").build()); 30 | map.put("map", AttributeValue.builder().m(subMap).build()); 31 | List list = new LinkedList<>(); 32 | list.add(AttributeValue.builder().s("entryOne").build()); 33 | list.add(AttributeValue.builder().s("entryTwo").build()); 34 | map.put("list", AttributeValue.builder().l(list).build()); 35 | 36 | AttributeValue test = AttributeValue.builder().m(map).build(); 37 | JsonObject json = (JsonObject) DynamoDBUtil.toJson(test); 38 | 39 | System.out.format("toJson: %s\n", json); 40 | assertEquals("test", json.getString("string")); 41 | assertEquals(true, json.getBoolean("boolean")); 42 | assertEquals(new BigInteger("123"), json.getJsonNumber("number").bigIntegerValue()); 43 | JsonObject mapJson = json.getJsonObject("map"); 44 | assertNotNull(mapJson); 45 | assertEquals("value", mapJson.getString("key")); 46 | JsonArray arrJson = json.getJsonArray("list"); 47 | assertNotNull(arrJson); 48 | assertEquals("entryOne", arrJson.getString(0)); 49 | assertEquals("entryTwo", arrJson.getString(1)); 50 | } 51 | 52 | @Test 53 | public void jsonToAttributeValue() throws Exception { 54 | JsonObjectBuilder builder = Json.createObjectBuilder(); 55 | builder.add("string", "test"); 56 | builder.add("boolean", true); 57 | builder.add("number", 123); 58 | JsonObjectBuilder subBuilder = Json.createObjectBuilder(); 59 | subBuilder.add("key", "value"); 60 | builder.add("map", subBuilder); 61 | JsonArrayBuilder list = Json.createArrayBuilder(); 62 | list.add("entryOne"); 63 | list.add("entryTwo"); 64 | builder.add("list", list); 65 | Map attrValue = DynamoDBUtil.toAttribute(builder.build()); 66 | 67 | System.out.format("toAttributeValue: %s\n", attrValue); 68 | assertEquals("test", attrValue.get("string").s()); 69 | assertEquals(true, attrValue.get("boolean").bool()); 70 | assertEquals("123", attrValue.get("number").n()); 71 | Map subAttrValue = attrValue.get("map").m(); 72 | assertNotNull(subAttrValue); 73 | assertEquals("value", subAttrValue.get("key").s()); 74 | List listAttrValue = attrValue.get("list").l(); 75 | assertNotNull(listAttrValue); 76 | assertEquals("entryOne", listAttrValue.get(0).s()); 77 | assertEquals("entryTwo", listAttrValue.get(1).s()); 78 | } 79 | 80 | /** 81 | * At times it may be necessary to store part of the data in a compressed format to avoid exceeding the 400k entry limit. 82 | */ 83 | @Test 84 | public void compression() throws Exception { 85 | JsonObject entry = Json.createObjectBuilder().add("test", "value").build(); 86 | AttributeValue compressed = DynamoDBUtil.compress(entry); 87 | entry = (JsonObject) DynamoDBUtil.decompress(compressed); 88 | assertEquals("value", entry.getString("test")); 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /src/test/java/ElasticSearchTest.java: -------------------------------------------------------------------------------- 1 | import static org.junit.jupiter.api.Assertions.assertNotNull; 2 | 3 | import java.io.ByteArrayInputStream; 4 | import java.time.Duration; 5 | 6 | import javax.json.JsonObject; 7 | 8 | import org.junit.jupiter.api.Test; 9 | 10 | import software.amazon.awssdk.auth.credentials.AwsCredentialsProviderChain; 11 | import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider; 12 | import software.amazon.awssdk.auth.credentials.InstanceProfileCredentialsProvider; 13 | import software.amazon.awssdk.http.SdkHttpFullRequest; 14 | import software.amazon.awssdk.http.SdkHttpMethod; 15 | 16 | public class ElasticSearchTest { 17 | 18 | public static final String ELASTIC_SEARCH_HOST = "XXXXXXXXXXXXXX.us-west-1.es.amazonaws.com"; 19 | 20 | 21 | //@Test 22 | public void searchTest() throws Exception { 23 | AWSSignerHttpClient client = AWSSignerHttpClient.builder().serviceName("es").awsCredentials(AwsCredentialsProviderChain.of(InstanceProfileCredentialsProvider.builder().build(), DefaultCredentialsProvider.create())).build(); 24 | SdkHttpFullRequest httpRequest = SdkHttpFullRequest.builder().method(SdkHttpMethod.GET).protocol("https").host(ELASTIC_SEARCH_HOST).appendRawQueryParameter("pretty", "true").build(); 25 | JsonObject obj = client. execute(httpRequest); 26 | assertNotNull(obj); 27 | System.out.format("Result: %s\n", obj); 28 | 29 | } 30 | 31 | //@Test 32 | public void createIndexTest() throws Exception { 33 | AWSSignerHttpClient client = AWSSignerHttpClient.builder().serviceName("es").readTimeout(Duration.ofMinutes(1)).awsCredentials(AwsCredentialsProviderChain.of(InstanceProfileCredentialsProvider.builder().build(), DefaultCredentialsProvider.create())).build(); 34 | 35 | String json = "{\"settings\": { \"index\": { \"number_of_shards\": 5, \"number_of_replicas\": 1 } } }"; 36 | SdkHttpFullRequest httpRequest = SdkHttpFullRequest.builder().method(SdkHttpMethod.PUT).protocol("https").host(ELASTIC_SEARCH_HOST).encodedPath("/test-index").appendRawQueryParameter("pretty", "true").contentStreamProvider(() -> new ByteArrayInputStream(json.getBytes())).appendHeader("Content-Type", "application/json") 37 | .appendHeader("Content-Length", String.valueOf(json.getBytes().length)).build(); 38 | JsonObject obj = client. execute(httpRequest); 39 | assertNotNull(obj); 40 | System.out.format("Result: %s\n", obj); 41 | 42 | httpRequest = SdkHttpFullRequest.builder().method(SdkHttpMethod.DELETE).protocol("https").host(ELASTIC_SEARCH_HOST).encodedPath("/test-index").appendRawQueryParameter("pretty", "true").build(); 43 | obj = client. execute(httpRequest); 44 | assertNotNull(obj); 45 | System.out.format("Result: %s\n", obj); 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/test/java/S3PresignTest.java: -------------------------------------------------------------------------------- 1 | import java.time.Duration; 2 | 3 | import javax.json.Json; 4 | import javax.json.JsonObject; 5 | import javax.ws.rs.client.Client; 6 | import javax.ws.rs.client.ClientBuilder; 7 | import javax.ws.rs.client.Entity; 8 | import javax.ws.rs.client.WebTarget; 9 | import javax.ws.rs.core.MediaType; 10 | import javax.ws.rs.core.Response; 11 | 12 | import org.apache.johnzon.jaxrs.JsrProvider; 13 | import org.junit.jupiter.api.Test; 14 | 15 | import software.amazon.awssdk.services.s3.presigner.S3Presigner; 16 | import software.amazon.awssdk.services.s3.presigner.model.PresignedGetObjectRequest; 17 | import software.amazon.awssdk.services.s3.presigner.model.PresignedPutObjectRequest; 18 | 19 | public class S3PresignTest { 20 | 21 | 22 | public static final String S3_BUCKET = "XXXXXXXXXXXXXXXXXX"; 23 | public static final String S3_FILENAME = "/test/test.txt"; 24 | 25 | //@Test 26 | public void presignTest() throws Exception { 27 | S3Presigner presigner = S3Presigner.create(); 28 | Client client = ClientBuilder.newClient(); 29 | client.register(JsrProvider.class); 30 | PresignedGetObjectRequest gpresigned = presigner.presignGetObject(r -> r.signatureDuration(Duration.ofMinutes(5)).getObjectRequest(gor -> gor.bucket(S3_BUCKET).key(S3_FILENAME).build())); 31 | 32 | WebTarget target = client.target(gpresigned.url().toURI()); 33 | System.out.format("Download URL: %s\n", gpresigned.url()); 34 | Response response = target.request().get(); 35 | System.out.format("Download Response: %d %s\n", response.getStatus(), response.readEntity(String.class)); 36 | 37 | PresignedPutObjectRequest ppresigned = presigner.presignPutObject(r -> r.signatureDuration(Duration.ofMinutes(5)).putObjectRequest(por -> por.bucket(S3_BUCKET).key(S3_FILENAME).build())); 38 | 39 | System.out.format("Upload URL: %s\n", ppresigned.url()); 40 | target = client.target(ppresigned.url().toURI()); 41 | JsonObject obj = Json.createObjectBuilder().add("test", "test").build(); 42 | response = target.request().put(Entity.entity(obj, MediaType.APPLICATION_JSON)); 43 | System.out.format("Upload Response: %d %s\n", response.getStatus(), response.readEntity(String.class)); 44 | 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/test/resources/log4j2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | --------------------------------------------------------------------------------