Skip to content

Commit 5131ade

Browse files
authored
Add Examples directory/package and add an example class for PartialRsaKeyPair (#19)
* Add Examples directory/package and add an example class for demonstrating use of PartialRsaKeyPair * Make it so examples are run as unit tests
1 parent 05fbeeb commit 5131ade

File tree

5 files changed

+236
-5
lines changed

5 files changed

+236
-5
lines changed

pom.xml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,26 @@
117117
<version>2.22.2</version>
118118
</plugin>
119119

120+
<plugin>
121+
<groupId>org.codehaus.mojo</groupId>
122+
<artifactId>build-helper-maven-plugin</artifactId>
123+
<version>3.3.0</version>
124+
<executions>
125+
<execution>
126+
<id>add-test-source</id>
127+
<phase>generate-test-sources</phase>
128+
<goals>
129+
<goal>add-test-source</goal>
130+
</goals>
131+
<configuration>
132+
<sources>
133+
<source>src/examples/java</source>
134+
</sources>
135+
</configuration>
136+
</execution>
137+
</executions>
138+
</plugin>
139+
120140
<plugin>
121141
<groupId>org.jacoco</groupId>
122142
<artifactId>jacoco-maven-plugin</artifactId>
Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
package software.amazon.encryption.s3.examples;
2+
3+
import software.amazon.awssdk.core.ResponseBytes;
4+
import software.amazon.awssdk.core.sync.RequestBody;
5+
import software.amazon.awssdk.services.s3.S3Client;
6+
import software.amazon.awssdk.services.s3.model.Delete;
7+
import software.amazon.awssdk.services.s3.model.GetObjectResponse;
8+
import software.amazon.awssdk.services.s3.model.ObjectIdentifier;
9+
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
10+
import software.amazon.encryption.s3.S3EncryptionClient;
11+
import software.amazon.encryption.s3.S3EncryptionClientException;
12+
import software.amazon.encryption.s3.materials.PartialRsaKeyPair;
13+
14+
import java.security.KeyPair;
15+
import java.security.KeyPairGenerator;
16+
import java.security.NoSuchAlgorithmException;
17+
import java.util.Set;
18+
import java.util.stream.Collectors;
19+
import java.util.stream.Stream;
20+
21+
import static org.junit.jupiter.api.Assertions.assertEquals;
22+
import static org.junit.jupiter.api.Assertions.fail;
23+
24+
public class PartialKeyPairExample {
25+
26+
private static final String OBJECT_CONTENT = "Hello, world!";
27+
28+
// Use unique object keys for each example
29+
private static final String PUBLIC_AND_PRIVATE_KEY_OBJECT_KEY = "PublicAndPrivateKeyTestObject";
30+
private static final String PUBLIC_KEY_OBJECT_KEY = "PublicKeyTestObject";
31+
private static final String PRIVATE_KEY_OBJECT_KEY = "PrivateKeyTestObject";
32+
33+
private static final Set<ObjectIdentifier> PARTIAL_KEY_PAIR_EXAMPLE_OBJECT_KEYS = Stream
34+
.of(PUBLIC_AND_PRIVATE_KEY_OBJECT_KEY, PUBLIC_KEY_OBJECT_KEY, PRIVATE_KEY_OBJECT_KEY)
35+
.map(k -> ObjectIdentifier.builder().key(k).build())
36+
.collect(Collectors.toSet());
37+
38+
// This example generates a new key. In practice, you would
39+
// retrieve your key from an existing keystore.
40+
private static final KeyPair RSA_KEY_PAIR = retrieveRsaKeyPair();
41+
42+
public static void main(final String[] args) {
43+
final String bucket = args[0];
44+
45+
useBothPublicAndPrivateKey(bucket);
46+
useOnlyPublicKey(bucket);
47+
useOnlyPrivateKey(bucket);
48+
cleanup(bucket);
49+
}
50+
51+
public static void useBothPublicAndPrivateKey(final String bucket) {
52+
// Instantiate the S3 Encryption Client to encrypt and decrypt
53+
// by specifying an RSA wrapping key pair with the rsaKeyPair builder
54+
// parameter.
55+
// This means that the S3 Encryption Client can perform both encrypt and decrypt operations
56+
// as part of the S3 putObject and getObject operations.
57+
S3Client s3Client = S3EncryptionClient.builder()
58+
.rsaKeyPair(RSA_KEY_PAIR)
59+
.build();
60+
61+
// Call putObject to encrypt the object and upload it to S3
62+
s3Client.putObject(PutObjectRequest.builder()
63+
.bucket(bucket)
64+
.key(PUBLIC_AND_PRIVATE_KEY_OBJECT_KEY)
65+
.build(), RequestBody.fromString(OBJECT_CONTENT));
66+
67+
// Call getObject to retrieve and decrypt the object from S3
68+
ResponseBytes<GetObjectResponse> objectResponse = s3Client.getObjectAsBytes(builder -> builder
69+
.bucket(bucket)
70+
.key(PUBLIC_AND_PRIVATE_KEY_OBJECT_KEY));
71+
String output = objectResponse.asUtf8String();
72+
73+
// Verify that the decrypted object matches the original plaintext object
74+
assertEquals(OBJECT_CONTENT, output, "Decrypted response does not match original plaintext!");
75+
76+
// Close the client
77+
s3Client.close();
78+
}
79+
80+
static void useOnlyPublicKey(final String bucket) {
81+
// Instantiate the S3 Encryption client to encrypt by specifying the
82+
// public key from an RSA key pair with the PartialKeyPair object.
83+
// When you specify the public key alone, all GetObject calls will fail
84+
// because the private key is required to decrypt.
85+
S3Client s3Client = S3EncryptionClient.builder()
86+
.rsaKeyPair(new PartialRsaKeyPair(null, RSA_KEY_PAIR.getPublic()))
87+
.build();
88+
89+
// Call putObject to encrypt the object and upload it to S3
90+
s3Client.putObject(PutObjectRequest.builder()
91+
.bucket(bucket)
92+
.key(PUBLIC_KEY_OBJECT_KEY)
93+
.build(), RequestBody.fromString(OBJECT_CONTENT));
94+
95+
// Attempt to call getObject to retrieve and decrypt the object from S3.
96+
try {
97+
s3Client.getObjectAsBytes(builder -> builder
98+
.bucket(bucket)
99+
.key(PUBLIC_KEY_OBJECT_KEY));
100+
fail("Expected exception! No private key provided for decryption.");
101+
} catch (final S3EncryptionClientException exception) {
102+
// This is expected; the s3Client cannot successfully call getObject
103+
// when instantiated with a public key.
104+
}
105+
106+
// Close the client
107+
s3Client.close();
108+
}
109+
110+
static void useOnlyPrivateKey(final String bucket) {
111+
112+
// Instantiate the S3 Encryption client to decrypt by specifying the
113+
// private key from an RSA key pair with the PartialRsaKeyPair object.
114+
// When you specify the private key alone, all PutObject calls will
115+
// fail because the public key is required to encrypt.
116+
S3Client s3ClientPrivateKeyOnly = S3EncryptionClient.builder()
117+
.rsaKeyPair(new PartialRsaKeyPair(RSA_KEY_PAIR.getPrivate(), null))
118+
.build();
119+
120+
// Attempt to call putObject to encrypt the object and upload it to S3
121+
try {
122+
s3ClientPrivateKeyOnly.putObject(PutObjectRequest.builder()
123+
.bucket(bucket)
124+
.key(PRIVATE_KEY_OBJECT_KEY)
125+
.build(), RequestBody.fromString(OBJECT_CONTENT));
126+
fail("Expected exception! No public key provided for encryption.");
127+
} catch (final S3EncryptionClientException exception) {
128+
// This is expected; the s3Client cannot successfully call putObject
129+
// when instantiated with a private key.
130+
}
131+
132+
// Instantiate a new S3 Encryption client with a public key in order
133+
// to successfully call PutObject so that the client which only has
134+
// a private key can call GetObject on a valid S3 Object.
135+
S3Client s3ClientPublicKeyOnly = S3EncryptionClient.builder()
136+
.rsaKeyPair(new PartialRsaKeyPair(null, RSA_KEY_PAIR.getPublic()))
137+
.build();
138+
139+
// Call putObject to encrypt the object and upload it to S3
140+
s3ClientPublicKeyOnly.putObject(PutObjectRequest.builder()
141+
.bucket(bucket)
142+
.key(PRIVATE_KEY_OBJECT_KEY)
143+
.build(), RequestBody.fromString(OBJECT_CONTENT));
144+
145+
// Call getObject to retrieve and decrypt the object from S3
146+
ResponseBytes<GetObjectResponse> objectResponse = s3ClientPrivateKeyOnly.getObjectAsBytes(builder -> builder
147+
.bucket(bucket)
148+
.key(PRIVATE_KEY_OBJECT_KEY));
149+
String output = objectResponse.asUtf8String();
150+
151+
// Verify that the decrypted object matches the original plaintext object
152+
assertEquals(OBJECT_CONTENT, output, "The decrypted response does not match the original plaintext!");
153+
154+
// Close the clients
155+
s3ClientPublicKeyOnly.close();
156+
s3ClientPrivateKeyOnly.close();
157+
}
158+
159+
public static void cleanup(final String bucket) {
160+
// The S3 Encryption client is not required when deleting encrypted
161+
// objects, use the S3 Client.
162+
final S3Client s3Client = S3Client.builder().build();
163+
final Delete delete = Delete.builder()
164+
.objects(PARTIAL_KEY_PAIR_EXAMPLE_OBJECT_KEYS)
165+
.build();
166+
s3Client.deleteObjects(builder -> builder
167+
.bucket(bucket)
168+
.delete(delete)
169+
.build());
170+
171+
// Close the client
172+
s3Client.close();
173+
}
174+
175+
private static KeyPair retrieveRsaKeyPair() {
176+
try {
177+
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
178+
keyPairGen.initialize(2048);
179+
return keyPairGen.generateKeyPair();
180+
} catch (final NoSuchAlgorithmException exception) {
181+
// This should be impossible, wrap with a runtime exception
182+
throw new RuntimeException(exception);
183+
}
184+
}
185+
186+
}

src/test/java/software/amazon/encryption/s3/S3EncryptionClientTest.java

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,16 @@
1717

1818
import static org.junit.jupiter.api.Assertions.*;
1919
import static software.amazon.encryption.s3.S3EncryptionClient.withAdditionalEncryptionContext;
20+
import static software.amazon.encryption.s3.S3EncryptionClientTestResources.BUCKET;
21+
import static software.amazon.encryption.s3.S3EncryptionClientTestResources.KMS_KEY_ALIAS;
22+
import static software.amazon.encryption.s3.S3EncryptionClientTestResources.KMS_KEY_ID;
2023

2124
/**
2225
* This class is an integration test for verifying behavior of the V3 client
2326
* under various scenarios.
2427
*/
2528
public class S3EncryptionClientTest {
2629

27-
private static final String BUCKET = System.getenv("AWS_S3EC_TEST_BUCKET");
28-
private static final String KMS_KEY_ID = System.getenv("AWS_S3EC_TEST_KMS_KEY_ID");
29-
// This alias must point to the same key as KMS_KEY_ID
30-
private static final String KMS_KEY_ALIAS = System.getenv("AWS_S3EC_TEST_KMS_KEY_ALIAS");
31-
3230
private static SecretKey AES_KEY;
3331
private static KeyPair RSA_KEY_PAIR;
3432

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package software.amazon.encryption.s3;
2+
3+
/**
4+
* Determines which AWS resources to use while running tests.
5+
*/
6+
public class S3EncryptionClientTestResources {
7+
8+
public static final String BUCKET = System.getenv("AWS_S3EC_TEST_BUCKET");
9+
public static final String KMS_KEY_ID = System.getenv("AWS_S3EC_TEST_KMS_KEY_ID");
10+
// This alias must point to the same key as KMS_KEY_ID
11+
public static final String KMS_KEY_ALIAS = System.getenv("AWS_S3EC_TEST_KMS_KEY_ALIAS");
12+
13+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package software.amazon.encryption.s3.examples;
2+
3+
import org.junit.jupiter.api.Test;
4+
import software.amazon.encryption.s3.S3EncryptionClientTestResources;
5+
6+
public class PartialKeyPairExampleTest {
7+
8+
@Test
9+
public void testPartialKeyPairExamples() {
10+
final String bucket = S3EncryptionClientTestResources.BUCKET;
11+
12+
PartialKeyPairExample.main(new String[]{bucket});
13+
}
14+
}

0 commit comments

Comments
 (0)