Skip to content

chore(Java): Examples for Mutations #742

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Sep 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions AwsCryptographicMaterialProviders/runtimes/java/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,23 @@ group = "software.amazon.cryptography"
version = "1.6.0-SNAPSHOT"
description = "AWS Cryptographic Material Providers Library"

sourceSets {
create("examples") {
compileClasspath += sourceSets.main.get().output
runtimeClasspath += sourceSets.main.get().output
}
create("testExamples") {
compileClasspath += sourceSets.test.get().output + sourceSets["examples"].output + sourceSets.main.get().output
runtimeClasspath += sourceSets.test.get().output + sourceSets["examples"].output + sourceSets.main.get().output
}
}
val examplesImplementation by configurations.getting{
extendsFrom(configurations.testImplementation.get())
}
val testExamplesImplementation by configurations.getting{
extendsFrom(configurations.testImplementation.get())
}

java {
toolchain.languageVersion.set(JavaLanguageVersion.of(8))
sourceSets["main"].java {
Expand All @@ -29,6 +46,12 @@ java {
sourceSets["test"].java {
srcDir("src/test")
}
sourceSets["examples"].java {
srcDir("src/examples")
}
sourceSets["testExamples"].java {
srcDir("src/testExamples")
}
withJavadocJar()
withSourcesJar()
}
Expand Down Expand Up @@ -83,6 +106,15 @@ dependencies {

// https://mvnrepository.com/artifact/org.testng/testng
testImplementation("org.testng:testng:7.5")

// Example Dependencies are marked as testImplementation
testImplementation("software.amazon.awssdk:arns")
testImplementation("software.amazon.awssdk:auth")
testImplementation("software.amazon.awssdk:sts")
testImplementation("software.amazon.awssdk:apache-client:2.19.0")
testAnnotationProcessor("org.projectlombok:lombok:1.18.30")
testImplementation("com.google.code.findbugs:jsr305:3.0.2")

}

publishing {
Expand Down Expand Up @@ -256,6 +288,22 @@ tasks.test {
})
}

val testExamples = task<Test>("testExamples") {
description = "Runs examples tests."
group = "verification"

testClassesDirs = sourceSets["testExamples"].output.classesDirs
classpath = sourceSets["testExamples"].runtimeClasspath + sourceSets["examples"].output + sourceSets.main.get().output
shouldRunAfter("compileJava", "compileExamplesJava", "test")
// This will show System.out.println statements
testLogging.showStandardStreams = true
useTestNG()

testLogging {
events("passed")
}
}

fun buildPom(mavenPublication: MavenPublication) {
mavenPublication.pom.withXml {
var dependencyManagementNode = asNode().appendNode("dependencyManagement").appendNode("dependencies").appendNode("dependency")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package software.amazon.cryptography.example.hierarchy;

import java.util.List;
import java.util.stream.Collectors;
import javax.annotation.Nullable;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
import software.amazon.awssdk.services.kms.KmsClient;
import software.amazon.cryptography.keystore.model.AwsKms;
import software.amazon.cryptography.keystore.model.DynamoDBTable;
import software.amazon.cryptography.keystore.model.Storage;
import software.amazon.cryptography.keystoreadmin.KeyStoreAdmin;
import software.amazon.cryptography.keystoreadmin.model.KeyManagementStrategy;
import software.amazon.cryptography.keystoreadmin.model.KeyStoreAdminConfig;
import software.amazon.cryptography.keystoreadmin.model.MutatedBranchKeyItem;

public class AdminProvider {

public static KeyStoreAdmin admin(
String keyStoreTableName,
String logicalKeyStoreName,
@Nullable DynamoDbClient dynamoDbClient
) {
DynamoDBTable table = DynamoDBTable
.builder()
.ddbClient(dynamoDbClient)
.ddbTableName(keyStoreTableName)
.build();
Storage storage = Storage.builder().ddb(table).build();

KeyStoreAdminConfig config = KeyStoreAdminConfig
.builder()
.logicalKeyStoreName(logicalKeyStoreName)
.storage(storage)
.build();

return KeyStoreAdmin.builder().KeyStoreAdminConfig(config).build();
}

public static KeyManagementStrategy strategy(@Nullable KmsClient kmsClient) {
kmsClient = kms(kmsClient);
return KeyManagementStrategy
.builder()
.AwsKmsReEncrypt(AwsKms.builder().kmsClient(kmsClient).build())
.build();
}

@SuppressWarnings("resource")
public static DynamoDbClient dynamoDB(
@Nullable DynamoDbClient dynamoDbClient
) {
if (dynamoDbClient == null) {
dynamoDbClient = DynamoDbClient.create();
}
return dynamoDbClient;
}

@SuppressWarnings("resource")
public static KmsClient kms(@Nullable KmsClient kmsClient) {
if (kmsClient == null) {
kmsClient = KmsClient.create();
}
return kmsClient;
}

public static String mutatedItemsToString(
List<MutatedBranchKeyItem> mutatedItems
) {
return mutatedItems
.stream()
.map(item -> String.format("%s : %s", item.itemType(), item.description())
)
.collect(Collectors.joining(",\n "));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package software.amazon.cryptography.example.hierarchy;

import javax.annotation.Nullable;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
import software.amazon.awssdk.services.kms.KmsClient;
import software.amazon.cryptography.keystore.model.AwsKms;
import software.amazon.cryptography.keystoreadmin.KeyStoreAdmin;
import software.amazon.cryptography.keystoreadmin.model.CreateKeyInput;
import software.amazon.cryptography.keystoreadmin.model.KMSIdentifier;
import software.amazon.cryptography.keystoreadmin.model.KeyManagementStrategy;

/*
The Hierarchical Keyring Example and Searchable Encryption Examples
rely on the existence of a DDB-backed key store with pre-existing
branch key material or beacon key material.

See the "Create KeyStore Table Example" for how to first set up
the DDB Table that will back this KeyStore.

This example demonstrates configuring a KeyStore and then
using a helper method to create a branch key and beacon key
that share the same Id, then return that Id.
We will always create a new beacon key alongside a new branch key,
even if you are not using searchable encryption.

This key creation should occur within your control plane.
*/
public class CreateKeyExample {

public static String CreateKey(
String keyStoreTableName,
String logicalKeyStoreName,
String kmsKeyArn,
@Nullable DynamoDbClient dynamoDbClient,
@Nullable KmsClient kmsClient
) {
if (kmsClient == null) {
kmsClient = KmsClient.create();
}
KeyStoreAdmin admin = AdminProvider.admin(
keyStoreTableName,
logicalKeyStoreName,
dynamoDbClient
);

// 2. Create a new branch key and beacon key in our KeyStore.
// Both the branch key and the beacon key will share an Id.
// This creation is eventually consistent.

final String branchKeyId = admin
.CreateKey(
CreateKeyInput
.builder()
.kmsArn(KMSIdentifier.builder().kmsKeyArn(kmsKeyArn).build())
.strategy(
KeyManagementStrategy
.builder()
.AwsKmsReEncrypt(AwsKms.builder().kmsClient(kmsClient).build())
.build()
)
.build()
)
.branchKeyIdentifier();

return branchKeyId;
}

public static void main(final String[] args) {
if (args.length <= 1) {
throw new IllegalArgumentException(
"To run this example, include the keyStoreTableName, logicalKeyStoreName, and kmsKeyArn in args"
);
}
final String keyStoreTableName = args[0];
final String logicalKeyStoreName = args[1];
final String kmsKeyArn = args[2];
CreateKey(keyStoreTableName, logicalKeyStoreName, kmsKeyArn, null, null);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package software.amazon.cryptography.example.hierarchy;

import java.util.HashMap;
import javax.annotation.Nullable;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
import software.amazon.awssdk.services.kms.KmsClient;
import software.amazon.cryptography.keystoreadmin.KeyStoreAdmin;
import software.amazon.cryptography.keystoreadmin.model.ApplyMutationInput;
import software.amazon.cryptography.keystoreadmin.model.ApplyMutationOutput;
import software.amazon.cryptography.keystoreadmin.model.ApplyMutationResult;
import software.amazon.cryptography.keystoreadmin.model.InitializeMutationInput;
import software.amazon.cryptography.keystoreadmin.model.InitializeMutationOutput;
import software.amazon.cryptography.keystoreadmin.model.KeyManagementStrategy;
import software.amazon.cryptography.keystoreadmin.model.MutationToken;
import software.amazon.cryptography.keystoreadmin.model.Mutations;

public class MutationExample {

public static String End2End(
String keyStoreTableName,
String logicalKeyStoreName,
String kmsKeyArnOriginal,
String kmsKeyArnTerminal,
@Nullable DynamoDbClient dynamoDbClient,
@Nullable KmsClient kmsClient
) {
kmsClient = AdminProvider.kms(kmsClient);
KeyStoreAdmin admin = AdminProvider.admin(
keyStoreTableName,
logicalKeyStoreName,
dynamoDbClient
);
KeyManagementStrategy strategy = AdminProvider.strategy(kmsClient);

String branchKeyId = CreateKeyExample.CreateKey(
keyStoreTableName,
logicalKeyStoreName,
kmsKeyArnOriginal,
dynamoDbClient,
kmsClient
);
System.out.println("BranchKey ID to mutate: " + branchKeyId);
HashMap<String, String> terminalEC = new HashMap<>();
terminalEC.put("Robbie", "is a dog.");
Mutations mutations = Mutations
.builder()
.terminalEncryptionContext(terminalEC)
.terminalKmsArn(kmsKeyArnTerminal)
.build();

InitializeMutationInput initInput = InitializeMutationInput
.builder()
.mutations(mutations)
.branchKeyIdentifier(branchKeyId)
.strategy(strategy)
.build();

InitializeMutationOutput initOutput = admin.InitializeMutation(initInput);

MutationToken token = initOutput.mutationToken();
System.out.println(
"InitLogs: " +
branchKeyId +
" items: \n" +
AdminProvider.mutatedItemsToString(initOutput.mutatedBranchKeyItems())
);
boolean done = false;
int limitLoop = 10;

while (!done) {
ApplyMutationInput applyInput = ApplyMutationInput
.builder()
.mutationToken(token)
.pageSize(98)
.strategy(strategy)
.build();
ApplyMutationOutput applyOutput = admin.ApplyMutation(applyInput);
ApplyMutationResult result = applyOutput.result();

System.out.println(
"ApplyLogs: " +
branchKeyId +
" items: \n" +
AdminProvider.mutatedItemsToString(applyOutput.mutatedBranchKeyItems())
);

if (result.continueMutation() != null) token = result.continueMutation();
if (result.completeMutation() != null) done = true;
if (limitLoop == 0) done = true;

limitLoop--;
}

System.out.println("Done with Mutation: " + branchKeyId);

return branchKeyId;
}

public static void main(final String[] args) {
if (args.length <= 1) {
throw new IllegalArgumentException(
"To run this example, include the keyStoreTableName, logicalKeyStoreName, kmsKeyOriginal, and kmsKeyTerminal in args"
);
}
final String keyStoreTableName = args[0];
final String logicalKeyStoreName = args[1];
final String kmsKeyArnOriginal = args[2];
final String kmsKeyArnTerminal = args[3];
End2End(
keyStoreTableName,
logicalKeyStoreName,
kmsKeyArnOriginal,
kmsKeyArnTerminal,
null,
null
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -103,14 +103,11 @@ public ApplyMutationOutput build() {
"Missing value for required field `mutatedBranchKeyItems`"
);
}
if (
Objects.nonNull(this.mutatedBranchKeyItems()) &&
this.mutatedBranchKeyItems().size() < 1
) {
throw new IllegalArgumentException(
"The size of `mutatedBranchKeyItems` must be greater than or equal to 1"
);
}
// if (Objects.nonNull(this.mutatedBranchKeyItems())) {
// throw new IllegalArgumentException(
// "The size of `mutatedBranchKeyItems` must be greater than or equal to 1"
// );
// }
return new ApplyMutationOutput(this);
}
}
Expand Down
Loading
Loading