22
33import java .io .ByteArrayInputStream ;
44import java .io .IOException ;
5- import java .nio .charset .StandardCharsets ;
65import java .security .InvalidAlgorithmParameterException ;
76import java .security .InvalidKeyException ;
87import java .security .NoSuchAlgorithmException ;
9- import java .security .SecureRandom ;
108import java .util .Base64 ;
119import java .util .Collections ;
1210import java .util .HashMap ;
1311import java .util .List ;
1412import java .util .Map ;
15- import java .util .Map .Entry ;
1613import javax .crypto .BadPaddingException ;
1714import javax .crypto .Cipher ;
1815import javax .crypto .IllegalBlockSizeException ;
2825import software .amazon .awssdk .http .AbortableInputStream ;
2926import software .amazon .awssdk .protocols .jsoncore .JsonNode ;
3027import software .amazon .awssdk .protocols .jsoncore .JsonNodeParser ;
31- import software .amazon .awssdk .protocols .jsoncore .JsonWriter ;
32- import software .amazon .awssdk .protocols .jsoncore .JsonWriter .JsonGenerationException ;
3328import software .amazon .awssdk .services .s3 .S3Client ;
3429import software .amazon .awssdk .services .s3 .model .GetObjectRequest ;
3530import software .amazon .awssdk .services .s3 .model .GetObjectResponse ;
36- import software .amazon .awssdk .services .s3 .model .InvalidObjectStateException ;
37- import software .amazon .awssdk .services .s3 .model .NoSuchKeyException ;
3831import software .amazon .awssdk .services .s3 .model .PutObjectRequest ;
3932import software .amazon .awssdk .services .s3 .model .PutObjectResponse ;
40- import software .amazon .awssdk .services .s3 .model .S3Exception ;
4133import software .amazon .awssdk .utils .IoUtils ;
4234import software .amazon .encryption .s3 .algorithms .AlgorithmSuite ;
43- import software .amazon .encryption .s3 .materials . DecryptionMaterials ;
35+ import software .amazon .encryption .s3 .internal . PutEncryptedObjectPipeline ;
4436import software .amazon .encryption .s3 .materials .DecryptMaterialsRequest ;
45- import software .amazon .encryption .s3 .materials .EncryptionMaterialsRequest ;
37+ import software .amazon .encryption .s3 .materials .DecryptionMaterials ;
4638import software .amazon .encryption .s3 .materials .EncryptedDataKey ;
47- import software .amazon .encryption .s3 .materials .EncryptionMaterials ;
4839import software .amazon .encryption .s3 .materials .MaterialsManager ;
4940
5041public class S3EncryptionClient implements S3Client {
@@ -59,78 +50,25 @@ public S3EncryptionClient(S3Client client, MaterialsManager materialsManager) {
5950
6051 @ Override
6152 public PutObjectResponse putObject (PutObjectRequest putObjectRequest , RequestBody requestBody )
62- throws AwsServiceException , SdkClientException , S3Exception {
63-
64- // TODO: This is proof-of-concept code and needs to be refactored
65-
66- // Get content encryption key
67- EncryptionMaterials materials = _materialsManager .getEncryptionMaterials (EncryptionMaterialsRequest .builder ()
68- .build ());
69- SecretKey contentKey = materials .dataKey ();
70- // Encrypt content
71- byte [] iv = new byte [12 ]; // default GCM IV length
72- new SecureRandom ().nextBytes (iv );
73-
74- final String contentEncryptionAlgorithm = "AES/GCM/NoPadding" ;
75- final Cipher cipher ;
76- try {
77- cipher = Cipher .getInstance (contentEncryptionAlgorithm );
78- cipher .init (Cipher .ENCRYPT_MODE , contentKey , new GCMParameterSpec (128 , iv ));
79- } catch (NoSuchAlgorithmException
80- | NoSuchPaddingException
81- | InvalidAlgorithmParameterException
82- | InvalidKeyException e ) {
83- throw new RuntimeException (e );
84- }
53+ throws AwsServiceException , SdkClientException {
8554
86- byte [] ciphertext ;
87- try {
88- byte [] input = IoUtils .toByteArray (requestBody .contentStreamProvider ().newStream ());
89- ciphertext = cipher .doFinal (input );
90- } catch (IOException e ) {
91- throw new RuntimeException (e );
92- } catch (IllegalBlockSizeException e ) {
93- throw new RuntimeException (e );
94- } catch (BadPaddingException e ) {
95- throw new RuntimeException (e );
96- }
97-
98- // Save content metadata into request
99- Base64 .Encoder encoder = Base64 .getEncoder ();
100- Map <String ,String > metadata = new HashMap <>(putObjectRequest .metadata ());
101- EncryptedDataKey edk = materials .encryptedDataKeys ().get (0 );
102- metadata .put ("x-amz-key-v2" , encoder .encodeToString (edk .ciphertext ()));
103- metadata .put ("x-amz-iv" , encoder .encodeToString (iv ));
104- metadata .put ("x-amz-matdesc" , /* TODO: JSON encoded */ "{}" );
105- metadata .put ("x-amz-cek-alg" , contentEncryptionAlgorithm );
106- metadata .put ("x-amz-tag-len" , /* TODO: take from algo suite */ "128" );
107- metadata .put ("x-amz-wrap-alg" , edk .keyProviderId ());
108-
109- try (JsonWriter jsonWriter = JsonWriter .create ()) {
110- jsonWriter .writeStartObject ();
111- for (Entry <String ,String > entry : materials .encryptionContext ().entrySet ()) {
112- jsonWriter .writeFieldName (entry .getKey ()).writeValue (entry .getValue ());
113- }
114- jsonWriter .writeEndObject ();
115-
116- String jsonEncryptionContext = new String (jsonWriter .getBytes (), StandardCharsets .UTF_8 );
117- metadata .put ("x-amz-matdesc" , jsonEncryptionContext );
118- } catch (JsonGenerationException e ) {
119- throw new RuntimeException (e );
120- }
121-
122- putObjectRequest = putObjectRequest .toBuilder ().metadata (metadata ).build ();
55+ PutEncryptedObjectPipeline pipeline = PutEncryptedObjectPipeline .builder ()
56+ .s3Client (_wrappedClient )
57+ .materialsManager (_materialsManager )
58+ .build ();
12359
124- return _wrappedClient .putObject (putObjectRequest , RequestBody . fromBytes ( ciphertext ) );
60+ return pipeline .putObject (putObjectRequest , requestBody );
12561 }
12662
12763 @ Override
128- public <T > T getObject (GetObjectRequest getObjectRequest , ResponseTransformer <GetObjectResponse , T > responseTransformer )
129- throws NoSuchKeyException , InvalidObjectStateException , AwsServiceException , SdkClientException , S3Exception {
64+ public <T > T getObject (GetObjectRequest getObjectRequest ,
65+ ResponseTransformer <GetObjectResponse , T > responseTransformer )
66+ throws AwsServiceException , SdkClientException {
13067
13168 // TODO: This is proof-of-concept code and needs to be refactored
13269
133- ResponseInputStream <GetObjectResponse > objectStream = _wrappedClient .getObject (getObjectRequest );
70+ ResponseInputStream <GetObjectResponse > objectStream = _wrappedClient .getObject (
71+ getObjectRequest );
13472 byte [] output ;
13573 try {
13674 output = IoUtils .toByteArray (objectStream );
@@ -169,11 +107,12 @@ public <T> T getObject(GetObjectRequest getObjectRequest, ResponseTransformer<Ge
169107 final String contentEncryptionAlgorithm = metadata .get ("x-amz-cek-alg" );
170108 AlgorithmSuite algorithmSuite = null ;
171109 if (contentEncryptionAlgorithm .equals ("AES/GCM/NoPadding" )) {
172- algorithmSuite = AlgorithmSuite .ALG_AES_256_GCM_IV12_TAG16_NO_KDF ;;
110+ algorithmSuite = AlgorithmSuite .ALG_AES_256_GCM_IV12_TAG16_NO_KDF ;
173111 }
174112
175113 if (algorithmSuite == null ) {
176- throw new RuntimeException ("Unknown content encryption algorithm: " + contentEncryptionAlgorithm );
114+ throw new RuntimeException (
115+ "Unknown content encryption algorithm: " + contentEncryptionAlgorithm );
177116 }
178117
179118 DecryptMaterialsRequest request = DecryptMaterialsRequest .builder ()
0 commit comments