Skip to content

Commit

Permalink
Fix code review feedback.
Browse files Browse the repository at this point in the history
  • Loading branch information
jcookems committed Jan 11, 2013
1 parent 2f81d3e commit e827e13
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 58 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ public static boolean canUseStrongCrypto() {
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
}
catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ public class EncryptionIntegrationTest extends IntegrationTestBase {
public void uploadAesProtectedAssetAndDownloadSuccess() throws Exception {
// Arrange
if (!EncryptionHelper.canUseStrongCrypto()) {
Assert.fail("JVM does not appear support the required encryption");
Assert.fail("JVM does not support the required encryption");
}

// Media Services requires 256-bit (32-byte) keys and
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,6 @@
import java.util.Random;
import java.util.UUID;

import junit.framework.Assert;

import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
Expand Down Expand Up @@ -94,14 +92,8 @@ public void uploadFiles() throws Exception {
@Test
public void uploadEncryptedFiles() throws Exception {
signalSetupStarting();
// Media Services requires 256-bit (32-byte) keys for AES encryption.
Random random = new Random();
byte[] aesKey = new byte[32];
random.nextBytes(aesKey);
byte[] aesKey = getNewAesKey();
AssetInfo asset = wrapper.createAsset(testAssetPrefix + "uploadEncryptedFiles", AssetOption.StorageEncrypted);
if (asset == null) {
Assert.fail("JVM does not appear support the required encryption");
}

signalSetupFinished();

Expand Down Expand Up @@ -152,15 +144,9 @@ public void transformAsset() throws Exception {
@Test
public void transformEncryptedAsset() throws Exception {
signalSetupStarting();
// Media Services requires 256-bit (32-byte) keys for AES encryption.
Random random = new Random();
byte[] aesKey = new byte[32];
random.nextBytes(aesKey);
byte[] aesKey = getNewAesKey();
AssetInfo asset = wrapper
.createAsset(testAssetPrefix + "transformEncryptedAsset", AssetOption.StorageEncrypted);
if (asset == null) {
Assert.fail("JVM does not appear support the required encryption");
}

wrapper.uploadFilesToAsset(asset, 10, getTestAssetFiles(), aesKey);
String jobName = "my job transformEncryptedAsset" + UUID.randomUUID().toString();
Expand All @@ -182,6 +168,14 @@ public void transformEncryptedAsset() throws Exception {
validator.validateAssetFiles(getTestAssetFiles(), actualFileStreams);
}

private byte[] getNewAesKey() {
// Media Services requires 256-bit (32-byte) keys for AES encryption.
Random random = new Random();
byte[] aesKey = new byte[32];
random.nextBytes(aesKey);
return aesKey;
}

private void waitForJobToFinish(JobInfo job) throws InterruptedException, ServiceException {
for (int counter = 0; !wrapper.isJobFinished(job); counter++) {
if (counter > 30) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import junit.framework.Assert;

import com.microsoft.windowsazure.services.core.ServiceException;
import com.microsoft.windowsazure.services.core.storage.utils.Base64;
import com.microsoft.windowsazure.services.media.MediaContract;
Expand Down Expand Up @@ -128,7 +130,7 @@ public MediaServiceWrapper(MediaContract service) {
// Manage
public AssetInfo createAsset(String name, AssetOption encryption) throws ServiceException {
if (encryption == AssetOption.StorageEncrypted && !EncryptionHelper.canUseStrongCrypto()) {
return null;
Assert.fail("JVM does not support the required encryption");
}

// Create asset. The SDK's top-level method is the simplest way to do that.
Expand Down Expand Up @@ -164,66 +166,35 @@ public void uploadFilesToAsset(AssetInfo asset, int uploadWindowInMinutes,
uploadWindowInMinutes, EnumSet.of(AccessPolicyPermission.WRITE)));
LocatorInfo locator = service.create(Locator.create(accessPolicy.getId(), asset.getId(), LocatorType.SAS));

String contentKeyId = null;
if (aesKey != null) {
String protectionKeyId = service.action(ProtectionKey.getProtectionKeyId(ContentKeyType.StorageEncryption));
String protectionKey = service.action(ProtectionKey.getProtectionKey(protectionKeyId));

String contentKeyIdUuid = UUID.randomUUID().toString();
contentKeyId = "nb:kid:UUID:" + contentKeyIdUuid;

byte[] encryptedContentKey = EncryptionHelper.encryptSymmetricKey(protectionKey, aesKey);
String encryptedContentKeyString = Base64.encode(encryptedContentKey);
String checksum = EncryptionHelper.calculateContentKeyChecksum(contentKeyIdUuid, aesKey);

service.create(ContentKey.create(contentKeyId, ContentKeyType.StorageEncryption, encryptedContentKeyString)
.setChecksum(checksum).setProtectionKeyId(protectionKeyId));
service.action(Asset.linkContentKey(asset.getId(), contentKeyId));
}
String contentKeyId = createAssetContentKey(asset, aesKey);

WritableBlobContainerContract uploader = service.createBlobWriter(locator);

Hashtable<String, AssetFileInfo> infoToUpload = new Hashtable<String, AssetFileInfo>();

boolean isFirst = true;
for (String fileName : inputFiles.keySet()) {

MessageDigest digest = MessageDigest.getInstance("MD5");

InputStream inputStream = inputFiles.get(fileName);

String initializationVector = null;
byte[] iv = null;
if (aesKey != null) {
// Media Services requires 128-bit (16-byte) initialization vectors (IV)
// for AES encryption, but also that only the first 8 bytes are filled.
Random random = new Random();
byte[] effectiveIv = new byte[8];
random.nextBytes(effectiveIv);
byte[] iv = new byte[16];
System.arraycopy(effectiveIv, 0, iv, 0, effectiveIv.length);

byte[] sub = new byte[9];
// Offset the bytes to ensure that the sign-bit is not set.
// Media Services expects unsigned Int64 values.
System.arraycopy(iv, 0, sub, 1, 8);
BigInteger longIv = new BigInteger(sub);
initializationVector = longIv.toString();

iv = createIV();
inputStream = EncryptionHelper.encryptFile(inputStream, aesKey, iv);
}

InputStream digestStream = new DigestInputStream(inputStream, digest);
CountingStream countingStream = new CountingStream(digestStream);

uploader.createBlockBlob(fileName, countingStream);

inputStream.close();
byte[] md5hash = digest.digest();
String md5 = Base64.encode(md5hash);

AssetFileInfo fi = new AssetFileInfo(null, new AssetFileType().setContentChecksum(md5)
.setContentFileSize(new Long(countingStream.getCount())).setIsPrimary(isFirst).setName(fileName)
.setParentAssetId(asset.getAlternateId()).setInitializationVector(initializationVector));
.setContentFileSize(countingStream.getCount()).setIsPrimary(isFirst).setName(fileName)
.setInitializationVector(getIVString(iv)));
infoToUpload.put(fileName, fi);

isFirst = false;
Expand All @@ -249,9 +220,54 @@ public void uploadFilesToAsset(AssetInfo asset, int uploadWindowInMinutes,
service.delete(AccessPolicy.delete(accessPolicy.getId()));
}

private String getIVString(byte[] iv) {
if (iv == null) {
return null;
}

// Offset the bytes to ensure that the sign-bit is not set.
// Media Services expects unsigned Int64 values.
byte[] sub = new byte[9];
System.arraycopy(iv, 0, sub, 1, 8);
BigInteger longIv = new BigInteger(sub);
return longIv.toString();
}

private byte[] createIV() {
// Media Services requires 128-bit (16-byte) initialization vectors (IV)
// for AES encryption, but also that only the first 8 bytes are filled.
Random random = new Random();
byte[] effectiveIv = new byte[8];
random.nextBytes(effectiveIv);
byte[] iv = new byte[16];
System.arraycopy(effectiveIv, 0, iv, 0, effectiveIv.length);
return iv;
}

private String createAssetContentKey(AssetInfo asset, byte[] aesKey) throws Exception {
if (aesKey == null) {
return null;
}

String protectionKeyId = service.action(ProtectionKey.getProtectionKeyId(ContentKeyType.StorageEncryption));
String protectionKey = service.action(ProtectionKey.getProtectionKey(protectionKeyId));

String contentKeyIdUuid = UUID.randomUUID().toString();
String contentKeyId = "nb:kid:UUID:" + contentKeyIdUuid;

byte[] encryptedContentKey = EncryptionHelper.encryptSymmetricKey(protectionKey, aesKey);
String encryptedContentKeyString = Base64.encode(encryptedContentKey);
String checksum = EncryptionHelper.calculateContentKeyChecksum(contentKeyIdUuid, aesKey);

service.create(ContentKey.create(contentKeyId, ContentKeyType.StorageEncryption, encryptedContentKeyString)
.setChecksum(checksum).setProtectionKeyId(protectionKeyId));
service.action(Asset.linkContentKey(asset.getId(), contentKeyId));
return contentKeyId;
}

private static class CountingStream extends InputStream {
private final InputStream wrappedStream;
private int count;
private long count;

public CountingStream(InputStream wrapped) {
wrappedStream = wrapped;
Expand All @@ -264,7 +280,7 @@ public int read() throws IOException {
return wrappedStream.read();
}

public int getCount() {
public long getCount() {
return count;
}
}
Expand Down Expand Up @@ -473,7 +489,6 @@ public static boolean canUseStrongCrypto() {
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
}
catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
Expand Down

0 comments on commit e827e13

Please sign in to comment.