2323import java .security .NoSuchAlgorithmException ;
2424import java .security .PrivateKey ;
2525import java .security .PublicKey ;
26- import java .security .SecureRandom ;
2726
2827import static org .junit .jupiter .api .Assertions .assertEquals ;
2928import static org .junit .jupiter .api .Assertions .assertTrue ;
@@ -61,6 +60,7 @@ public static void main(final String[] args) throws NoSuchAlgorithmException {
6160 final String bucket = args [0 ];
6261 simpleAesKeyringReEncryptInstructionFile (bucket );
6362 simpleRsaKeyringReEncryptInstructionFile (bucket );
63+ simpleRsaKeyringReEncryptInstructionFileWithCustomSuffix (bucket );
6464 }
6565
6666 /**
@@ -81,7 +81,6 @@ public static void simpleAesKeyringReEncryptInstructionFile(final String bucket)
8181 // Create the original AES keyring with materials description
8282 AesKeyring oldKeyring = AesKeyring .builder ()
8383 .wrappingKey (originalAesKey )
84- .secureRandom (new SecureRandom ())
8584 .materialsDescription (MaterialsDescription .builder ()
8685 .put ("version" , "1.0" )
8786 .put ("rotated" , "no" )
@@ -113,7 +112,6 @@ public static void simpleAesKeyringReEncryptInstructionFile(final String bucket)
113112 // Create a new keyring with the new AES key and updated materials description
114113 AesKeyring newKeyring = AesKeyring .builder ()
115114 .wrappingKey (newAesKey )
116- .secureRandom (new SecureRandom ())
117115 .materialsDescription (MaterialsDescription .builder ()
118116 .put ("version" , "2.0" )
119117 .put ("rotated" , "yes" )
@@ -167,8 +165,8 @@ public static void simpleAesKeyringReEncryptInstructionFile(final String bucket)
167165 }
168166
169167 /**
170- * This example demonstrates generating a custom instruction file to enable access to encrypted object by a third party .
171- * This enables secure sharing of encrypted objects without sharing private keys .
168+ * This example demonstrates re-encrypting the encrypted data key in an instruction file with a new RSA wrapping key .
169+ * The other cryptographic parameters in the instruction file such as the IV and wrapping algorithm remain unchanged .
172170 *
173171 * @param bucket The name of the Amazon S3 bucket to perform operations on.
174172 * @throws NoSuchAlgorithmException if RSA algorithm is not available
@@ -178,6 +176,123 @@ public static void simpleRsaKeyringReEncryptInstructionFile(final String bucket)
178176 final String objectKey = appendTestSuffix ("rsa-re-encrypt-instruction-file-test" );
179177 final String input = "Testing re-encryption of instruction file with RSA Keyring" ;
180178
179+ // Generate the original RSA key pair for initial encryption
180+ KeyPair originalRsaKeyPair = generateRsaKeyPair ();
181+ PublicKey originalPublicKey = originalRsaKeyPair .getPublic ();
182+ PrivateKey originalPrivateKey = originalRsaKeyPair .getPrivate ();
183+
184+ // Create a partial RSA key pair for the original keyring
185+ PartialRsaKeyPair originalPartialRsaKeyPair = PartialRsaKeyPair .builder ()
186+ .publicKey (originalPublicKey )
187+ .privateKey (originalPrivateKey )
188+ .build ();
189+
190+ // Create the original RSA keyring with materials description
191+ RsaKeyring originalKeyring = RsaKeyring .builder ()
192+ .wrappingKeyPair (originalPartialRsaKeyPair )
193+ .materialsDescription (MaterialsDescription .builder ()
194+ .put ("version" , "1.0" )
195+ .put ("rotated" , "no" )
196+ .build ())
197+ .build ();
198+
199+ // Create a default S3 client for instruction file operations
200+ S3Client wrappedClient = S3Client .create ();
201+
202+ // Create the S3 Encryption Client with instruction file support enabled
203+ // The client can perform both putObject and getObject operations using RSA keyring
204+ S3EncryptionClient originalClient = S3EncryptionClient .builder ()
205+ .keyring (originalKeyring )
206+ .instructionFileConfig (InstructionFileConfig .builder ()
207+ .instructionFileClient (wrappedClient )
208+ .enableInstructionFilePutObject (true )
209+ .build ())
210+ .build ();
211+
212+ // Upload both the encrypted object and instruction file to the specified bucket in S3
213+ originalClient .putObject (builder -> builder
214+ .bucket (bucket )
215+ .key (objectKey )
216+ .build (), RequestBody .fromString (input ));
217+
218+ // Generate a new RSA key pair for the new RSA keyring
219+ KeyPair newKeyPair = generateRsaKeyPair ();
220+ PublicKey newPublicKey = newKeyPair .getPublic ();
221+ PrivateKey newPrivateKey = newKeyPair .getPrivate ();
222+
223+ // Create a partial RSA key pair for the new RSA keyring
224+ PartialRsaKeyPair newPartialRsaKeyPair = PartialRsaKeyPair .builder ()
225+ .publicKey (newPublicKey )
226+ .privateKey (newPrivateKey )
227+ .build ();
228+
229+ // Create the new RSA keyring with updated materials description
230+ RsaKeyring newKeyring = RsaKeyring .builder ()
231+ .wrappingKeyPair (newPartialRsaKeyPair )
232+ .materialsDescription (MaterialsDescription .builder ()
233+ .put ("version" , "2.0" )
234+ .put ("rotated" , "yes" )
235+ .build ())
236+ .build ();
237+
238+ // Create the re-encryption of instruction file request to re-encrypt the encrypted data key with the new wrapping key
239+ // This updates the instruction file without touching the encrypted object
240+ ReEncryptInstructionFileRequest reEncryptInstructionFileRequest = ReEncryptInstructionFileRequest .builder ()
241+ .bucket (bucket )
242+ .key (objectKey )
243+ .newKeyring (newKeyring )
244+ .build ();
245+
246+ // Perform the re-encryption of the instruction file
247+ ReEncryptInstructionFileResponse reEncryptInstructionFileResponse = originalClient .reEncryptInstructionFile (reEncryptInstructionFileRequest );
248+
249+ // Verify that the original client can no longer decrypt the object
250+ // This proves that the instruction file has been successfully re-encrypted
251+ try {
252+ originalClient .getObjectAsBytes (builder -> builder
253+ .bucket (bucket )
254+ .key (objectKey )
255+ .build ());
256+ throw new RuntimeException ("Original client should not be able to decrypt the object in S3 post re-encryption of instruction file!" );
257+ } catch (S3EncryptionClientException e ) {
258+ assertTrue (e .getMessage ().contains ("Unable to RSA-OAEP-SHA1 unwrap" ));
259+ }
260+
261+ // Create a new client with the rotated AES key
262+ S3EncryptionClient newClient = S3EncryptionClient .builder ()
263+ .keyring (newKeyring )
264+ .instructionFileConfig (InstructionFileConfig .builder ()
265+ .instructionFileClient (wrappedClient )
266+ .enableInstructionFilePutObject (true )
267+ .build ())
268+ .build ();
269+
270+ // Verify that the new client can successfully decrypt the object
271+ // This proves that the instruction file has been successfully re-encrypted
272+ ResponseBytes <GetObjectResponse > decryptedObject = newClient .getObjectAsBytes (builder -> builder
273+ .bucket (bucket )
274+ .key (objectKey )
275+ .build ());
276+
277+ // Assert that the decrypted object's content matches the original input
278+ assertEquals (input , decryptedObject .asUtf8String ());
279+
280+ // Call deleteObject to delete the object from given S3 Bucket
281+ deleteObject (bucket , objectKey , originalClient );
282+ }
283+
284+ /**
285+ * This example demonstrates generating a custom instruction file to enable access to encrypted object by a third party.
286+ * This enables secure sharing of encrypted objects without sharing private keys.
287+ *
288+ * @param bucket The name of the Amazon S3 bucket to perform operations on.
289+ * @throws NoSuchAlgorithmException if RSA algorithm is not available
290+ */
291+ public static void simpleRsaKeyringReEncryptInstructionFileWithCustomSuffix (final String bucket ) throws NoSuchAlgorithmException {
292+ // Set up the S3 object key and content to be encrypted
293+ final String objectKey = appendTestSuffix ("rsa-re-encrypt-instruction-file-test-with-custom-suffix" );
294+ final String input = "Testing re-encryption of instruction file with RSA Keyring" ;
295+
181296 // Generate RSA key pair for the original client
182297 KeyPair clientRsaKeyPair = generateRsaKeyPair ();
183298 PublicKey clientPublicKey = clientRsaKeyPair .getPublic ();
@@ -192,7 +307,6 @@ public static void simpleRsaKeyringReEncryptInstructionFile(final String bucket)
192307 // Create the client's RSA keyring with materials description
193308 RsaKeyring clientKeyring = RsaKeyring .builder ()
194309 .wrappingKeyPair (clientPartialRsaKeyPair )
195- .secureRandom (new SecureRandom ())
196310 .materialsDescription (MaterialsDescription .builder ()
197311 .put ("isOwner" , "yes" )
198312 .put ("access-level" , "admin" )
@@ -232,7 +346,6 @@ public static void simpleRsaKeyringReEncryptInstructionFile(final String bucket)
232346 // Create the third party's RSA keyring with updated materials description
233347 RsaKeyring thirdPartyKeyring = RsaKeyring .builder ()
234348 .wrappingKeyPair (thirdPartyPartialRsaKeyPair )
235- .secureRandom (new SecureRandom ())
236349 .materialsDescription (MaterialsDescription .builder ()
237350 .put ("isOwner" , "no" )
238351 .put ("access-level" , "user" )
@@ -255,7 +368,6 @@ public static void simpleRsaKeyringReEncryptInstructionFile(final String bucket)
255368 // Create the third party's S3 Encryption Client
256369 S3EncryptionClient thirdPartyClient = S3EncryptionClient .builder ()
257370 .keyring (thirdPartyKeyring )
258- .secureRandom (new SecureRandom ())
259371 .instructionFileConfig (InstructionFileConfig .builder ()
260372 .instructionFileClient (wrappedClient )
261373 .enableInstructionFilePutObject (true )
0 commit comments