package com.emc.atmos.api.test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import java.io.FileNotFoundException; import java.io.InputStream; import java.security.KeyPair; import java.security.KeyStore; import java.security.MessageDigest; import java.security.Provider; import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPublicKey; import java.util.ArrayList; import java.util.Collections; import java.util.Enumeration; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Random; import java.util.Set; import org.junit.After; import org.junit.Assert; import org.junit.Assume; import org.junit.Before; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.emc.atmos.api.AtmosConfig; import com.emc.atmos.api.ObjectId; import com.emc.atmos.api.ObjectIdentifier; import com.emc.atmos.api.ObjectPath; import com.emc.atmos.api.Range; import com.emc.atmos.api.bean.CreateObjectResponse; import com.emc.atmos.api.bean.Metadata; import com.emc.atmos.api.bean.ReadObjectResponse; import com.emc.atmos.api.encryption.AtmosEncryptionClient; import com.emc.atmos.api.encryption.CompressionConfig; import com.emc.atmos.api.encryption.EncryptionConfig; import com.emc.atmos.api.jersey.AtmosApiClient; import com.emc.atmos.api.request.CreateObjectRequest; import com.emc.atmos.api.request.ReadObjectRequest; import com.emc.atmos.api.request.UpdateObjectRequest; import com.emc.atmos.util.AtmosClientFactory; import com.emc.atmos.util.RandomInputStream; import com.emc.vipr.transform.TransformConstants; import com.emc.vipr.transform.TransformConstants.CompressionMode; import com.emc.vipr.transform.encryption.DoesNotNeedRekeyException; import com.emc.vipr.transform.encryption.KeyUtils; import com.emc.vipr.transform.util.CountingInputStream; public class AtmosEncryptionClientTest { private static final Logger logger = LoggerFactory.getLogger( AtmosEncryptionClientTest.class); private Properties keyprops; private KeyPair masterKey; private KeyPair oldKey; private KeyStore keystore; private String keystorePassword = "viprviprvipr"; private String keyAlias = "masterkey"; private String oldKeyAlias = "oldkey"; private String keystoreFile = "keystore.jks"; protected Provider provider; protected List<ObjectIdentifier> cleanup = Collections.synchronizedList( new ArrayList<ObjectIdentifier>() ); private AtmosConfig config; private AtmosApiClient api; @Before public void setUp() throws Exception { // Load some keys for manual mode keyprops = new Properties(); keyprops.load(this.getClass().getClassLoader() .getResourceAsStream("keys.properties")); masterKey = KeyUtils.rsaKeyPairFromBase64( keyprops.getProperty("masterkey.public"), keyprops.getProperty("masterkey.private")); logger.debug("Master key sizes: public: {} private: {}", ((RSAPublicKey)masterKey.getPublic()).getModulus().bitLength(), ((RSAPrivateKey)masterKey.getPrivate()).getModulus().bitLength()); oldKey = KeyUtils.rsaKeyPairFromBase64( keyprops.getProperty("oldkey.public"), keyprops.getProperty("oldkey.private")); logger.debug("Old key sizes: public: {} private: {}", ((RSAPublicKey)oldKey.getPublic()).getModulus().bitLength(), ((RSAPrivateKey)oldKey.getPrivate()).getModulus().bitLength()); // Init keystore for keystore mode keystore = KeyStore.getInstance("jks"); InputStream in = this.getClass().getClassLoader().getResourceAsStream(keystoreFile); if(in == null) { throw new FileNotFoundException(keystoreFile); } keystore.load(in, keystorePassword.toCharArray()); logger.debug("Keystore Loaded"); for(Enumeration<String> aliases = keystore.aliases(); aliases.hasMoreElements();) { logger.debug("Found key: {}", aliases.nextElement()); } // Initialize the AtmosClient config = AtmosClientFactory.getAtmosConfig(); Assume.assumeTrue("Could not load Atmos configuration", config != null); config.setDisableSslValidation( false ); config.setEnableExpect100Continue( false ); config.setEnableRetry( false ); api = new AtmosApiClient( config ); } @After public void tearDown() { for (final ObjectIdentifier cleanItem : cleanup) { try { api.delete(cleanItem); } catch (Throwable t) { logger.info("Failed to delete " + cleanItem + ": " + t.getMessage()); } } } private AtmosEncryptionClient getBasicEncryptionClient() throws Exception { Set<KeyPair> decryptionKeys = new HashSet<KeyPair>(); decryptionKeys.add(oldKey); EncryptionConfig ec = new EncryptionConfig(masterKey, decryptionKeys, provider, 128); AtmosEncryptionClient eclient = new AtmosEncryptionClient(api, ec, null); return eclient; } private AtmosEncryptionClient getBasicEncryptionClient(KeyPair masterKey) throws Exception { Set<KeyPair> decryptionKeys = new HashSet<KeyPair>(); EncryptionConfig ec = new EncryptionConfig(masterKey, decryptionKeys, provider, 128); AtmosEncryptionClient eclient = new AtmosEncryptionClient(api, ec, null); return eclient; } private AtmosEncryptionClient getBasicEncryptionClientWithCompression() throws Exception { Set<KeyPair> decryptionKeys = new HashSet<KeyPair>(); decryptionKeys.add(oldKey); EncryptionConfig ec = new EncryptionConfig(masterKey, decryptionKeys, provider, 128); CompressionConfig cc = new CompressionConfig(CompressionMode.Deflate, 5); AtmosEncryptionClient eclient = new AtmosEncryptionClient(api, ec, cc); return eclient; } private AtmosEncryptionClient getCompressionClient() throws Exception { CompressionConfig cc = new CompressionConfig(CompressionMode.Deflate, 5); AtmosEncryptionClient eclient = new AtmosEncryptionClient(api, null, cc); return eclient; } private AtmosEncryptionClient getKeystoreEncryptionClient() throws Exception { return getKeystoreEncryptionClient(keyAlias); } private AtmosEncryptionClient getKeystoreEncryptionClient(String masterKeyAlias) throws Exception { EncryptionConfig ec = new EncryptionConfig(keystore, keystorePassword.toCharArray(), masterKeyAlias, provider, 128); AtmosEncryptionClient eclient = new AtmosEncryptionClient(api, ec, null); return eclient; } // Test creating an encrypted object with basic keys @Test public void testCreateEncryptBasic() throws Exception { AtmosEncryptionClient eclient = getBasicEncryptionClient(); String content = "Hello World!"; ObjectId id = eclient.createObject(content, "text/plain"); cleanup.add(id); // Read back and test ReadObjectRequest ror = new ReadObjectRequest(); ror.setIdentifier(id); ReadObjectResponse<String> resp = eclient.readObject(ror, String.class); assertEquals("Content differs", content, resp.getObject()); assertEquals("unencrypted size incorrect", "12", resp.getMetadata().getMetadata().get(TransformConstants.META_ENCRYPTION_UNENC_SIZE).getValue()); assertEquals("encrypted size incorrect", "16", resp.getMetadata().getMetadata().get("size").getValue()); assertEquals("unencrypted sha1 incorrect", "2ef7bde608ce5404e97d5f042f95f89f1c232871", resp.getMetadata().getMetadata().get(TransformConstants.META_ENCRYPTION_UNENC_SHA1).getValue()); assertEquals("master key ID incorrect", KeyUtils.getRsaPublicKeyFingerprint((RSAPublicKey) masterKey.getPublic(), provider), resp.getMetadata().getMetadata().get(TransformConstants.META_ENCRYPTION_KEY_ID).getValue()); assertNotNull("IV null", resp.getMetadata().getMetadata().get(TransformConstants.META_ENCRYPTION_IV).getValue()); assertNotNull("Object key", resp.getMetadata().getMetadata().get(TransformConstants.META_ENCRYPTION_OBJECT_KEY).getValue()); } // Test creating an encrypted object with keystore keys @Test public void testCreateEncryptKeystore() throws Exception { AtmosEncryptionClient eclient = getKeystoreEncryptionClient(); String content = "Hello World!"; ObjectId id = eclient.createObject(content, "text/plain"); cleanup.add(id); // Read back and test ReadObjectRequest ror = new ReadObjectRequest(); ror.setIdentifier(id); ReadObjectResponse<String> resp = eclient.readObject(ror, String.class); assertEquals("Content differs", content, resp.getObject()); assertEquals("unencrypted size incorrect", "12", resp.getMetadata().getMetadata().get(TransformConstants.META_ENCRYPTION_UNENC_SIZE).getValue()); assertEquals("encrypted size incorrect", "16", resp.getMetadata().getMetadata().get("size").getValue()); assertEquals("unencrypted sha1 incorrect", "2ef7bde608ce5404e97d5f042f95f89f1c232871", resp.getMetadata().getMetadata().get(TransformConstants.META_ENCRYPTION_UNENC_SHA1).getValue()); assertEquals("master key ID incorrect", KeyUtils.getRsaPublicKeyFingerprint((RSAPublicKey) keystore.getCertificate(keyAlias).getPublicKey(), provider), resp.getMetadata().getMetadata().get(TransformConstants.META_ENCRYPTION_KEY_ID).getValue()); assertNotNull("IV null", resp.getMetadata().getMetadata().get(TransformConstants.META_ENCRYPTION_IV).getValue()); assertNotNull("Object key", resp.getMetadata().getMetadata().get(TransformConstants.META_ENCRYPTION_OBJECT_KEY).getValue()); } // Test with namespace @Test public void testCreateEncryptOnPath() throws Exception { AtmosEncryptionClient eclient = getBasicEncryptionClient(); String content = "Hello World!"; ObjectId id = eclient.createObject(new ObjectPath("/enctest/" + rand8char()), content, "text/plain"); cleanup.add(id); // Read back and test ReadObjectRequest ror = new ReadObjectRequest(); ror.setIdentifier(id); ReadObjectResponse<String> resp = eclient.readObject(ror, String.class); assertEquals("Content differs", content, resp.getObject()); assertEquals("unencrypted size incorrect", "12", resp.getMetadata().getMetadata().get(TransformConstants.META_ENCRYPTION_UNENC_SIZE).getValue()); assertEquals("encrypted size incorrect", "16", resp.getMetadata().getMetadata().get("size").getValue()); assertEquals("unencrypted sha1 incorrect", "2ef7bde608ce5404e97d5f042f95f89f1c232871", resp.getMetadata().getMetadata().get(TransformConstants.META_ENCRYPTION_UNENC_SHA1).getValue()); assertEquals("master key ID incorrect", KeyUtils.getRsaPublicKeyFingerprint((RSAPublicKey) masterKey.getPublic(), provider), resp.getMetadata().getMetadata().get(TransformConstants.META_ENCRYPTION_KEY_ID).getValue()); assertNotNull("IV null", resp.getMetadata().getMetadata().get(TransformConstants.META_ENCRYPTION_IV).getValue()); assertNotNull("Object key", resp.getMetadata().getMetadata().get(TransformConstants.META_ENCRYPTION_OBJECT_KEY).getValue()); } // Test a stream > 4MB. @Test public void testCreateEncryptStream() throws Exception { AtmosEncryptionClient eclient = getBasicEncryptionClient(); int size = 5*1024*1024+13; RandomInputStream rs = new RandomInputStream(size); ObjectId id = eclient.createObject(rs, "text/plain"); cleanup.add(id); // Read back and test ReadObjectRequest ror = new ReadObjectRequest(); ror.setIdentifier(id); ReadObjectResponse<byte[]> resp = eclient.readObject(ror, byte[].class); // Make sure the checksum matches MessageDigest sha1 = MessageDigest.getInstance("SHA-1"); byte[] sha1Digest = sha1.digest(resp.getObject()); // Hex Encode it String sha1hex = KeyUtils.toHexPadded(sha1Digest); assertNotNull("Missing SHA1 meta", resp.getMetadata().getMetadata().get( TransformConstants.META_ENCRYPTION_UNENC_SHA1)); assertEquals("SHA1 incorrect", sha1hex, resp.getMetadata().getMetadata().get( TransformConstants.META_ENCRYPTION_UNENC_SHA1).getValue()); assertEquals("Stream length incorrect", size, Integer.parseInt( resp.getMetadata().getMetadata().get(TransformConstants.META_ENCRYPTION_UNENC_SIZE).getValue())); } // Test with smaller (odd) chunks @Test public void testCreateEncryptStreamSmallChunks() throws Exception { AtmosEncryptionClient eclient = getBasicEncryptionClient(); eclient.setBufferSize(999999); // Get some data to encrypt. InputStream classin = this.getClass().getClassLoader() .getResourceAsStream("uncompressed.txt"); ObjectId id = eclient.createObject(classin, "text/plain"); cleanup.add(id); classin.close(); // Read back and test ReadObjectRequest ror = new ReadObjectRequest(); ror.setIdentifier(id); ReadObjectResponse<byte[]> resp = eclient.readObject(ror, byte[].class); Map<String, Metadata> objectData = resp.getMetadata().getMetadata(); assertEquals("Uncompressed digest incorrect", "027e997e6b1dfc97b93eb28dc9a6804096d85873", objectData.get(TransformConstants.META_ENCRYPTION_UNENC_SHA1).getValue()); assertEquals("Uncompressed size incorrect", 2516125, Long.parseLong(objectData .get(TransformConstants.META_ENCRYPTION_UNENC_SIZE).getValue())); assertNotNull("Missing IV", objectData.get(TransformConstants.META_ENCRYPTION_IV).getValue()); assertEquals("Incorrect master encryption key ID", KeyUtils.getRsaPublicKeyFingerprint((RSAPublicKey) masterKey .getPublic(), provider), objectData.get(TransformConstants.META_ENCRYPTION_KEY_ID).getValue()); assertNotNull("Missing object key", objectData.get(TransformConstants.META_ENCRYPTION_OBJECT_KEY).getValue()); assertNotNull("Missing metadata signature", objectData.get(TransformConstants.META_ENCRYPTION_META_SIG).getValue()); } // Test creating a compressed and encrypted object @Test public void testCreateEncryptCompress() throws Exception { AtmosEncryptionClient eclient = getBasicEncryptionClientWithCompression(); // Get some data to encrypt. InputStream classin = this.getClass().getClassLoader() .getResourceAsStream("uncompressed.txt"); CountingInputStream incount = new CountingInputStream(classin); ObjectId id = eclient.createObject(incount, "text/plain"); cleanup.add(id); classin.close(); long bytesSent = incount.getByteCount(); assertEquals("Incorrect number of bytes sent", 2516125, bytesSent); // Read back and test ReadObjectRequest ror = new ReadObjectRequest(); ror.setIdentifier(id); ReadObjectResponse<byte[]> resp = eclient.readObject(ror, byte[].class); Map<String, Metadata> objectData = resp.getMetadata().getMetadata(); assertEquals("Uncompressed digest incorrect", "027e997e6b1dfc97b93eb28dc9a6804096d85873", objectData.get(TransformConstants.META_COMPRESSION_UNCOMP_SHA1).getValue()); assertEquals("Transform mode incorrect", "COMP:Deflate/5|ENC:AES/CBC/PKCS5Padding", objectData.get(TransformConstants.META_TRANSFORM_MODE).getValue()); assertEquals("Uncompressed size incorrect", 2516125, Long.parseLong(objectData .get(TransformConstants.META_COMPRESSION_UNCOMP_SIZE).getValue())); assertNotNull("Missing IV", objectData.get(TransformConstants.META_ENCRYPTION_IV).getValue()); assertEquals("Incorrect master encryption key ID", KeyUtils.getRsaPublicKeyFingerprint((RSAPublicKey) masterKey .getPublic(), provider), objectData.get(TransformConstants.META_ENCRYPTION_KEY_ID).getValue()); assertNotNull("Missing object key", objectData.get(TransformConstants.META_ENCRYPTION_OBJECT_KEY).getValue()); assertNotNull("Missing metadata signature", objectData.get(TransformConstants.META_ENCRYPTION_META_SIG).getValue()); assertTrue("Object not compressed", bytesSent > Long.parseLong(objectData.get("size").getValue())); } // Test creating a compressed object (no encryption) @Test public void testCreateCompress() throws Exception { AtmosEncryptionClient eclient = getCompressionClient(); // Get some data to encrypt. InputStream classin = this.getClass().getClassLoader() .getResourceAsStream("uncompressed.txt"); CountingInputStream incount = new CountingInputStream(classin); ObjectId id = eclient.createObject(incount, "text/plain"); cleanup.add(id); classin.close(); long bytesSent = incount.getByteCount(); assertEquals("Incorrect number of bytes sent", 2516125, bytesSent); // Read back and test ReadObjectRequest ror = new ReadObjectRequest(); ror.setIdentifier(id); ReadObjectResponse<byte[]> resp = eclient.readObject(ror, byte[].class); Map<String, Metadata> objectData = resp.getMetadata().getMetadata(); assertEquals("Uncompressed digest incorrect", "027e997e6b1dfc97b93eb28dc9a6804096d85873", objectData.get(TransformConstants.META_COMPRESSION_UNCOMP_SHA1).getValue()); assertEquals("Uncompressed size incorrect", 2516125, Long.parseLong(objectData .get(TransformConstants.META_COMPRESSION_UNCOMP_SIZE).getValue())); assertTrue("Object not compressed", bytesSent > Long.parseLong(objectData.get("size").getValue())); // Encryption meta should not be present. assertNull("Should not have IV", objectData.get(TransformConstants.META_ENCRYPTION_IV)); assertNull("Should not have key", objectData.get(TransformConstants.META_ENCRYPTION_OBJECT_KEY)); assertNull("Should not have signature", objectData.get(TransformConstants.META_ENCRYPTION_META_SIG)); // Check stream byte[] data = resp.getObject(); assertEquals("Stream size incorrect", 2516125, data.length); MessageDigest sha1 = MessageDigest.getInstance("SHA-1"); byte[] digest = sha1.digest(data); assertEquals("Stream digest incorrect", "027e997e6b1dfc97b93eb28dc9a6804096d85873", KeyUtils.toHexPadded(digest)); } // Test rekeying an object with basic keys @Test public void testRekeyBasic() throws Exception { AtmosEncryptionClient eclient = getBasicEncryptionClient(oldKey); // Create an object encrypted with the old key. String content = "Hello World!"; ObjectId id = eclient.createObject(content, "text/plain"); cleanup.add(id); // Rekey eclient = getBasicEncryptionClient(); eclient.rekey(id); // Read back -- should now be encrypted with 'masterKey' ReadObjectRequest ror = new ReadObjectRequest(); ror.setIdentifier(id); ReadObjectResponse<String> resp = eclient.readObject(ror, String.class); assertEquals("Content differs", content, resp.getObject()); assertEquals("unencrypted size incorrect", "12", resp.getMetadata().getMetadata().get(TransformConstants.META_ENCRYPTION_UNENC_SIZE).getValue()); assertEquals("encrypted size incorrect", "16", resp.getMetadata().getMetadata().get("size").getValue()); assertEquals("unencrypted sha1 incorrect", "2ef7bde608ce5404e97d5f042f95f89f1c232871", resp.getMetadata().getMetadata().get(TransformConstants.META_ENCRYPTION_UNENC_SHA1).getValue()); assertEquals("master key ID incorrect", KeyUtils.getRsaPublicKeyFingerprint((RSAPublicKey) masterKey.getPublic(), provider), resp.getMetadata().getMetadata().get(TransformConstants.META_ENCRYPTION_KEY_ID).getValue()); assertNotNull("IV null", resp.getMetadata().getMetadata().get(TransformConstants.META_ENCRYPTION_IV).getValue()); assertNotNull("Object key", resp.getMetadata().getMetadata().get(TransformConstants.META_ENCRYPTION_OBJECT_KEY).getValue()); } // Test rekeying an object with a keystore public void testRekeyKeystore() throws Exception { AtmosEncryptionClient eclient = getKeystoreEncryptionClient(oldKeyAlias); String content = "Hello World!"; ObjectId id = eclient.createObject(content, "text/plain"); cleanup.add(id); // Rekey eclient = getKeystoreEncryptionClient(); eclient.rekey(id); // Read back and test ReadObjectRequest ror = new ReadObjectRequest(); ror.setIdentifier(id); ReadObjectResponse<String> resp = eclient.readObject(ror, String.class); assertEquals("Content differs", content, resp.getObject()); assertEquals("unencrypted size incorrect", "12", resp.getMetadata().getMetadata().get(TransformConstants.META_ENCRYPTION_UNENC_SIZE).getValue()); assertEquals("encrypted size incorrect", "16", resp.getMetadata().getMetadata().get("size")); assertEquals("unencrypted sha1 incorrect", "2ef7bde608ce5404e97d5f042f95f89f1c232871", resp.getMetadata().getMetadata().get(TransformConstants.META_ENCRYPTION_UNENC_SHA1).getValue()); assertEquals("master key ID incorrect", KeyUtils.getRsaPublicKeyFingerprint((RSAPublicKey) masterKey.getPublic(), provider), resp.getMetadata().getMetadata().get(TransformConstants.META_ENCRYPTION_KEY_ID).getValue()); assertNotNull("IV null", resp.getMetadata().getMetadata().get(TransformConstants.META_ENCRYPTION_IV).getValue()); assertNotNull("Object key", resp.getMetadata().getMetadata().get(TransformConstants.META_ENCRYPTION_OBJECT_KEY).getValue()); } // Test rekeying an object that does not need rekeying. // @Test(expected=DoesNotNeedRekeyException.class) public void testRekeyNoRekeyRequired() throws Exception { AtmosEncryptionClient eclient = getKeystoreEncryptionClient(oldKeyAlias); String content = "Hello World!"; ObjectId id = eclient.createObject(content, "text/plain"); cleanup.add(id); // Rekey -- should throw an exception that no rekey is needed. try { eclient.rekey(id); Assert.fail("DoesNotNeedRekeyException not thrown."); } catch(DoesNotNeedRekeyException e) { assertEquals("Wrong message", "Object is already using the current master key", e.getMessage()); } } // Test rekeying an object that does not need rekeying because it wasn't compressed // in the first place. //@Test(expected=DoesNotNeedRekeyException.class) public void testRekeyNotEncrypted() throws Exception { AtmosEncryptionClient eclient = getCompressionClient(); String content = "Hello World!"; ObjectId id = eclient.createObject(content, "text/plain"); cleanup.add(id); // Rekey -- should throw an exception that no rekey is needed. try { eclient.rekey(id); Assert.fail("DoesNotNeedRekeyException not thrown."); } catch(DoesNotNeedRekeyException e) { assertEquals("Wrong message", "Object was not rekeyed", e.getMessage()); } } // Test partial read (should fail) @Test(expected=UnsupportedOperationException.class) public void testPartialRead() throws Exception { AtmosEncryptionClient eclient = getKeystoreEncryptionClient(oldKeyAlias); String content = "Hello World!"; ObjectId id = eclient.createObject(content, "text/plain"); cleanup.add(id); // Partial read eclient.readObject(id, new Range(1, 1), byte[].class); } // Test partial read (should fail) @Test(expected=UnsupportedOperationException.class) public void testPartialRead2() throws Exception { AtmosEncryptionClient eclient = getKeystoreEncryptionClient(oldKeyAlias); String content = "Hello World!"; ObjectId id = eclient.createObject(content, "text/plain"); cleanup.add(id); // Partial read ReadObjectRequest req = new ReadObjectRequest().identifier(id).ranges(new Range(1,1), new Range(2,2)); eclient.readObject(req, byte[].class); } // Test object overwrite @Test public void testOverwriteObject() throws Exception { AtmosEncryptionClient eclient = getBasicEncryptionClient(); String content = "Hello World!"; ObjectId id = eclient.createObject(content, "text/plain"); cleanup.add(id); // Overwrite content = "Hello Again"; eclient.updateObject(id, content); // Read back and check. ReadObjectRequest ror = new ReadObjectRequest(); ror.setIdentifier(id); ReadObjectResponse<String> resp = eclient.readObject(ror, String.class); assertEquals("Content differs", content, resp.getObject()); assertEquals("unencrypted size incorrect", "11", resp.getMetadata().getMetadata().get(TransformConstants.META_ENCRYPTION_UNENC_SIZE).getValue()); assertEquals("encrypted size incorrect", "16", resp.getMetadata().getMetadata().get("size").getValue()); assertEquals("unencrypted sha1 incorrect", "f18fd13626d3c9c76dc1386bb6d5b4d6a9f6d365", resp.getMetadata().getMetadata().get(TransformConstants.META_ENCRYPTION_UNENC_SHA1).getValue()); assertEquals("master key ID incorrect", KeyUtils.getRsaPublicKeyFingerprint((RSAPublicKey) masterKey.getPublic(), provider), resp.getMetadata().getMetadata().get(TransformConstants.META_ENCRYPTION_KEY_ID).getValue()); assertNotNull("IV null", resp.getMetadata().getMetadata().get(TransformConstants.META_ENCRYPTION_IV).getValue()); assertNotNull("Object key", resp.getMetadata().getMetadata().get(TransformConstants.META_ENCRYPTION_OBJECT_KEY).getValue()); } // Test object overwrite and change from encrypted to compressed. @Test public void testOverwriteObjectCompressed() throws Exception { AtmosEncryptionClient eclient = getBasicEncryptionClient(); String content = "Hello World!"; Metadata[] meta = new Metadata[] { new Metadata("myname", "my value", false), new Metadata("listable", "", true)}; CreateObjectRequest req = new CreateObjectRequest().content(content).contentType("text/plain").userMetadata(meta); CreateObjectResponse resp1 = eclient.createObject(req); ObjectId id = resp1.getObjectId(); cleanup.add(id); // Overwrite eclient = getCompressionClient(); // Get some data to encrypt. InputStream classin = this.getClass().getClassLoader() .getResourceAsStream("uncompressed.txt"); CountingInputStream incount = new CountingInputStream(classin); eclient.updateObject(id, incount); cleanup.add(id); classin.close(); long bytesSent = incount.getByteCount(); assertEquals("Incorrect number of bytes sent", 2516125, bytesSent); // Read back and test ReadObjectRequest ror = new ReadObjectRequest(); ror.setIdentifier(id); ReadObjectResponse<byte[]> resp2 = eclient.readObject(ror, byte[].class); Map<String, Metadata> objectData = resp2.getMetadata().getMetadata(); assertEquals("Uncompressed digest incorrect", "027e997e6b1dfc97b93eb28dc9a6804096d85873", objectData.get(TransformConstants.META_COMPRESSION_UNCOMP_SHA1).getValue()); assertEquals("Uncompressed size incorrect", 2516125, Long.parseLong(objectData .get(TransformConstants.META_COMPRESSION_UNCOMP_SIZE).getValue())); assertTrue("Object not compressed", bytesSent > Long.parseLong(objectData.get("size").getValue())); // Encryption meta should not be present. assertNull("Should not have IV", objectData.get(TransformConstants.META_ENCRYPTION_IV)); assertNull("Should not have key", objectData.get(TransformConstants.META_ENCRYPTION_OBJECT_KEY)); assertNull("Should not have signature", objectData.get(TransformConstants.META_ENCRYPTION_META_SIG)); // Make sure our metadata tags are still there. for(Metadata m : meta) { Metadata mm = null; assertTrue("Missing metadata tag: " + m.getName(), (mm = objectData.get(m.getName())) != null); assertEquals("Metadata incorrect", m, mm); assertEquals("Metadata listable flag incorrect for " + m.getName(), m.isListable(), mm.isListable()); } // Check stream byte[] data = resp2.getObject(); assertEquals("Stream size incorrect", 2516125, data.length); MessageDigest sha1 = MessageDigest.getInstance("SHA-1"); byte[] digest = sha1.digest(data); assertEquals("Stream digest incorrect", "027e997e6b1dfc97b93eb28dc9a6804096d85873", KeyUtils.toHexPadded(digest)); } // Test partial update (should fail) @Test(expected=UnsupportedOperationException.class) public void testObjectPartialOverwrite() throws Exception { AtmosEncryptionClient eclient = getBasicEncryptionClient(); String content = "Hello World!"; ObjectId id = eclient.createObject(content, "text/plain"); cleanup.add(id); // Overwrite content = "Hello Again"; eclient.updateObject(id, content, new Range(5,15)); } // Test object append (should fail) @Test(expected=UnsupportedOperationException.class) public void testObjectAppend() throws Exception { AtmosEncryptionClient eclient = getBasicEncryptionClient(); String content = "Hello World!"; ObjectId id = eclient.createObject(content, "text/plain"); cleanup.add(id); // Append content = "Hello Again"; UpdateObjectRequest uor = new UpdateObjectRequest().identifier(id) .range(new Range(12,22)).content(content); eclient.updateObject(uor); } public static String rand8char() { Random r = new Random(); StringBuilder sb = new StringBuilder( 8 ); for ( int i = 0; i < 8; i++ ) { sb.append( (char) ('a' + r.nextInt( 26 )) ); } return sb.toString(); } }