From 1fd4cbdaaefab8ea7b8ef4353414e1c0c0a3bb52 Mon Sep 17 00:00:00 2001 From: Albert Cheng Date: Thu, 31 Jan 2013 17:46:13 -0800 Subject: [PATCH 01/20] partial checkin of rebind content key to allow compilation. --- .../services/media/models/ContentKey.java | 37 ++++++++++++++++ .../media/ContentKeyIntegrationTest.java | 43 ++++++++++++++++++- 2 files changed, 79 insertions(+), 1 deletion(-) diff --git a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/media/models/ContentKey.java b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/media/models/ContentKey.java index fea8d2ba62e2..88f0100b9675 100644 --- a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/media/models/ContentKey.java +++ b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/media/models/ContentKey.java @@ -16,6 +16,7 @@ package com.microsoft.windowsazure.services.media.models; import com.microsoft.windowsazure.services.media.entityoperations.DefaultDeleteOperation; +import com.microsoft.windowsazure.services.media.entityoperations.DefaultEntityTypeActionOperation; import com.microsoft.windowsazure.services.media.entityoperations.DefaultGetOperation; import com.microsoft.windowsazure.services.media.entityoperations.DefaultListOperation; import com.microsoft.windowsazure.services.media.entityoperations.EntityCreateOperation; @@ -213,4 +214,40 @@ public static EntityDeleteOperation delete(String contentKeyId) { return new DefaultDeleteOperation(ENTITY_SET, contentKeyId); } + /** + * Rebind content key with specified content key and X509 Certificate. + * + * @param contentKeyId + * the content key id + * @param x509Certificate + * the x509 certificate + * @return the entity action operation + */ + public static RebindContentKeyActionOperation rebind(String contentKeyId, String x509Certificate) { + return new RebindContentKeyActionOperation(contentKeyId, x509Certificate); + } + + /** + * Rebind content key with specified content key Id. + * + * @param contentKeyId + * the content key id + * @return the entity action operation + */ + public static RebindContentKeyActionOperation rebind(String contentKeyId) { + return rebind(contentKeyId, ""); + } + + private static class RebindContentKeyActionOperation extends DefaultEntityTypeActionOperation { + + private final String contentKeyId; + private final String x509Certificate; + + public RebindContentKeyActionOperation(String contentKeyId, String x509Certificate) { + this.contentKeyId = contentKeyId; + this.x509Certificate = x509Certificate; + } + + } + } diff --git a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/ContentKeyIntegrationTest.java b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/ContentKeyIntegrationTest.java index 3f479a6ea8b7..9ec0464ee3af 100644 --- a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/ContentKeyIntegrationTest.java +++ b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/ContentKeyIntegrationTest.java @@ -27,6 +27,7 @@ import com.microsoft.windowsazure.services.media.models.ContentKey; import com.microsoft.windowsazure.services.media.models.ContentKeyInfo; import com.microsoft.windowsazure.services.media.models.ContentKeyType; +import com.microsoft.windowsazure.services.media.models.ProtectionKey; import com.microsoft.windowsazure.services.media.models.ProtectionKeyType; public class ContentKeyIntegrationTest extends IntegrationTestBase { @@ -35,6 +36,15 @@ public class ContentKeyIntegrationTest extends IntegrationTestBase { private final ContentKeyType testContentKeyType = ContentKeyType.CommonEncryption; private final String testEncryptedContentKey = "ThisIsEncryptedContentKey"; + private ContentKeyInfo createTestContentKey(String contentKeyNameSuffix) throws ServiceException { + String testContentKeyId = createRandomContentKeyId(); + String testContentKeyName = testContentKeyPrefix + contentKeyNameSuffix; + + ContentKeyInfo contentKeyInfo = service.create(ContentKey.create(testContentKeyId, testContentKeyType, + testEncryptedContentKey).setName(testContentKeyName)); + return contentKeyInfo; + } + private String createRandomContentKeyId() { UUID uuid = UUID.randomUUID(); String randomContentKey = String.format("nb:kid:UUID:%s", uuid); @@ -64,7 +74,7 @@ private void verifyContentKeyProperties(String message, String id, ContentKeyTyp public void canCreateContentKey() throws Exception { // Arrange String testCanCreateContentKeyId = createRandomContentKeyId(); - String testCanCreateContentKeyName = "testCanCreateContentKey"; + String testCanCreateContentKeyName = testContentKeyPrefix + "testCanCreateContentKey"; // Act ContentKeyInfo contentKeyInfo = service.create(ContentKey.create(testCanCreateContentKeyId, testContentKeyType, @@ -176,4 +186,35 @@ public void cannotDeleteContentKeyByNonexistId() throws Exception { service.delete(ContentKey.delete(validButNonexistContentKeyId)); } + @Test + public void rebindContentKeyNoX509CertificateSuccess() throws ServiceException { + ContentKeyInfo contentKeyInfo = createTestContentKey("rebindContentKeyNoX509Success"); + + String rebindedContentKey = service.action(ContentKey.rebind(contentKeyInfo.getId())); + + assertEquals(contentKeyInfo.getEncryptedContentKey(), rebindedContentKey); + } + + @Test + public void rebindContentKeyWithX509CertficateSuccess() throws ServiceException { + ContentKeyInfo contentKeyInfo = createTestContentKey("rebindContentKeyWithX509Certificate"); + String protectionKeyId = service.action(ProtectionKey.getProtectionKeyId(ContentKeyType.CommonEncryption)); + String x509Certificate = service.action(ProtectionKey.getProtectionKey(protectionKeyId)); + String rebindedContentKey = service.action(ContentKey.rebind(contentKeyInfo.getId(), x509Certificate)); + assertEquals(contentKeyInfo.getEncryptedContentKey(), rebindedContentKey); + } + + @Test + public void rebindContentKeyWithIncorrectContentKeyIdFailed() throws ServiceException { + expectedException.expect(ServiceException.class); + service.action(ContentKey.rebind("invalidContentKeyId")); + } + + @Test + public void rebindContentKeyWithIncorrectX509CertificateFailed() throws ServiceException { + expectedException.expect(ServiceException.class); + ContentKeyInfo contentKeyInfo = createTestContentKey("rebindContentKeyWithIncorrectX509CertficateFailed"); + + service.action(ContentKey.rebind(contentKeyInfo.getId(), "InvalidX509Certificate")); + } } From 3e47119f1b62a6c9d5b4c4caa09e5d5623309719 Mon Sep 17 00:00:00 2001 From: Albert Cheng Date: Wed, 6 Feb 2013 10:45:56 -0800 Subject: [PATCH 02/20] update rebind content key. --- .../services/media/models/ContentKey.java | 43 +++++++++++++--- .../media/ContentKeyIntegrationTest.java | 50 ++++++++++++++++--- .../media/EncryptionIntegrationTest.java | 4 +- 3 files changed, 81 insertions(+), 16 deletions(-) diff --git a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/media/models/ContentKey.java b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/media/models/ContentKey.java index 88f0100b9675..ef2b91db547f 100644 --- a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/media/models/ContentKey.java +++ b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/media/models/ContentKey.java @@ -15,6 +15,12 @@ package com.microsoft.windowsazure.services.media.models; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.security.InvalidParameterException; + +import com.microsoft.windowsazure.services.core.utils.pipeline.PipelineHelpers; import com.microsoft.windowsazure.services.media.entityoperations.DefaultDeleteOperation; import com.microsoft.windowsazure.services.media.entityoperations.DefaultEntityTypeActionOperation; import com.microsoft.windowsazure.services.media.entityoperations.DefaultGetOperation; @@ -23,7 +29,9 @@ import com.microsoft.windowsazure.services.media.entityoperations.EntityDeleteOperation; import com.microsoft.windowsazure.services.media.entityoperations.EntityGetOperation; import com.microsoft.windowsazure.services.media.entityoperations.EntityOperationSingleResultBase; +import com.microsoft.windowsazure.services.media.entityoperations.EntityTypeActionOperation; import com.microsoft.windowsazure.services.media.implementation.content.ContentKeyRestType; +import com.sun.jersey.api.client.ClientResponse; import com.sun.jersey.api.client.GenericType; /** @@ -223,7 +231,7 @@ public static EntityDeleteOperation delete(String contentKeyId) { * the x509 certificate * @return the entity action operation */ - public static RebindContentKeyActionOperation rebind(String contentKeyId, String x509Certificate) { + public static EntityTypeActionOperation rebind(String contentKeyId, String x509Certificate) { return new RebindContentKeyActionOperation(contentKeyId, x509Certificate); } @@ -234,20 +242,39 @@ public static RebindContentKeyActionOperation rebind(String contentKeyId, String * the content key id * @return the entity action operation */ - public static RebindContentKeyActionOperation rebind(String contentKeyId) { + public static EntityTypeActionOperation rebind(String contentKeyId) { return rebind(contentKeyId, ""); } private static class RebindContentKeyActionOperation extends DefaultEntityTypeActionOperation { - private final String contentKeyId; - private final String x509Certificate; - public RebindContentKeyActionOperation(String contentKeyId, String x509Certificate) { - this.contentKeyId = contentKeyId; - this.x509Certificate = x509Certificate; + super("RebindContentKey"); + + String escapedContentKeyId; + try { + escapedContentKeyId = URLEncoder.encode(contentKeyId, "UTF-8"); + } + catch (UnsupportedEncodingException e) { + throw new InvalidParameterException("UTF-8 encoding is not supported."); + } + this.addQueryParameter("x509Certificate", "'" + x509Certificate + "'"); + this.addQueryParameter("contentkeyid", "'" + escapedContentKeyId + "'"); + } - } + @Override + public String processTypeResponse(ClientResponse clientResponse) { + PipelineHelpers.ThrowIfNotSuccess(clientResponse); + String contentKeyValue; + contentKeyValue = parseResponse(clientResponse); + return contentKeyValue; + } + private String parseResponse(ClientResponse clientResponse) { + InputStream inputStream = clientResponse.getEntityInputStream(); + return inputStream.toString(); + } + + } } diff --git a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/ContentKeyIntegrationTest.java b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/ContentKeyIntegrationTest.java index 9ec0464ee3af..31c213ea427b 100644 --- a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/ContentKeyIntegrationTest.java +++ b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/ContentKeyIntegrationTest.java @@ -19,11 +19,13 @@ import java.util.ArrayList; import java.util.List; +import java.util.Random; import java.util.UUID; import org.junit.Test; import com.microsoft.windowsazure.services.core.ServiceException; +import com.microsoft.windowsazure.services.core.storage.utils.Base64; import com.microsoft.windowsazure.services.media.models.ContentKey; import com.microsoft.windowsazure.services.media.models.ContentKeyInfo; import com.microsoft.windowsazure.services.media.models.ContentKeyType; @@ -45,6 +47,31 @@ private ContentKeyInfo createTestContentKey(String contentKeyNameSuffix) throws return contentKeyInfo; } + private ContentKeyInfo createValidTestContentKey(String contentKeyNameSuffix) throws Exception { + + Random random = new Random(); + byte[] aesKey = new byte[32]; + random.nextBytes(aesKey); + + String testContnetKeyName = testContentKeyPrefix + contentKeyNameSuffix; + String protectionKeyId = service.action(ProtectionKey.getProtectionKeyId(ContentKeyType.StorageEncryption)); + String protectionKey = service.action(ProtectionKey.getProtectionKey(protectionKeyId)); + + String testContentKeyIdUuid = UUID.randomUUID().toString(); + String testContentKeyId = String.format("nb:kid:UUID:%s", testContentKeyIdUuid); + + byte[] encryptedContentKey = EncryptionHelper.encryptSymmetricKey(protectionKey, aesKey); + String encryptedContentKeyString = Base64.encode(encryptedContentKey); + String checksum = EncryptionHelper.calculateContentKeyChecksum(testContentKeyIdUuid, aesKey); + + ContentKeyInfo contentKeyInfo = service.create(ContentKey + .create(testContentKeyId, ContentKeyType.StorageEncryption, encryptedContentKeyString) + .setChecksum(checksum).setProtectionKeyId(protectionKeyId).setName(testContnetKeyName)); + + return contentKeyInfo; + + } + private String createRandomContentKeyId() { UUID uuid = UUID.randomUUID(); String randomContentKey = String.format("nb:kid:UUID:%s", uuid); @@ -187,17 +214,28 @@ public void cannotDeleteContentKeyByNonexistId() throws Exception { } @Test - public void rebindContentKeyNoX509CertificateSuccess() throws ServiceException { - ContentKeyInfo contentKeyInfo = createTestContentKey("rebindContentKeyNoX509Success"); + public void rebindContentKeyNoX509CertificateSuccess() throws Exception { - String rebindedContentKey = service.action(ContentKey.rebind(contentKeyInfo.getId())); + ContentKeyInfo contentKeyInfo = createValidTestContentKey("rebindContentKeyNoX509Success"); + + String contentKey = service.action(ContentKey.rebind(contentKeyInfo.getId())); + assertNotNull(contentKey); + + } + + @Test + public void rebindInvalidContentKeyNoX509CertificateFail() throws ServiceException { + expectedException.expect(ServiceException.class); + expectedException.expect(new ServiceExceptionMatcher(400)); + ContentKeyInfo contentKeyInfo = createTestContentKey("rebindInvalidContentKeyNoX509Fail"); + + service.action(ContentKey.rebind(contentKeyInfo.getId())); - assertEquals(contentKeyInfo.getEncryptedContentKey(), rebindedContentKey); } @Test - public void rebindContentKeyWithX509CertficateSuccess() throws ServiceException { - ContentKeyInfo contentKeyInfo = createTestContentKey("rebindContentKeyWithX509Certificate"); + public void rebindContentKeyWithX509CertficateSuccess() throws Exception { + ContentKeyInfo contentKeyInfo = createValidTestContentKey("rebindContentKeyWithX509Success"); String protectionKeyId = service.action(ProtectionKey.getProtectionKeyId(ContentKeyType.CommonEncryption)); String x509Certificate = service.action(ProtectionKey.getProtectionKey(protectionKeyId)); String rebindedContentKey = service.action(ContentKey.rebind(contentKeyInfo.getId(), x509Certificate)); diff --git a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/EncryptionIntegrationTest.java b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/EncryptionIntegrationTest.java index 964bafa5072e..973cd91a49a4 100644 --- a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/EncryptionIntegrationTest.java +++ b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/EncryptionIntegrationTest.java @@ -89,7 +89,7 @@ public void uploadAesProtectedAssetAndDownloadSuccess() throws Exception { WritableBlobContainerContract blobWriter = getBlobWriter(assetInfo.getId(), durationInMinutes); // gets the public key for storage encryption. - String contentKeyId = makeContentKeyId(aesKey); + String contentKeyId = createContentKey(aesKey); // link the content key with the asset. service.action(Asset.linkContentKey(assetInfo.getId(), contentKeyId)); @@ -143,7 +143,7 @@ private JobInfo decodeAsset(String name, String inputAssetId) throws ServiceExce return currentJobInfo; } - private String makeContentKeyId(byte[] aesKey) throws ServiceException, Exception { + private String createContentKey(byte[] aesKey) throws ServiceException, Exception { String protectionKeyId = service.action(ProtectionKey.getProtectionKeyId(ContentKeyType.StorageEncryption)); String protectionKey = service.action(ProtectionKey.getProtectionKey(protectionKeyId)); From 0f77e6d3e0e9cb5f976113e9fb55c591de25cdc5 Mon Sep 17 00:00:00 2001 From: Albert Cheng Date: Thu, 7 Feb 2013 18:15:35 -0800 Subject: [PATCH 03/20] rebind content key working without X509Certificate working. --- .../windowsazure/services/media/models/ContentKey.java | 2 +- .../services/media/ContentKeyIntegrationTest.java | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/media/models/ContentKey.java b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/media/models/ContentKey.java index ef2b91db547f..9b6002836cf5 100644 --- a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/media/models/ContentKey.java +++ b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/media/models/ContentKey.java @@ -259,7 +259,7 @@ public RebindContentKeyActionOperation(String contentKeyId, String x509Certifica throw new InvalidParameterException("UTF-8 encoding is not supported."); } this.addQueryParameter("x509Certificate", "'" + x509Certificate + "'"); - this.addQueryParameter("contentkeyid", "'" + escapedContentKeyId + "'"); + this.addQueryParameter("id", "'" + escapedContentKeyId + "'"); } diff --git a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/ContentKeyIntegrationTest.java b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/ContentKeyIntegrationTest.java index 31c213ea427b..4d4c1075c169 100644 --- a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/ContentKeyIntegrationTest.java +++ b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/ContentKeyIntegrationTest.java @@ -17,6 +17,7 @@ import static org.junit.Assert.*; +import java.net.URLEncoder; import java.util.ArrayList; import java.util.List; import java.util.Random; @@ -226,7 +227,7 @@ public void rebindContentKeyNoX509CertificateSuccess() throws Exception { @Test public void rebindInvalidContentKeyNoX509CertificateFail() throws ServiceException { expectedException.expect(ServiceException.class); - expectedException.expect(new ServiceExceptionMatcher(400)); + expectedException.expect(new ServiceExceptionMatcher(500)); ContentKeyInfo contentKeyInfo = createTestContentKey("rebindInvalidContentKeyNoX509Fail"); service.action(ContentKey.rebind(contentKeyInfo.getId())); @@ -236,8 +237,9 @@ public void rebindInvalidContentKeyNoX509CertificateFail() throws ServiceExcepti @Test public void rebindContentKeyWithX509CertficateSuccess() throws Exception { ContentKeyInfo contentKeyInfo = createValidTestContentKey("rebindContentKeyWithX509Success"); - String protectionKeyId = service.action(ProtectionKey.getProtectionKeyId(ContentKeyType.CommonEncryption)); - String x509Certificate = service.action(ProtectionKey.getProtectionKey(protectionKeyId)); + String protectionKeyId = service.action(ProtectionKey.getProtectionKeyId(ContentKeyType.StorageEncryption)); + String x509Certificate = URLEncoder.encode(service.action(ProtectionKey.getProtectionKey(protectionKeyId)), + "UTF-8"); String rebindedContentKey = service.action(ContentKey.rebind(contentKeyInfo.getId(), x509Certificate)); assertEquals(contentKeyInfo.getEncryptedContentKey(), rebindedContentKey); } From 861b366d1c01579879cd1194099711eb8425beb9 Mon Sep 17 00:00:00 2001 From: Albert Cheng Date: Mon, 4 Mar 2013 15:38:37 -0800 Subject: [PATCH 04/20] fix the query parameter escape problem. --- .../services/media/entityoperations/EntityRestProxy.java | 1 + .../services/media/implementation/ResourceLocationManager.java | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/media/entityoperations/EntityRestProxy.java b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/media/entityoperations/EntityRestProxy.java index c99273aaabf3..00366fac6f60 100644 --- a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/media/entityoperations/EntityRestProxy.java +++ b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/media/entityoperations/EntityRestProxy.java @@ -172,6 +172,7 @@ public T action(EntityTypeActionOperation entityTypeActionOperation) thro .queryParams(entityTypeActionOperation.getQueryParameters()) .accept(entityTypeActionOperation.getAcceptType()).accept(MediaType.APPLICATION_XML_TYPE) .entity(entityTypeActionOperation.getRequestContents(), MediaType.APPLICATION_XML_TYPE); + ClientResponse clientResponse = webResource.method(entityTypeActionOperation.getVerb(), ClientResponse.class); return entityTypeActionOperation.processTypeResponse(clientResponse); } diff --git a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/media/implementation/ResourceLocationManager.java b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/media/implementation/ResourceLocationManager.java index e1d4807f51a2..50c8b54a8f55 100644 --- a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/media/implementation/ResourceLocationManager.java +++ b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/media/implementation/ResourceLocationManager.java @@ -36,7 +36,7 @@ public URI getBaseURI() { public URI getRedirectedURI(URI originalURI) { UriBuilder uriBuilder = UriBuilder.fromUri(baseURI).path(originalURI.getPath()); - String queryString = originalURI.getQuery(); + String queryString = originalURI.getRawQuery(); if (queryString != null && !queryString.isEmpty()) { uriBuilder.replaceQuery(queryString); From 3b2b413e535060ba800f57d7790280f54122b925 Mon Sep 17 00:00:00 2001 From: Albert Cheng Date: Tue, 5 Mar 2013 15:49:38 -0800 Subject: [PATCH 05/20] update rebind content key unit test using X509 Certificate. --- .../implementation/content/ObjectFactory.java | 11 +++- .../content/RebindContentKeyType.java | 53 +++++++++++++++++++ .../services/media/models/ContentKey.java | 40 ++++++++++++-- .../media/ContentKeyIntegrationTest.java | 42 ++++++++++----- .../services/media/EncryptionHelper.java | 14 +++++ 5 files changed, 141 insertions(+), 19 deletions(-) create mode 100644 microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/media/implementation/content/RebindContentKeyType.java diff --git a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/media/implementation/content/ObjectFactory.java b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/media/implementation/content/ObjectFactory.java index 3769e3a1aa02..5540314a8f3b 100644 --- a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/media/implementation/content/ObjectFactory.java +++ b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/media/implementation/content/ObjectFactory.java @@ -105,11 +105,20 @@ public ContentKeyRestType createContentKeyRestType() { } /** - * Create an instance of {@link AssetFileType} + * Create an instance of {@link AssetFileType}. * * @return a new AssetFileType instance. */ public AssetFileType createAssetFileType() { return new AssetFileType(); } + + /** + * Creates a new Object object. + * + * @return the rebind content key type + */ + public RebindContentKeyType createRebindContentKeyType() { + return new RebindContentKeyType(); + } } diff --git a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/media/implementation/content/RebindContentKeyType.java b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/media/implementation/content/RebindContentKeyType.java new file mode 100644 index 000000000000..9f84670b1068 --- /dev/null +++ b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/media/implementation/content/RebindContentKeyType.java @@ -0,0 +1,53 @@ +/** + * Copyright Microsoft Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.microsoft.windowsazure.services.media.implementation.content; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlValue; + +/** + * The Class RebindContentKeyType. + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlRootElement(name = "RebindContentKey", namespace = Constants.ODATA_DATA_NS) +public class RebindContentKeyType implements MediaServiceDTO { + + /** The rebind content key. */ + @XmlValue + String rebindContentKey; + + /** + * Gets the content key. + * + * @return the content key + */ + public String getContentKey() { + return rebindContentKey; + } + + /** + * Sets the content key. + * + * @param rebindContentKey + * the new content key + */ + public void setContentKey(String rebindContentKey) { + this.rebindContentKey = rebindContentKey; + } + +} diff --git a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/media/models/ContentKey.java b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/media/models/ContentKey.java index 9b6002836cf5..6f579dd608a5 100644 --- a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/media/models/ContentKey.java +++ b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/media/models/ContentKey.java @@ -20,6 +20,12 @@ import java.net.URLEncoder; import java.security.InvalidParameterException; +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBElement; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Unmarshaller; +import javax.xml.transform.stream.StreamSource; + import com.microsoft.windowsazure.services.core.utils.pipeline.PipelineHelpers; import com.microsoft.windowsazure.services.media.entityoperations.DefaultDeleteOperation; import com.microsoft.windowsazure.services.media.entityoperations.DefaultEntityTypeActionOperation; @@ -31,6 +37,7 @@ import com.microsoft.windowsazure.services.media.entityoperations.EntityOperationSingleResultBase; import com.microsoft.windowsazure.services.media.entityoperations.EntityTypeActionOperation; import com.microsoft.windowsazure.services.media.implementation.content.ContentKeyRestType; +import com.microsoft.windowsazure.services.media.implementation.content.RebindContentKeyType; import com.sun.jersey.api.client.ClientResponse; import com.sun.jersey.api.client.GenericType; @@ -247,6 +254,9 @@ public static EntityTypeActionOperation rebind(String contentKeyId) { } private static class RebindContentKeyActionOperation extends DefaultEntityTypeActionOperation { + private final JAXBContext jaxbContext; + + private final Unmarshaller unmarshaller; public RebindContentKeyActionOperation(String contentKeyId, String x509Certificate) { super("RebindContentKey"); @@ -261,19 +271,39 @@ public RebindContentKeyActionOperation(String contentKeyId, String x509Certifica this.addQueryParameter("x509Certificate", "'" + x509Certificate + "'"); this.addQueryParameter("id", "'" + escapedContentKeyId + "'"); + try { + jaxbContext = JAXBContext.newInstance(RebindContentKeyType.class); + } + catch (JAXBException e) { + throw new RuntimeException(e); + } + + try { + unmarshaller = jaxbContext.createUnmarshaller(); + } + catch (JAXBException e) { + throw new RuntimeException(e); + } } @Override public String processTypeResponse(ClientResponse clientResponse) { PipelineHelpers.ThrowIfNotSuccess(clientResponse); - String contentKeyValue; - contentKeyValue = parseResponse(clientResponse); - return contentKeyValue; + RebindContentKeyType rebindContentKeyType = parseResponse(clientResponse); + return rebindContentKeyType.getContentKey(); } - private String parseResponse(ClientResponse clientResponse) { + private RebindContentKeyType parseResponse(ClientResponse clientResponse) { InputStream inputStream = clientResponse.getEntityInputStream(); - return inputStream.toString(); + JAXBElement rebindContentKeyTypeJaxbElement; + try { + rebindContentKeyTypeJaxbElement = unmarshaller.unmarshal(new StreamSource(inputStream), + RebindContentKeyType.class); + } + catch (JAXBException e) { + throw new RuntimeException(e); + } + return rebindContentKeyTypeJaxbElement.getValue(); } } diff --git a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/ContentKeyIntegrationTest.java b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/ContentKeyIntegrationTest.java index 5a9e160c2a6d..88fa4bd40501 100644 --- a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/ContentKeyIntegrationTest.java +++ b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/ContentKeyIntegrationTest.java @@ -17,7 +17,7 @@ import static org.junit.Assert.*; -import java.net.URLEncoder; +import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.List; import java.util.Random; @@ -39,6 +39,13 @@ public class ContentKeyIntegrationTest extends IntegrationTestBase { private final ContentKeyType testContentKeyType = ContentKeyType.CommonEncryption; private final String testEncryptedContentKey = "ThisIsEncryptedContentKey"; + private void assertByteArrayEquals(byte[] source, byte[] target) { + assertEquals(source.length, target.length); + for (int i = 0; i < source.length; i++) { + assertEquals(source[i], target[i]); + } + } + private ContentKeyInfo createTestContentKey(String contentKeyNameSuffix) throws ServiceException { String testContentKeyId = createRandomContentKeyId(); String testContentKeyName = testContentKeyPrefix + contentKeyNameSuffix; @@ -48,12 +55,8 @@ private ContentKeyInfo createTestContentKey(String contentKeyNameSuffix) throws return contentKeyInfo; } - private ContentKeyInfo createValidTestContentKey(String contentKeyNameSuffix) throws Exception { - - Random random = new Random(); - byte[] aesKey = new byte[32]; - random.nextBytes(aesKey); - + private ContentKeyInfo createValidTestContentKeyWithAesKey(String contentKeyNameSuffix, byte[] aesKey) + throws Exception { String testContnetKeyName = testContentKeyPrefix + contentKeyNameSuffix; String protectionKeyId = service.action(ProtectionKey.getProtectionKeyId(ContentKeyType.StorageEncryption)); String protectionKey = service.action(ProtectionKey.getProtectionKey(protectionKeyId)); @@ -70,7 +73,18 @@ private ContentKeyInfo createValidTestContentKey(String contentKeyNameSuffix) th .setChecksum(checksum).setProtectionKeyId(protectionKeyId).setName(testContnetKeyName)); return contentKeyInfo; + } + + private ContentKeyInfo createValidTestContentKey(String contentKeyNameSuffix) throws Exception { + byte[] aesKey = createRandomAesKey(); + return createValidTestContentKeyWithAesKey(contentKeyNameSuffix, aesKey); + } + private byte[] createRandomAesKey() { + Random random = new Random(); + byte[] aesKey = new byte[32]; + random.nextBytes(aesKey); + return aesKey; } private String createRandomContentKeyId() { @@ -254,12 +268,14 @@ public void rebindInvalidContentKeyNoX509CertificateFail() throws ServiceExcepti @Test public void rebindContentKeyWithX509CertficateSuccess() throws Exception { - ContentKeyInfo contentKeyInfo = createValidTestContentKey("rebindContentKeyWithX509Success"); - String protectionKeyId = service.action(ProtectionKey.getProtectionKeyId(ContentKeyType.StorageEncryption)); - String x509Certificate = URLEncoder.encode(service.action(ProtectionKey.getProtectionKey(protectionKeyId)), - "UTF-8"); - String rebindedContentKey = service.action(ContentKey.rebind(contentKeyInfo.getId(), x509Certificate)); - assertEquals(contentKeyInfo.getEncryptedContentKey(), rebindedContentKey); + byte[] aesKey = createRandomAesKey(); + ContentKeyInfo contentKeyInfo = createValidTestContentKeyWithAesKey("rebindContentKeyWithX509Success", aesKey); + X509Certificate x509Certificate = EncryptionHelper.loadX509Certificate("cert.pem"); + + String rebindedContentKey = service.action(ContentKey.rebind(contentKeyInfo.getId(), x509Certificate + .getPublicKey().toString())); + byte[] decryptedAesKey = EncryptionHelper.decryptSymmetricKey(rebindedContentKey, x509Certificate); + assertByteArrayEquals(aesKey, decryptedAesKey); } @Test diff --git a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/EncryptionHelper.java b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/EncryptionHelper.java index 5954e6a8cdc4..6dca69073c4f 100644 --- a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/EncryptionHelper.java +++ b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/EncryptionHelper.java @@ -16,11 +16,13 @@ package com.microsoft.windowsazure.services.media; import java.io.ByteArrayInputStream; +import java.io.FileInputStream; import java.io.InputStream; import java.security.Key; import java.security.SecureRandom; import java.security.cert.Certificate; import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; import javax.crypto.Cipher; import javax.crypto.CipherInputStream; @@ -74,4 +76,16 @@ public static InputStream encryptFile(InputStream inputStream, byte[] key, byte[ CipherInputStream cipherInputStream = new CipherInputStream(inputStream, cipher); return cipherInputStream; } + + public static byte[] decryptSymmetricKey(String rebindedContentKey, X509Certificate x509Certificate) { + return null; + } + + public static X509Certificate loadX509Certificate(String certificateFileName) throws Exception { + CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); + FileInputStream certificateInputStream = new FileInputStream(certificateFileName); + X509Certificate x509Certificate = (X509Certificate) certificateFactory + .generateCertificate(certificateInputStream); + return x509Certificate; + } } From 46d50dcd736544810a86248a2f0b177335e8fef3 Mon Sep 17 00:00:00 2001 From: Albert Cheng Date: Tue, 12 Mar 2013 17:36:49 -0700 Subject: [PATCH 06/20] repro of the client side decryption problem. --- microsoft-azure-api/pom.xml | 6 +++ .../media/ContentKeyIntegrationTest.java | 9 +++-- .../services/media/EncryptionHelper.java | 37 ++++++++++++++++--- .../media/EncryptionIntegrationTest.java | 26 ++++++++++++- 4 files changed, 68 insertions(+), 10 deletions(-) diff --git a/microsoft-azure-api/pom.xml b/microsoft-azure-api/pom.xml index c2ae959ce9d9..ad72be876819 100644 --- a/microsoft-azure-api/pom.xml +++ b/microsoft-azure-api/pom.xml @@ -104,6 +104,12 @@ commons-lang3 3.1 + + org.bouncycastle + bcprov-jdk16 + 1.46 + test + diff --git a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/ContentKeyIntegrationTest.java b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/ContentKeyIntegrationTest.java index 88fa4bd40501..a36a11599622 100644 --- a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/ContentKeyIntegrationTest.java +++ b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/ContentKeyIntegrationTest.java @@ -17,6 +17,8 @@ import static org.junit.Assert.*; +import java.net.URLEncoder; +import java.security.Security; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.List; @@ -268,12 +270,13 @@ public void rebindInvalidContentKeyNoX509CertificateFail() throws ServiceExcepti @Test public void rebindContentKeyWithX509CertficateSuccess() throws Exception { + Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); byte[] aesKey = createRandomAesKey(); ContentKeyInfo contentKeyInfo = createValidTestContentKeyWithAesKey("rebindContentKeyWithX509Success", aesKey); - X509Certificate x509Certificate = EncryptionHelper.loadX509Certificate("cert.pem"); + X509Certificate x509Certificate = EncryptionHelper.loadX509Certificate("c:\\users\\gongchen\\cert\\server.crt"); - String rebindedContentKey = service.action(ContentKey.rebind(contentKeyInfo.getId(), x509Certificate - .getPublicKey().toString())); + String rebindedContentKey = service.action(ContentKey.rebind(contentKeyInfo.getId(), + URLEncoder.encode(Base64.encode(x509Certificate.getEncoded()), "UTF-8"))); byte[] decryptedAesKey = EncryptionHelper.decryptSymmetricKey(rebindedContentKey, x509Certificate); assertByteArrayEquals(aesKey, decryptedAesKey); } diff --git a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/EncryptionHelper.java b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/EncryptionHelper.java index 6dca69073c4f..fd176eeb5158 100644 --- a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/EncryptionHelper.java +++ b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/EncryptionHelper.java @@ -19,7 +19,9 @@ import java.io.FileInputStream; import java.io.InputStream; import java.security.Key; +import java.security.Provider; import java.security.SecureRandom; +import java.security.Security; import java.security.cert.Certificate; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; @@ -45,11 +47,20 @@ public static boolean canUseStrongCrypto() { } public static byte[] encryptSymmetricKey(String protectionKey, byte[] inputData) throws Exception { - Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding"); - CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); byte[] protectionKeyBytes = Base64.decode(protectionKey); - ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(protectionKeyBytes); + return encryptSymmetricKey(protectionKeyBytes, inputData); + } + + public static byte[] encryptSymmetricKey(byte[] protectionKey, byte[] inputData) throws Exception { + CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); + ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(protectionKey); Certificate certificate = certificateFactory.generateCertificate(byteArrayInputStream); + return encryptSymmetricKey(certificate, inputData); + } + + public static byte[] encryptSymmetricKey(Certificate certificate, byte[] inputData) throws Exception { + // Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding"); + Cipher cipher = Cipher.getInstance("RSA/None/OAEPPadding", "BC"); Key publicKey = certificate.getPublicKey(); SecureRandom secureRandom = new SecureRandom(); cipher.init(Cipher.ENCRYPT_MODE, publicKey, secureRandom); @@ -77,8 +88,24 @@ public static InputStream encryptFile(InputStream inputStream, byte[] key, byte[ return cipherInputStream; } - public static byte[] decryptSymmetricKey(String rebindedContentKey, X509Certificate x509Certificate) { - return null; + public static byte[] decryptSymmetricKey(String rebindedContentKey, X509Certificate x509Certificate) + throws Exception { + for (Provider provider : Security.getProviders()) { + System.out.println(provider.getName()); + for (String key : provider.stringPropertyNames()) + System.out.println("\t" + key + "\t" + provider.getProperty(key)); + } + + byte[] rebindedContentKeyByteArray = Base64.decode(rebindedContentKey); + return decryptSymmetricKey(rebindedContentKeyByteArray, x509Certificate); + } + + public static byte[] decryptSymmetricKey(byte[] rebindedContentKey, X509Certificate x509Certificate) + throws Exception { + Cipher cipher = Cipher.getInstance("RSA/None/OAEPPadding", "BC"); + cipher.init(Cipher.DECRYPT_MODE, x509Certificate); + byte[] decrypted = cipher.doFinal(rebindedContentKey); + return decrypted; } public static X509Certificate loadX509Certificate(String certificateFileName) throws Exception { diff --git a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/EncryptionIntegrationTest.java b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/EncryptionIntegrationTest.java index 973cd91a49a4..0f745a4f9b93 100644 --- a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/EncryptionIntegrationTest.java +++ b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/EncryptionIntegrationTest.java @@ -21,6 +21,8 @@ import java.io.InputStream; import java.math.BigInteger; import java.net.URL; +import java.security.Security; +import java.security.cert.X509Certificate; import java.util.EnumSet; import java.util.List; import java.util.Random; @@ -59,7 +61,14 @@ import com.microsoft.windowsazure.services.media.models.TaskState; public class EncryptionIntegrationTest extends IntegrationTestBase { - private final String strorageDecryptionProcessor = "Storage Decryption"; + private final String storageDecryptionProcessor = "Storage Decryption"; + + private void assertByteArrayEquals(byte[] source, byte[] target) { + assertEquals(source.length, target.length); + for (int i = 0; i < source.length; i++) { + assertEquals(source[i], target[i]); + } + } @Test public void uploadAesProtectedAssetAndDownloadSuccess() throws Exception { @@ -124,9 +133,22 @@ public void uploadAesProtectedAssetAndDownloadSuccess() throws Exception { assertStreamsEqual(expected, actual); } + @Test + public void encryptedContentCanBeDecrypted() throws Exception { + Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); + Random random = new Random(); + byte[] aesKey = new byte[32]; + random.nextBytes(aesKey); + X509Certificate x509Certificate = EncryptionHelper.loadX509Certificate("c:\\users\\gongchen\\cert\\server.crt"); + byte[] encryptedContentKey = EncryptionHelper.encryptSymmetricKey(x509Certificate, aesKey); + byte[] decryptedAesKey = EncryptionHelper.decryptSymmetricKey(encryptedContentKey, x509Certificate); + + assertByteArrayEquals(aesKey, decryptedAesKey); + } + private JobInfo decodeAsset(String name, String inputAssetId) throws ServiceException, InterruptedException { MediaProcessorInfo mediaProcessorInfo = service.list( - MediaProcessor.list().set("$filter", "Name eq '" + strorageDecryptionProcessor + "'")).get(0); + MediaProcessor.list().set("$filter", "Name eq '" + storageDecryptionProcessor + "'")).get(0); String taskBody = "" + "JobInputAsset(0)JobOutputAsset(0)"; From 7692cdb46c0d4a053353b14be869e3479313dee8 Mon Sep 17 00:00:00 2001 From: Albert Cheng Date: Tue, 26 Mar 2013 18:23:19 -0700 Subject: [PATCH 07/20] add certificate creation algorithm. --- .../services/media/EncryptionHelper.java | 50 +++++++++++++- .../media/EncryptionIntegrationTest.java | 67 +++++++++++++++++-- 2 files changed, 110 insertions(+), 7 deletions(-) diff --git a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/EncryptionHelper.java b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/EncryptionHelper.java index fd176eeb5158..feaeaa7ca01f 100644 --- a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/EncryptionHelper.java +++ b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/EncryptionHelper.java @@ -18,19 +18,34 @@ import java.io.ByteArrayInputStream; import java.io.FileInputStream; import java.io.InputStream; +import java.math.BigInteger; import java.security.Key; +import java.security.KeyPair; +import java.security.PrivateKey; import java.security.Provider; import java.security.SecureRandom; import java.security.Security; import java.security.cert.Certificate; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; +import java.util.Date; import javax.crypto.Cipher; import javax.crypto.CipherInputStream; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; +import sun.security.x509.AlgorithmId; +import sun.security.x509.CertificateAlgorithmId; +import sun.security.x509.CertificateIssuerName; +import sun.security.x509.CertificateSerialNumber; +import sun.security.x509.CertificateSubjectName; +import sun.security.x509.CertificateValidity; +import sun.security.x509.CertificateVersion; +import sun.security.x509.CertificateX509Key; +import sun.security.x509.X509CertImpl; +import sun.security.x509.X509CertInfo; + import com.microsoft.windowsazure.services.core.storage.utils.Base64; class EncryptionHelper { @@ -60,7 +75,7 @@ public static byte[] encryptSymmetricKey(byte[] protectionKey, byte[] inputData) public static byte[] encryptSymmetricKey(Certificate certificate, byte[] inputData) throws Exception { // Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding"); - Cipher cipher = Cipher.getInstance("RSA/None/OAEPPadding", "BC"); + Cipher cipher = Cipher.getInstance("RSA/None/OAEPWithSHA1AndMGF1Padding", "BC"); Key publicKey = certificate.getPublicKey(); SecureRandom secureRandom = new SecureRandom(); cipher.init(Cipher.ENCRYPT_MODE, publicKey, secureRandom); @@ -102,7 +117,7 @@ public static byte[] decryptSymmetricKey(String rebindedContentKey, X509Certific public static byte[] decryptSymmetricKey(byte[] rebindedContentKey, X509Certificate x509Certificate) throws Exception { - Cipher cipher = Cipher.getInstance("RSA/None/OAEPPadding", "BC"); + Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA1AndMGF1Padding", "BC"); cipher.init(Cipher.DECRYPT_MODE, x509Certificate); byte[] decrypted = cipher.doFinal(rebindedContentKey); return decrypted; @@ -115,4 +130,35 @@ public static X509Certificate loadX509Certificate(String certificateFileName) th .generateCertificate(certificateInputStream); return x509Certificate; } + + public static X509Certificate createCertificate(String ownerName, KeyPair keyPair, int days, String algorithm) + throws Exception { + PrivateKey privkey = keyPair.getPrivate(); + X509CertInfo info = new X509CertInfo(); + Date from = new Date(); + Date to = new Date(from.getTime() + days * 86400000l); + CertificateValidity interval = new CertificateValidity(from, to); + BigInteger sn = new BigInteger(64, new SecureRandom()); + sun.security.x509.X500Name owner = new sun.security.x509.X500Name(ownerName); + + info.set(X509CertInfo.VALIDITY, interval); + info.set(X509CertInfo.SERIAL_NUMBER, new CertificateSerialNumber(sn)); + info.set(X509CertInfo.SUBJECT, new CertificateSubjectName(owner)); + info.set(X509CertInfo.ISSUER, new CertificateIssuerName(owner)); + info.set(X509CertInfo.KEY, new CertificateX509Key(keyPair.getPublic())); + info.set(X509CertInfo.VERSION, new CertificateVersion(CertificateVersion.V3)); + AlgorithmId algo = new AlgorithmId(AlgorithmId.md5WithRSAEncryption_oid); + info.set(X509CertInfo.ALGORITHM_ID, new CertificateAlgorithmId(algo)); + + // Sign the cert to identify the algorithm that's used. + X509CertImpl cert = new X509CertImpl(info); + cert.sign(privkey, algorithm); + + // Update the algorith, and resign. + algo = (AlgorithmId) cert.get(X509CertImpl.SIG_ALG); + info.set(CertificateAlgorithmId.NAME + "." + CertificateAlgorithmId.ALGORITHM, algo); + cert = new X509CertImpl(info); + cert.sign(privkey, algorithm); + return cert; + } } diff --git a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/EncryptionIntegrationTest.java b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/EncryptionIntegrationTest.java index 0f745a4f9b93..c4caaac2820b 100644 --- a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/EncryptionIntegrationTest.java +++ b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/EncryptionIntegrationTest.java @@ -21,6 +21,10 @@ import java.io.InputStream; import java.math.BigInteger; import java.net.URL; +import java.security.Key; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.SecureRandom; import java.security.Security; import java.security.cert.X509Certificate; import java.util.EnumSet; @@ -28,8 +32,11 @@ import java.util.Random; import java.util.UUID; +import javax.crypto.Cipher; + import junit.framework.Assert; +import org.junit.BeforeClass; import org.junit.Test; import com.microsoft.windowsazure.services.core.ServiceException; @@ -70,6 +77,11 @@ private void assertByteArrayEquals(byte[] source, byte[] target) { } } + @BeforeClass + public static void Setup() { + Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); + } + @Test public void uploadAesProtectedAssetAndDownloadSuccess() throws Exception { // Arrange @@ -135,17 +147,62 @@ public void uploadAesProtectedAssetAndDownloadSuccess() throws Exception { @Test public void encryptedContentCanBeDecrypted() throws Exception { - Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); - Random random = new Random(); byte[] aesKey = new byte[32]; - random.nextBytes(aesKey); + for (int i = 0; i < 32; i++) { + aesKey[i] = 1; + } + X509Certificate x509Certificate = EncryptionHelper.loadX509Certificate("c:\\users\\gongchen\\cert\\server.crt"); - byte[] encryptedContentKey = EncryptionHelper.encryptSymmetricKey(x509Certificate, aesKey); - byte[] decryptedAesKey = EncryptionHelper.decryptSymmetricKey(encryptedContentKey, x509Certificate); + Key publicKey = x509Certificate.getPublicKey(); + byte[] encryptedAesKey = EncryptionHelper.encryptSymmetricKey(x509Certificate, aesKey); + byte[] decryptedAesKey = EncryptionHelper.decryptSymmetricKey(encryptedAesKey, x509Certificate); assertByteArrayEquals(aesKey, decryptedAesKey); } + @Test + public void testEncryptionDecryptionFunction() throws Exception { + // Arrange + Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); + byte[] input = "abc".getBytes(); + Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA1AndMGF1Padding", "BC"); + SecureRandom random = new SecureRandom(); + KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA", "BC"); + generator.initialize(386, random); + KeyPair pair = generator.generateKeyPair(); + Key pubKey = pair.getPublic(); + Key privKey = pair.getPrivate(); + cipher.init(Cipher.ENCRYPT_MODE, pubKey, random); + byte[] cipherText = cipher.doFinal(input); + cipher.init(Cipher.DECRYPT_MODE, privKey); + + //Act + byte[] plainText = cipher.doFinal(cipherText); + + // Assert + assertByteArrayEquals(input, plainText); + } + + @Test + public void testGeneratedCertificateSuccess() throws Exception { + // Arrange + SecureRandom random = new SecureRandom(); + KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA", "BC"); + generator.initialize(386, random); + KeyPair keyPair = generator.generateKeyPair(); + String ownerName = "Microsoft"; + int days = 365; + String algorithm = "RSA"; + X509Certificate x509Certificate = EncryptionHelper.createCertificate(ownerName, keyPair, days, algorithm); + + // Act + String certificateString = Base64.encode(x509Certificate.getEncoded()); + + // Assert + assertNotNull(certificateString); + + } + private JobInfo decodeAsset(String name, String inputAssetId) throws ServiceException, InterruptedException { MediaProcessorInfo mediaProcessorInfo = service.list( MediaProcessor.list().set("$filter", "Name eq '" + storageDecryptionProcessor + "'")).get(0); From b8e9151d30c3558e2f4c843dc3b9c750d8646856 Mon Sep 17 00:00:00 2001 From: Albert Cheng Date: Mon, 8 Apr 2013 15:25:30 -0700 Subject: [PATCH 08/20] successful running of unit tests. --- .../media/ContentKeyIntegrationTest.java | 4 ++- .../services/media/EncryptionHelper.java | 35 ++++++++++++++----- .../media/EncryptionIntegrationTest.java | 29 ++++++++++++--- 3 files changed, 55 insertions(+), 13 deletions(-) diff --git a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/ContentKeyIntegrationTest.java b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/ContentKeyIntegrationTest.java index a36a11599622..5962bfd5bffd 100644 --- a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/ContentKeyIntegrationTest.java +++ b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/ContentKeyIntegrationTest.java @@ -18,6 +18,7 @@ import static org.junit.Assert.*; import java.net.URLEncoder; +import java.security.PrivateKey; import java.security.Security; import java.security.cert.X509Certificate; import java.util.ArrayList; @@ -274,10 +275,11 @@ public void rebindContentKeyWithX509CertficateSuccess() throws Exception { byte[] aesKey = createRandomAesKey(); ContentKeyInfo contentKeyInfo = createValidTestContentKeyWithAesKey("rebindContentKeyWithX509Success", aesKey); X509Certificate x509Certificate = EncryptionHelper.loadX509Certificate("c:\\users\\gongchen\\cert\\server.crt"); + PrivateKey privateKey = EncryptionHelper.getPrivateKey("c:\\users\\gongchen\\cert\\server.der"); String rebindedContentKey = service.action(ContentKey.rebind(contentKeyInfo.getId(), URLEncoder.encode(Base64.encode(x509Certificate.getEncoded()), "UTF-8"))); - byte[] decryptedAesKey = EncryptionHelper.decryptSymmetricKey(rebindedContentKey, x509Certificate); + byte[] decryptedAesKey = EncryptionHelper.decryptSymmetricKey(rebindedContentKey, privateKey); assertByteArrayEquals(aesKey, decryptedAesKey); } diff --git a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/EncryptionHelper.java b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/EncryptionHelper.java index feaeaa7ca01f..e3e517e2118c 100644 --- a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/EncryptionHelper.java +++ b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/EncryptionHelper.java @@ -16,10 +16,13 @@ package com.microsoft.windowsazure.services.media; import java.io.ByteArrayInputStream; +import java.io.DataInputStream; +import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.math.BigInteger; import java.security.Key; +import java.security.KeyFactory; import java.security.KeyPair; import java.security.PrivateKey; import java.security.Provider; @@ -28,6 +31,7 @@ import java.security.cert.Certificate; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; +import java.security.spec.PKCS8EncodedKeySpec; import java.util.Date; import javax.crypto.Cipher; @@ -103,8 +107,7 @@ public static InputStream encryptFile(InputStream inputStream, byte[] key, byte[ return cipherInputStream; } - public static byte[] decryptSymmetricKey(String rebindedContentKey, X509Certificate x509Certificate) - throws Exception { + public static byte[] decryptSymmetricKey(String rebindedContentKey, PrivateKey privateKey) throws Exception { for (Provider provider : Security.getProviders()) { System.out.println(provider.getName()); for (String key : provider.stringPropertyNames()) @@ -112,13 +115,12 @@ public static byte[] decryptSymmetricKey(String rebindedContentKey, X509Certific } byte[] rebindedContentKeyByteArray = Base64.decode(rebindedContentKey); - return decryptSymmetricKey(rebindedContentKeyByteArray, x509Certificate); + return decryptSymmetricKey(rebindedContentKeyByteArray, privateKey); } - public static byte[] decryptSymmetricKey(byte[] rebindedContentKey, X509Certificate x509Certificate) - throws Exception { + public static byte[] decryptSymmetricKey(byte[] rebindedContentKey, PrivateKey privateKey) throws Exception { Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA1AndMGF1Padding", "BC"); - cipher.init(Cipher.DECRYPT_MODE, x509Certificate); + cipher.init(Cipher.DECRYPT_MODE, privateKey); byte[] decrypted = cipher.doFinal(rebindedContentKey); return decrypted; } @@ -131,15 +133,18 @@ public static X509Certificate loadX509Certificate(String certificateFileName) th return x509Certificate; } - public static X509Certificate createCertificate(String ownerName, KeyPair keyPair, int days, String algorithm) + @SuppressWarnings("restriction") + public static X509Certificate createCertificate(String dn, KeyPair keyPair, int days, String algorithm) throws Exception { + + sun.security.x509.X500Name owner = new sun.security.x509.X500Name(dn); + PrivateKey privkey = keyPair.getPrivate(); X509CertInfo info = new X509CertInfo(); Date from = new Date(); Date to = new Date(from.getTime() + days * 86400000l); CertificateValidity interval = new CertificateValidity(from, to); BigInteger sn = new BigInteger(64, new SecureRandom()); - sun.security.x509.X500Name owner = new sun.security.x509.X500Name(ownerName); info.set(X509CertInfo.VALIDITY, interval); info.set(X509CertInfo.SERIAL_NUMBER, new CertificateSerialNumber(sn)); @@ -161,4 +166,18 @@ public static X509Certificate createCertificate(String ownerName, KeyPair keyPai cert.sign(privkey, algorithm); return cert; } + + public static PrivateKey getPrivateKey(String filename) throws Exception { + + File f = new File(filename); + FileInputStream fis = new FileInputStream(f); + DataInputStream dis = new DataInputStream(fis); + byte[] keyBytes = new byte[(int) f.length()]; + dis.readFully(keyBytes); + dis.close(); + + PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes); + KeyFactory kf = KeyFactory.getInstance("RSA"); + return kf.generatePrivate(spec); + } } diff --git a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/EncryptionIntegrationTest.java b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/EncryptionIntegrationTest.java index c4caaac2820b..0368c80a6a9c 100644 --- a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/EncryptionIntegrationTest.java +++ b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/EncryptionIntegrationTest.java @@ -24,6 +24,7 @@ import java.security.Key; import java.security.KeyPair; import java.security.KeyPairGenerator; +import java.security.PrivateKey; import java.security.SecureRandom; import java.security.Security; import java.security.cert.X509Certificate; @@ -153,13 +154,33 @@ public void encryptedContentCanBeDecrypted() throws Exception { } X509Certificate x509Certificate = EncryptionHelper.loadX509Certificate("c:\\users\\gongchen\\cert\\server.crt"); - Key publicKey = x509Certificate.getPublicKey(); + PrivateKey privateKey = EncryptionHelper.getPrivateKey("c:\\users\\gongchen\\cert\\server.der"); byte[] encryptedAesKey = EncryptionHelper.encryptSymmetricKey(x509Certificate, aesKey); - byte[] decryptedAesKey = EncryptionHelper.decryptSymmetricKey(encryptedAesKey, x509Certificate); + byte[] decryptedAesKey = EncryptionHelper.decryptSymmetricKey(encryptedAesKey, privateKey); assertByteArrayEquals(aesKey, decryptedAesKey); } + @Test + public void testEncryptedContentCanBeDecrypted() throws Exception { + Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); + byte[] input = "abc".getBytes(); + Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA1AndMGF1Padding", "BC"); + SecureRandom random = new SecureRandom(); + X509Certificate x509Certificate = EncryptionHelper.loadX509Certificate("c:\\users\\gongchen\\cert\\server.crt"); + PrivateKey privateKey = EncryptionHelper.getPrivateKey("c:\\users\\gongchen\\cert\\server.der"); + Key pubKey = x509Certificate.getPublicKey(); + cipher.init(Cipher.ENCRYPT_MODE, pubKey, random); + byte[] cipherText = cipher.doFinal(input); + cipher.init(Cipher.DECRYPT_MODE, privateKey); + + //Act + byte[] plainText = cipher.doFinal(cipherText); + + // Assert + assertByteArrayEquals(input, plainText); + } + @Test public void testEncryptionDecryptionFunction() throws Exception { // Arrange @@ -188,9 +209,9 @@ public void testGeneratedCertificateSuccess() throws Exception { // Arrange SecureRandom random = new SecureRandom(); KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA", "BC"); - generator.initialize(386, random); + generator.initialize(256, random); KeyPair keyPair = generator.generateKeyPair(); - String ownerName = "Microsoft"; + String ownerName = "EMAILADDRESS=gongchen@microsoft,CN=Albert Cheng,OU=Windows Azure,O=\"Microsoft\",L=Redmond,ST=WA,C=US"; int days = 365; String algorithm = "RSA"; X509Certificate x509Certificate = EncryptionHelper.createCertificate(ownerName, keyPair, days, algorithm); From 40af700271cd9d4179cc617e71915779923227df Mon Sep 17 00:00:00 2001 From: Albert Cheng Date: Mon, 8 Apr 2013 16:11:16 -0700 Subject: [PATCH 09/20] remove the hard coded dependency on certificate. --- .../media/ContentKeyIntegrationTest.java | 7 ++- .../services/media/EncryptionHelper.java | 48 ------------------ .../media/EncryptionIntegrationTest.java | 33 +++--------- .../src/test/resources/certificate/server.crt | 17 +++++++ .../src/test/resources/certificate/server.der | Bin 0 -> 634 bytes 5 files changed, 30 insertions(+), 75 deletions(-) create mode 100644 microsoft-azure-api/src/test/resources/certificate/server.crt create mode 100644 microsoft-azure-api/src/test/resources/certificate/server.der diff --git a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/ContentKeyIntegrationTest.java b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/ContentKeyIntegrationTest.java index 5962bfd5bffd..7388fe5cbf48 100644 --- a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/ContentKeyIntegrationTest.java +++ b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/ContentKeyIntegrationTest.java @@ -17,6 +17,7 @@ import static org.junit.Assert.*; +import java.net.URL; import java.net.URLEncoder; import java.security.PrivateKey; import java.security.Security; @@ -274,8 +275,10 @@ public void rebindContentKeyWithX509CertficateSuccess() throws Exception { Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); byte[] aesKey = createRandomAesKey(); ContentKeyInfo contentKeyInfo = createValidTestContentKeyWithAesKey("rebindContentKeyWithX509Success", aesKey); - X509Certificate x509Certificate = EncryptionHelper.loadX509Certificate("c:\\users\\gongchen\\cert\\server.crt"); - PrivateKey privateKey = EncryptionHelper.getPrivateKey("c:\\users\\gongchen\\cert\\server.der"); + URL serverCertificateUri = getClass().getResource("/certificate/server.crt"); + X509Certificate x509Certificate = EncryptionHelper.loadX509Certificate(serverCertificateUri.getFile()); + URL serverPrivateKey = getClass().getResource("/certificate/server.der"); + PrivateKey privateKey = EncryptionHelper.getPrivateKey(serverPrivateKey.getFile()); String rebindedContentKey = service.action(ContentKey.rebind(contentKeyInfo.getId(), URLEncoder.encode(Base64.encode(x509Certificate.getEncoded()), "UTF-8"))); diff --git a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/EncryptionHelper.java b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/EncryptionHelper.java index e3e517e2118c..f564456a666d 100644 --- a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/EncryptionHelper.java +++ b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/EncryptionHelper.java @@ -20,10 +20,8 @@ import java.io.File; import java.io.FileInputStream; import java.io.InputStream; -import java.math.BigInteger; import java.security.Key; import java.security.KeyFactory; -import java.security.KeyPair; import java.security.PrivateKey; import java.security.Provider; import java.security.SecureRandom; @@ -32,24 +30,12 @@ import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.security.spec.PKCS8EncodedKeySpec; -import java.util.Date; import javax.crypto.Cipher; import javax.crypto.CipherInputStream; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; -import sun.security.x509.AlgorithmId; -import sun.security.x509.CertificateAlgorithmId; -import sun.security.x509.CertificateIssuerName; -import sun.security.x509.CertificateSerialNumber; -import sun.security.x509.CertificateSubjectName; -import sun.security.x509.CertificateValidity; -import sun.security.x509.CertificateVersion; -import sun.security.x509.CertificateX509Key; -import sun.security.x509.X509CertImpl; -import sun.security.x509.X509CertInfo; - import com.microsoft.windowsazure.services.core.storage.utils.Base64; class EncryptionHelper { @@ -133,40 +119,6 @@ public static X509Certificate loadX509Certificate(String certificateFileName) th return x509Certificate; } - @SuppressWarnings("restriction") - public static X509Certificate createCertificate(String dn, KeyPair keyPair, int days, String algorithm) - throws Exception { - - sun.security.x509.X500Name owner = new sun.security.x509.X500Name(dn); - - PrivateKey privkey = keyPair.getPrivate(); - X509CertInfo info = new X509CertInfo(); - Date from = new Date(); - Date to = new Date(from.getTime() + days * 86400000l); - CertificateValidity interval = new CertificateValidity(from, to); - BigInteger sn = new BigInteger(64, new SecureRandom()); - - info.set(X509CertInfo.VALIDITY, interval); - info.set(X509CertInfo.SERIAL_NUMBER, new CertificateSerialNumber(sn)); - info.set(X509CertInfo.SUBJECT, new CertificateSubjectName(owner)); - info.set(X509CertInfo.ISSUER, new CertificateIssuerName(owner)); - info.set(X509CertInfo.KEY, new CertificateX509Key(keyPair.getPublic())); - info.set(X509CertInfo.VERSION, new CertificateVersion(CertificateVersion.V3)); - AlgorithmId algo = new AlgorithmId(AlgorithmId.md5WithRSAEncryption_oid); - info.set(X509CertInfo.ALGORITHM_ID, new CertificateAlgorithmId(algo)); - - // Sign the cert to identify the algorithm that's used. - X509CertImpl cert = new X509CertImpl(info); - cert.sign(privkey, algorithm); - - // Update the algorith, and resign. - algo = (AlgorithmId) cert.get(X509CertImpl.SIG_ALG); - info.set(CertificateAlgorithmId.NAME + "." + CertificateAlgorithmId.ALGORITHM, algo); - cert = new X509CertImpl(info); - cert.sign(privkey, algorithm); - return cert; - } - public static PrivateKey getPrivateKey(String filename) throws Exception { File f = new File(filename); diff --git a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/EncryptionIntegrationTest.java b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/EncryptionIntegrationTest.java index 0368c80a6a9c..d883fd935428 100644 --- a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/EncryptionIntegrationTest.java +++ b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/EncryptionIntegrationTest.java @@ -152,9 +152,10 @@ public void encryptedContentCanBeDecrypted() throws Exception { for (int i = 0; i < 32; i++) { aesKey[i] = 1; } - - X509Certificate x509Certificate = EncryptionHelper.loadX509Certificate("c:\\users\\gongchen\\cert\\server.crt"); - PrivateKey privateKey = EncryptionHelper.getPrivateKey("c:\\users\\gongchen\\cert\\server.der"); + URL serverCertificateUri = getClass().getResource("/certificate/server.crt"); + X509Certificate x509Certificate = EncryptionHelper.loadX509Certificate(serverCertificateUri.getFile()); + URL serverPrivateKey = getClass().getResource("/certificate/server.der"); + PrivateKey privateKey = EncryptionHelper.getPrivateKey(serverPrivateKey.getFile()); byte[] encryptedAesKey = EncryptionHelper.encryptSymmetricKey(x509Certificate, aesKey); byte[] decryptedAesKey = EncryptionHelper.decryptSymmetricKey(encryptedAesKey, privateKey); @@ -167,8 +168,10 @@ public void testEncryptedContentCanBeDecrypted() throws Exception { byte[] input = "abc".getBytes(); Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA1AndMGF1Padding", "BC"); SecureRandom random = new SecureRandom(); - X509Certificate x509Certificate = EncryptionHelper.loadX509Certificate("c:\\users\\gongchen\\cert\\server.crt"); - PrivateKey privateKey = EncryptionHelper.getPrivateKey("c:\\users\\gongchen\\cert\\server.der"); + URL serverCertificateUri = getClass().getResource("/certificate/server.crt"); + X509Certificate x509Certificate = EncryptionHelper.loadX509Certificate(serverCertificateUri.getFile()); + URL serverPrivateKey = getClass().getResource("/certificate/server.der"); + PrivateKey privateKey = EncryptionHelper.getPrivateKey(serverPrivateKey.getFile()); Key pubKey = x509Certificate.getPublicKey(); cipher.init(Cipher.ENCRYPT_MODE, pubKey, random); byte[] cipherText = cipher.doFinal(input); @@ -204,26 +207,6 @@ public void testEncryptionDecryptionFunction() throws Exception { assertByteArrayEquals(input, plainText); } - @Test - public void testGeneratedCertificateSuccess() throws Exception { - // Arrange - SecureRandom random = new SecureRandom(); - KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA", "BC"); - generator.initialize(256, random); - KeyPair keyPair = generator.generateKeyPair(); - String ownerName = "EMAILADDRESS=gongchen@microsoft,CN=Albert Cheng,OU=Windows Azure,O=\"Microsoft\",L=Redmond,ST=WA,C=US"; - int days = 365; - String algorithm = "RSA"; - X509Certificate x509Certificate = EncryptionHelper.createCertificate(ownerName, keyPair, days, algorithm); - - // Act - String certificateString = Base64.encode(x509Certificate.getEncoded()); - - // Assert - assertNotNull(certificateString); - - } - private JobInfo decodeAsset(String name, String inputAssetId) throws ServiceException, InterruptedException { MediaProcessorInfo mediaProcessorInfo = service.list( MediaProcessor.list().set("$filter", "Name eq '" + storageDecryptionProcessor + "'")).get(0); diff --git a/microsoft-azure-api/src/test/resources/certificate/server.crt b/microsoft-azure-api/src/test/resources/certificate/server.crt new file mode 100644 index 000000000000..45100ad45f50 --- /dev/null +++ b/microsoft-azure-api/src/test/resources/certificate/server.crt @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE----- +MIICqTCCAhICCQDm00hjjGf/ITANBgkqhkiG9w0BAQUFADCBlzELMAkGA1UEBhMC +VVMxCzAJBgNVBAgTAldBMRAwDgYDVQQHEwdSZWRtb25kMRMwEQYDVQQKEwpNaWNy +b3NvZnQgMRYwFAYDVQQLEw1XaW5kb3dzIEF6dXJlMRUwEwYDVQQDEwxBbGJlcnQg +Q2hlbmcxJTAjBgkqhkiG9w0BCQEWFmdvbmdjaGVuQG1pY3Jvc29mdC5jb20wIBcN +MTMwMzA2MDAxNzU2WhgPMTkyMzA5MTIyMDMxNDhaMIGXMQswCQYDVQQGEwJVUzEL +MAkGA1UECBMCV0ExEDAOBgNVBAcTB1JlZG1vbmQxEzARBgNVBAoTCk1pY3Jvc29m +dCAxFjAUBgNVBAsTDVdpbmRvd3MgQXp1cmUxFTATBgNVBAMTDEFsYmVydCBDaGVu +ZzElMCMGCSqGSIb3DQEJARYWZ29uZ2NoZW5AbWljcm9zb2Z0LmNvbTCBnzANBgkq +hkiG9w0BAQEFAAOBjQAwgYkCgYEAxct8f2TOECFGtZs5zJN9Vmtk6Jeo2ThbJ8XO +0GPgjKjfLGUgUHrUKSpaHrObaEQWtU9/qdCmctHep5veulGApZ6cBKjL+7xKGQfP +KHq+6nsvF2EutZenPvsFDb7msezcT+Ut1yMnCUd9sjcD0/g2AO5CpplnUR7MOIaq +j/ifsNMCAwEAATANBgkqhkiG9w0BAQUFAAOBgQCAY9QBsXtEfDTZ6Gmplkd2mGGf +aFxRXtEtEXxBrEjhq3c2F8le1ZpMysWfmgpsImZODf3rPN5+UMmL2cqxF0RU+7kG +qPSo8egxg33IfEMTeH5WYHr8ilOxBfw25nnUGr7Cym0m0JAmh5xR47vmEb/EHIXf +iFKpK6o4bjjnszUV2g== +-----END CERTIFICATE----- diff --git a/microsoft-azure-api/src/test/resources/certificate/server.der b/microsoft-azure-api/src/test/resources/certificate/server.der new file mode 100644 index 0000000000000000000000000000000000000000..e34ad905b9f157d99e71e21fea545291d011b2a7 GIT binary patch literal 634 zcmV-=0)_oBf&z8|0RS)!1_>&LNQUpU@(FLTmk_A0)c@5#mjtuWX=#F zMzxzc%#(dqYh>t`sM$DMC&kXtW8jRa-z;SyP10oo}2`z%lo`a83)fOdcNv=FBf4hwU?(p`vncY=CSPDPvtGwBPR(*eX=(L)A%+3 z?n0)SXHg!^IEJc^_@A)T0s{d60Rn-51N+@R6QQ!e%8>F{d`5i_cQWaFWS#8+C>6QXiN{cQxvN%?UEo$;_KEexd9k~9-C$?ZG{6I5 zqt6%l`e$zfa8B7DX+?rsJFO5rX&H`Zh)d8Vj2MN@!M~iZ*aT0zm-e z{U6|uDW4S{Nc$W4{COoL{$Di82tI8Ibv%1i=Z^#q!Q;Qi=@Pom$q`swAD>6#Fx=JU z5kcSX{qLyToQ>ZCK>*z#V!|TI=p{_-U7(NHBR=d%UfcT3GAC%qWd7Etho~9$PkW3xQqizkw2Cae>V6SEe(KjJy0zeUzGlj2mW(FY{ zVNeubU|sGu%A>wYb;LkMDYrnSOxcD;*oPc7u_>?+Jp*2j`068y7%M UN!}B-i;Qe13_leN%CIL3vQ8Hx+W-In literal 0 HcmV?d00001 From 97ec64c388c6f4f1d97bb1382ff82b453a9d64d7 Mon Sep 17 00:00:00 2001 From: Albert Cheng Date: Mon, 8 Apr 2013 18:10:34 -0700 Subject: [PATCH 10/20] Remove un-used code, get unit tests passed. --- .../media/ContentKeyIntegrationTest.java | 2 +- .../services/media/EncryptionHelper.java | 55 +++++++++++-------- .../media/EncryptionIntegrationTest.java | 4 +- 3 files changed, 36 insertions(+), 25 deletions(-) diff --git a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/ContentKeyIntegrationTest.java b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/ContentKeyIntegrationTest.java index 7388fe5cbf48..6c40d16a0ad7 100644 --- a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/ContentKeyIntegrationTest.java +++ b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/ContentKeyIntegrationTest.java @@ -263,7 +263,7 @@ public void rebindContentKeyNoX509CertificateSuccess() throws Exception { @Test public void rebindInvalidContentKeyNoX509CertificateFail() throws ServiceException { expectedException.expect(ServiceException.class); - expectedException.expect(new ServiceExceptionMatcher(500)); + expectedException.expect(new ServiceExceptionMatcher(400)); ContentKeyInfo contentKeyInfo = createTestContentKey("rebindInvalidContentKeyNoX509Fail"); service.action(ContentKey.rebind(contentKeyInfo.getId())); diff --git a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/EncryptionHelper.java b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/EncryptionHelper.java index f564456a666d..8c1fd55e2b8e 100644 --- a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/EncryptionHelper.java +++ b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/EncryptionHelper.java @@ -23,9 +23,7 @@ import java.security.Key; import java.security.KeyFactory; import java.security.PrivateKey; -import java.security.Provider; import java.security.SecureRandom; -import java.security.Security; import java.security.cert.Certificate; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; @@ -93,25 +91,35 @@ public static InputStream encryptFile(InputStream inputStream, byte[] key, byte[ return cipherInputStream; } - public static byte[] decryptSymmetricKey(String rebindedContentKey, PrivateKey privateKey) throws Exception { - for (Provider provider : Security.getProviders()) { - System.out.println(provider.getName()); - for (String key : provider.stringPropertyNames()) - System.out.println("\t" + key + "\t" + provider.getProperty(key)); + public static byte[] decryptSymmetricKey(String encryptedContent, PrivateKey privateKey) throws Exception { + byte[] encryptedContentByteArray = Base64.decode(encryptedContent); + return decryptSymmetricKey(encryptedContentByteArray, privateKey); + } + + public static byte[] decryptSymmetricKey(byte[] encryptedContent, PrivateKey privateKey) throws Exception { + if (encryptedContent == null) { + throw new IllegalArgumentException("The encrypted content cannot be null."); } - byte[] rebindedContentKeyByteArray = Base64.decode(rebindedContentKey); - return decryptSymmetricKey(rebindedContentKeyByteArray, privateKey); - } + if (encryptedContent.length == 0) { + throw new IllegalArgumentException("The encrypted content cannot be empty."); + } + + if (privateKey == null) { + throw new IllegalArgumentException("The private key cannot be null."); + } - public static byte[] decryptSymmetricKey(byte[] rebindedContentKey, PrivateKey privateKey) throws Exception { Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA1AndMGF1Padding", "BC"); cipher.init(Cipher.DECRYPT_MODE, privateKey); - byte[] decrypted = cipher.doFinal(rebindedContentKey); + byte[] decrypted = cipher.doFinal(encryptedContent); return decrypted; } public static X509Certificate loadX509Certificate(String certificateFileName) throws Exception { + if ((certificateFileName == null) || certificateFileName.isEmpty()) { + throw new IllegalArgumentException("certificate file name cannot be null or empty."); + } + CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); FileInputStream certificateInputStream = new FileInputStream(certificateFileName); X509Certificate x509Certificate = (X509Certificate) certificateFactory @@ -119,17 +127,20 @@ public static X509Certificate loadX509Certificate(String certificateFileName) th return x509Certificate; } - public static PrivateKey getPrivateKey(String filename) throws Exception { + public static PrivateKey getPrivateKey(String certificateFileName) throws Exception { + if ((certificateFileName == null) || certificateFileName.isEmpty()) { + throw new IllegalArgumentException("certificate file name cannot be null or empty."); + } - File f = new File(filename); - FileInputStream fis = new FileInputStream(f); - DataInputStream dis = new DataInputStream(fis); - byte[] keyBytes = new byte[(int) f.length()]; - dis.readFully(keyBytes); - dis.close(); + File file = new File(certificateFileName); + FileInputStream fis = new FileInputStream(file); + DataInputStream dataInputStream = new DataInputStream(fis); + byte[] keyBytes = new byte[(int) file.length()]; + dataInputStream.readFully(keyBytes); + dataInputStream.close(); - PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes); - KeyFactory kf = KeyFactory.getInstance("RSA"); - return kf.generatePrivate(spec); + PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(keyBytes); + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + return keyFactory.generatePrivate(pkcs8EncodedKeySpec); } } diff --git a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/EncryptionIntegrationTest.java b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/EncryptionIntegrationTest.java index d883fd935428..b4ca462e65bd 100644 --- a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/EncryptionIntegrationTest.java +++ b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/EncryptionIntegrationTest.java @@ -163,7 +163,7 @@ public void encryptedContentCanBeDecrypted() throws Exception { } @Test - public void testEncryptedContentCanBeDecrypted() throws Exception { + public void testEncryptedContentCanBeDecryptedUsingPreGeneratedKeyPair() throws Exception { Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); byte[] input = "abc".getBytes(); Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA1AndMGF1Padding", "BC"); @@ -185,7 +185,7 @@ public void testEncryptedContentCanBeDecrypted() throws Exception { } @Test - public void testEncryptionDecryptionFunction() throws Exception { + public void testEncryptionDecryptionFunctionUsingGeneratedKeyPair() throws Exception { // Arrange Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); byte[] input = "abc".getBytes(); From 7a47f343a953fa2a8352c403c851d887182f8d19 Mon Sep 17 00:00:00 2001 From: Albert Cheng Date: Mon, 8 Apr 2013 18:24:02 -0700 Subject: [PATCH 11/20] Remove unused comments. --- .../microsoft/windowsazure/services/media/EncryptionHelper.java | 1 - 1 file changed, 1 deletion(-) diff --git a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/EncryptionHelper.java b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/EncryptionHelper.java index 8c1fd55e2b8e..823163c9bbf0 100644 --- a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/EncryptionHelper.java +++ b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/EncryptionHelper.java @@ -62,7 +62,6 @@ public static byte[] encryptSymmetricKey(byte[] protectionKey, byte[] inputData) } public static byte[] encryptSymmetricKey(Certificate certificate, byte[] inputData) throws Exception { - // Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding"); Cipher cipher = Cipher.getInstance("RSA/None/OAEPWithSHA1AndMGF1Padding", "BC"); Key publicKey = certificate.getPublicKey(); SecureRandom secureRandom = new SecureRandom(); From dd2a0d3b35c09b2604e32311edf1d5bae80a8c69 Mon Sep 17 00:00:00 2001 From: Albert Cheng Date: Tue, 9 Apr 2013 14:41:25 -0700 Subject: [PATCH 12/20] address code review feedback. --- .../implementation/content/ObjectFactory.java | 4 ++-- .../services/media/ContentKeyIntegrationTest.java | 14 ++++++++------ .../services/media/EncryptionIntegrationTest.java | 2 -- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/media/implementation/content/ObjectFactory.java b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/media/implementation/content/ObjectFactory.java index 5540314a8f3b..3aa8d5bfbfe9 100644 --- a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/media/implementation/content/ObjectFactory.java +++ b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/media/implementation/content/ObjectFactory.java @@ -114,9 +114,9 @@ public AssetFileType createAssetFileType() { } /** - * Creates a new Object object. + * Creates an instance of (@link RebindContentKeyType). * - * @return the rebind content key type + * @return the rebind content key type instance. */ public RebindContentKeyType createRebindContentKeyType() { return new RebindContentKeyType(); diff --git a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/ContentKeyIntegrationTest.java b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/ContentKeyIntegrationTest.java index 6c40d16a0ad7..f2fb697ad98c 100644 --- a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/ContentKeyIntegrationTest.java +++ b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/ContentKeyIntegrationTest.java @@ -24,7 +24,6 @@ import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.List; -import java.util.Random; import java.util.UUID; import org.junit.Test; @@ -80,14 +79,17 @@ private ContentKeyInfo createValidTestContentKeyWithAesKey(String contentKeyName } private ContentKeyInfo createValidTestContentKey(String contentKeyNameSuffix) throws Exception { - byte[] aesKey = createRandomAesKey(); + byte[] aesKey = createTestAesKey(); return createValidTestContentKeyWithAesKey(contentKeyNameSuffix, aesKey); } - private byte[] createRandomAesKey() { - Random random = new Random(); + private byte[] createTestAesKey() { byte[] aesKey = new byte[32]; - random.nextBytes(aesKey); + int i; + for (i = 0; i < 32; i++) { + aesKey[i] = 1; + } + return aesKey; } @@ -273,7 +275,7 @@ public void rebindInvalidContentKeyNoX509CertificateFail() throws ServiceExcepti @Test public void rebindContentKeyWithX509CertficateSuccess() throws Exception { Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); - byte[] aesKey = createRandomAesKey(); + byte[] aesKey = createTestAesKey(); ContentKeyInfo contentKeyInfo = createValidTestContentKeyWithAesKey("rebindContentKeyWithX509Success", aesKey); URL serverCertificateUri = getClass().getResource("/certificate/server.crt"); X509Certificate x509Certificate = EncryptionHelper.loadX509Certificate(serverCertificateUri.getFile()); diff --git a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/EncryptionIntegrationTest.java b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/EncryptionIntegrationTest.java index b4ca462e65bd..8842ee00ac22 100644 --- a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/EncryptionIntegrationTest.java +++ b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/EncryptionIntegrationTest.java @@ -164,7 +164,6 @@ public void encryptedContentCanBeDecrypted() throws Exception { @Test public void testEncryptedContentCanBeDecryptedUsingPreGeneratedKeyPair() throws Exception { - Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); byte[] input = "abc".getBytes(); Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA1AndMGF1Padding", "BC"); SecureRandom random = new SecureRandom(); @@ -187,7 +186,6 @@ public void testEncryptedContentCanBeDecryptedUsingPreGeneratedKeyPair() throws @Test public void testEncryptionDecryptionFunctionUsingGeneratedKeyPair() throws Exception { // Arrange - Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); byte[] input = "abc".getBytes(); Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA1AndMGF1Padding", "BC"); SecureRandom random = new SecureRandom(); From 5e17ffdf6fc16e66e0d2b5e79a4defbc73a69813 Mon Sep 17 00:00:00 2001 From: Albert Cheng Date: Wed, 10 Apr 2013 14:28:23 -0700 Subject: [PATCH 13/20] allow space in file path for unit test files. --- .../services/media/ContentKeyIntegrationTest.java | 6 ++++++ .../services/media/EncryptionIntegrationTest.java | 13 ++++++++----- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/ContentKeyIntegrationTest.java b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/ContentKeyIntegrationTest.java index f2fb697ad98c..a8b4f3cab0cb 100644 --- a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/ContentKeyIntegrationTest.java +++ b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/ContentKeyIntegrationTest.java @@ -26,6 +26,7 @@ import java.util.List; import java.util.UUID; +import org.junit.BeforeClass; import org.junit.Test; import com.microsoft.windowsazure.services.core.ServiceException; @@ -118,6 +119,11 @@ private void verifyContentKeyProperties(String message, String id, ContentKeyTyp assertEquals(message + " Checksum", checksum, actual.getChecksum()); } + @BeforeClass + public static void Setup() { + Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); + } + @Test public void canCreateContentKey() throws Exception { // Arrange diff --git a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/EncryptionIntegrationTest.java b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/EncryptionIntegrationTest.java index 8842ee00ac22..2c259df74b9f 100644 --- a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/EncryptionIntegrationTest.java +++ b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/EncryptionIntegrationTest.java @@ -21,6 +21,7 @@ import java.io.InputStream; import java.math.BigInteger; import java.net.URL; +import java.net.URLDecoder; import java.security.Key; import java.security.KeyPair; import java.security.KeyPairGenerator; @@ -147,15 +148,16 @@ public void uploadAesProtectedAssetAndDownloadSuccess() throws Exception { } @Test - public void encryptedContentCanBeDecrypted() throws Exception { + public void testEncryptedContentCanBeDecrypted() throws Exception { byte[] aesKey = new byte[32]; for (int i = 0; i < 32; i++) { aesKey[i] = 1; } URL serverCertificateUri = getClass().getResource("/certificate/server.crt"); - X509Certificate x509Certificate = EncryptionHelper.loadX509Certificate(serverCertificateUri.getFile()); + X509Certificate x509Certificate = EncryptionHelper.loadX509Certificate(URLDecoder.decode( + serverCertificateUri.getFile(), "UTF-8")); URL serverPrivateKey = getClass().getResource("/certificate/server.der"); - PrivateKey privateKey = EncryptionHelper.getPrivateKey(serverPrivateKey.getFile()); + PrivateKey privateKey = EncryptionHelper.getPrivateKey(URLDecoder.decode(serverPrivateKey.getFile(), "UTF-8")); byte[] encryptedAesKey = EncryptionHelper.encryptSymmetricKey(x509Certificate, aesKey); byte[] decryptedAesKey = EncryptionHelper.decryptSymmetricKey(encryptedAesKey, privateKey); @@ -168,9 +170,10 @@ public void testEncryptedContentCanBeDecryptedUsingPreGeneratedKeyPair() throws Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA1AndMGF1Padding", "BC"); SecureRandom random = new SecureRandom(); URL serverCertificateUri = getClass().getResource("/certificate/server.crt"); - X509Certificate x509Certificate = EncryptionHelper.loadX509Certificate(serverCertificateUri.getFile()); + X509Certificate x509Certificate = EncryptionHelper.loadX509Certificate(URLDecoder.decode( + serverCertificateUri.getFile(), "UTF-8")); URL serverPrivateKey = getClass().getResource("/certificate/server.der"); - PrivateKey privateKey = EncryptionHelper.getPrivateKey(serverPrivateKey.getFile()); + PrivateKey privateKey = EncryptionHelper.getPrivateKey(URLDecoder.decode(serverPrivateKey.getFile(), "UTF-8")); Key pubKey = x509Certificate.getPublicKey(); cipher.init(Cipher.ENCRYPT_MODE, pubKey, random); byte[] cipherText = cipher.doFinal(input); From e745872649f8be6b9da7edcb450ea4106f86be90 Mon Sep 17 00:00:00 2001 From: Albert Cheng Date: Thu, 11 Apr 2013 10:53:11 -0700 Subject: [PATCH 14/20] ensure the tests can be passed on machines with space in directory name. --- .../services/media/ContentKeyIntegrationTest.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/ContentKeyIntegrationTest.java b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/ContentKeyIntegrationTest.java index a8b4f3cab0cb..c195aad9320f 100644 --- a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/ContentKeyIntegrationTest.java +++ b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/ContentKeyIntegrationTest.java @@ -18,6 +18,7 @@ import static org.junit.Assert.*; import java.net.URL; +import java.net.URLDecoder; import java.net.URLEncoder; import java.security.PrivateKey; import java.security.Security; @@ -284,9 +285,10 @@ public void rebindContentKeyWithX509CertficateSuccess() throws Exception { byte[] aesKey = createTestAesKey(); ContentKeyInfo contentKeyInfo = createValidTestContentKeyWithAesKey("rebindContentKeyWithX509Success", aesKey); URL serverCertificateUri = getClass().getResource("/certificate/server.crt"); - X509Certificate x509Certificate = EncryptionHelper.loadX509Certificate(serverCertificateUri.getFile()); + X509Certificate x509Certificate = EncryptionHelper.loadX509Certificate(URLDecoder.decode( + serverCertificateUri.getFile(), "UTF-8")); URL serverPrivateKey = getClass().getResource("/certificate/server.der"); - PrivateKey privateKey = EncryptionHelper.getPrivateKey(serverPrivateKey.getFile()); + PrivateKey privateKey = EncryptionHelper.getPrivateKey(URLDecoder.decode(serverPrivateKey.getFile(), "UTF-8")); String rebindedContentKey = service.action(ContentKey.rebind(contentKeyInfo.getId(), URLEncoder.encode(Base64.encode(x509Certificate.getEncoded()), "UTF-8"))); From 3e44038e61fcbdea010d7c5ff68b18a1059b9972 Mon Sep 17 00:00:00 2001 From: Albert Cheng Date: Thu, 11 Apr 2013 20:18:39 -0700 Subject: [PATCH 15/20] Exposing error message body for UniformInterfaceException --- .../core/utils/pipeline/PipelineHelpers.java | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/core/utils/pipeline/PipelineHelpers.java b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/core/utils/pipeline/PipelineHelpers.java index c022ab06a799..8aca9db4c693 100644 --- a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/core/utils/pipeline/PipelineHelpers.java +++ b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/core/utils/pipeline/PipelineHelpers.java @@ -27,17 +27,29 @@ import com.sun.jersey.api.client.WebResource.Builder; public class PipelineHelpers { + + private static String createErrorMessage(ClientResponse clientResponse) { + clientResponse.bufferEntity(); + String errorMessage = clientResponse.toString(); + if (clientResponse.hasEntity()) { + errorMessage = errorMessage + " " + clientResponse.getEntity(String.class); + } + return errorMessage; + } + public static void ThrowIfNotSuccess(ClientResponse clientResponse) { int statusCode = clientResponse.getStatus(); if ((statusCode < 200) || (statusCode >= 300)) { - throw new UniformInterfaceException(clientResponse); + String errorMessage = createErrorMessage(clientResponse); + throw new UniformInterfaceException(errorMessage, clientResponse); } } public static void ThrowIfError(ClientResponse clientResponse) { if (clientResponse.getStatus() >= 400) { - throw new UniformInterfaceException(clientResponse); + String errorMessage = createErrorMessage(clientResponse); + throw new UniformInterfaceException(errorMessage, clientResponse); } } From 4975d237292b3dfbdd1f03a41e689965caa146ea Mon Sep 17 00:00:00 2001 From: Albert Cheng Date: Fri, 19 Apr 2013 16:05:13 -0700 Subject: [PATCH 16/20] fix empty queue scenario. --- .../implementation/ServiceBusRestProxy.java | 4 ++ .../serviceBus/ServiceBusIntegrationTest.java | 46 ++++++++++++++++++- 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/serviceBus/implementation/ServiceBusRestProxy.java b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/serviceBus/implementation/ServiceBusRestProxy.java index 86a104fcaead..633c98336292 100644 --- a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/serviceBus/implementation/ServiceBusRestProxy.java +++ b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/serviceBus/implementation/ServiceBusRestProxy.java @@ -183,6 +183,10 @@ else if (options.isPeekLock()) { throw new RuntimeException("Unknown ReceiveMode"); } + if (clientResult.getStatus() == 204) { + return null; + } + BrokerProperties brokerProperties; if (clientResult.getHeaders().containsKey("BrokerProperties")) { brokerProperties = mapper.fromString(clientResult.getHeaders().getFirst("BrokerProperties")); diff --git a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/serviceBus/ServiceBusIntegrationTest.java b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/serviceBus/ServiceBusIntegrationTest.java index c09af13305b8..a4b414600ab8 100644 --- a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/serviceBus/ServiceBusIntegrationTest.java +++ b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/serviceBus/ServiceBusIntegrationTest.java @@ -47,6 +47,7 @@ import com.microsoft.windowsazure.services.serviceBus.models.ListTopicsResult; import com.microsoft.windowsazure.services.serviceBus.models.QueueInfo; import com.microsoft.windowsazure.services.serviceBus.models.ReceiveMessageOptions; +import com.microsoft.windowsazure.services.serviceBus.models.ReceiveQueueMessageResult; import com.microsoft.windowsazure.services.serviceBus.models.RuleInfo; import com.microsoft.windowsazure.services.serviceBus.models.SubscriptionInfo; import com.microsoft.windowsazure.services.serviceBus.models.TopicInfo; @@ -172,6 +173,21 @@ public void receiveMessageWorks() throws Exception { assertArrayEquals("Hello World".getBytes(), Arrays.copyOf(data, size)); } + @Test + public void receiveMessageEmptyQueueWorks() throws Exception { + // Arrange + String queueName = "TestReceiveMessageEmptyQueueWorks"; + service.createQueue(new QueueInfo(queueName)); + + // Act + ReceiveQueueMessageResult receiveQueueMessageResult = service.receiveQueueMessage(queueName, + RECEIVE_AND_DELETE_5_SECONDS); + + // Assert + assertNotNull(receiveQueueMessageResult); + assertNull(receiveQueueMessageResult.getValue()); + } + @Test public void peekLockMessageWorks() throws Exception { // Arrange @@ -189,6 +205,20 @@ public void peekLockMessageWorks() throws Exception { assertEquals("Hello Again", new String(data, 0, size)); } + @Test + public void peekLockMessageEmptyQueueWorks() throws Exception { + // Arrange + String queueName = "TestPeekLockMessageEmptyQueueWorks"; + service.createQueue(new QueueInfo(queueName)); + + // Act + ReceiveQueueMessageResult result = service.receiveQueueMessage(queueName, PEEK_LOCK_5_SECONDS); + + // Assert + assertNotNull(result); + assertNull(result.getValue()); + } + @Test public void peekLockedMessageCanBeCompleted() throws Exception { // Arrange @@ -252,8 +282,20 @@ public void peekLockedMessageCanBeDeleted() throws Exception { // Assert assertNotNull(lockToken); assertNotNull(lockedUntil); - assertNull(receivedMessage.getLockToken()); - assertNull(receivedMessage.getLockedUntilUtc()); + assertNull(receivedMessage); + } + + @Test + public void emptyQueueReturnsNullMessage() throws Exception { + // Arrange + String queueName = "testEmptyQueueReturnsNullMessage"; + service.createQueue(new QueueInfo(queueName)); + + // Act + BrokeredMessage brokeredMessage = service.receiveQueueMessage(queueName, PEEK_LOCK_5_SECONDS).getValue(); + + // Assert + assertNull(brokeredMessage); } @Test From 2dfa49eeeb63699216a39c6738cd8b75ab8b0ab7 Mon Sep 17 00:00:00 2001 From: Albert Cheng Date: Mon, 22 Apr 2013 18:06:31 -0700 Subject: [PATCH 17/20] fix the getBytes for StatusLine class. --- .../windowsazure/services/media/implementation/StatusLine.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/media/implementation/StatusLine.java b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/media/implementation/StatusLine.java index 70be929297ed..7fcc5d5ea948 100644 --- a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/media/implementation/StatusLine.java +++ b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/media/implementation/StatusLine.java @@ -53,7 +53,7 @@ public static StatusLine create(DataSource dataSource) { private static void expect(Reader reader, String string) { try { - byte[] byteArray = string.getBytes(); + byte[] byteArray = string.getBytes("UTF-8"); int ch; for (int i = 0; i < string.length(); i++) { ch = reader.read(); From 04a1cc6c4fbe278c2f8a5220b20d7f5f39ce6065 Mon Sep 17 00:00:00 2001 From: Albert Cheng Date: Mon, 29 Apr 2013 14:03:27 -0700 Subject: [PATCH 18/20] fix a test failure due to nimbus update. --- .../windowsazure/services/media/ContentKeyIntegrationTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/ContentKeyIntegrationTest.java b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/ContentKeyIntegrationTest.java index c195aad9320f..eca527f932ed 100644 --- a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/ContentKeyIntegrationTest.java +++ b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/ContentKeyIntegrationTest.java @@ -42,7 +42,7 @@ public class ContentKeyIntegrationTest extends IntegrationTestBase { private final String validButNonexistContentKeyId = "nb:kid:UUID:80dfe751-e5a1-4b29-a992-4a75276473af"; private final ContentKeyType testContentKeyType = ContentKeyType.CommonEncryption; - private final String testEncryptedContentKey = "ThisIsEncryptedContentKey"; + private final String testEncryptedContentKey = "bFE4M/kZrKi00AoLOVpbQ4R9xja5P/pfBv9SC9I1Gw8yx+OIWdazGNpT7MgpeOLSebkxO5iDAIUKX5Es6oRUiH6pTNAMEtiHFBrKywODKnTQ09pCAMmdIA4q1gLeEUpsXPY/YXaLsTrBGbmRtlUYyaZEjestsngV8JpkJemCGjmMF0bHCoQRKt0LCVl/cqyWawzBuyaJniUCDdU8jem7sjrw8BbgCDmTAUmaj9TYxEP98d3wEJcL4pzDzOloYWXqzNB9assXgcQ0eouT7onSHa1d76X2E5q16AIIoOndLyIuAxlwFqpzF6LFy3X9mNGEY1iLXeFA89DE0PPx8EHtyg=="; private void assertByteArrayEquals(byte[] source, byte[] target) { assertEquals(source.length, target.length); From e62d01a8b9e822826506d931be36536942735818 Mon Sep 17 00:00:00 2001 From: Albert Cheng Date: Fri, 10 May 2013 10:26:11 -0700 Subject: [PATCH 19/20] fix a unit test failure on ci server. --- .../serviceruntime/Protocol1RuntimeGoalStateClientTests.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/serviceruntime/Protocol1RuntimeGoalStateClientTests.java b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/serviceruntime/Protocol1RuntimeGoalStateClientTests.java index 00a669a080ff..903dc9756fd7 100644 --- a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/serviceruntime/Protocol1RuntimeGoalStateClientTests.java +++ b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/serviceruntime/Protocol1RuntimeGoalStateClientTests.java @@ -153,7 +153,7 @@ public RoleEnvironmentData deserialize(InputStream stream) { } try { - Thread.sleep(1000); + Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); From 40d1b004ca7e75b7002263329e0471634294913a Mon Sep 17 00:00:00 2001 From: Albert Cheng Date: Fri, 10 May 2013 11:52:01 -0700 Subject: [PATCH 20/20] fix for issue 314, remove codehaus exception --- .../media/implementation/OAuthTokenManagerTest.java | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/implementation/OAuthTokenManagerTest.java b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/implementation/OAuthTokenManagerTest.java index 50794db32c08..ec76153f61a7 100644 --- a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/implementation/OAuthTokenManagerTest.java +++ b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/media/implementation/OAuthTokenManagerTest.java @@ -24,8 +24,6 @@ import java.util.Date; import java.util.TimeZone; -import org.codehaus.jackson.JsonParseException; -import org.codehaus.jackson.map.JsonMappingException; import org.junit.Before; import org.junit.Test; import org.mockito.invocation.InvocationOnMock; @@ -64,8 +62,7 @@ public Date answer(InvocationOnMock invocation) throws Throwable { }); } - private void doIncrementingTokens() throws ServiceException, URISyntaxException, JsonParseException, - JsonMappingException, IOException { + private void doIncrementingTokens() throws ServiceException, URISyntaxException, IOException { doAnswer(new Answer() { int count = 0; @@ -82,8 +79,7 @@ public OAuthTokenResponse answer(InvocationOnMock invocation) throws Throwable { } @Test - public void clientUsesContractToGetToken() throws ServiceException, URISyntaxException, JsonParseException, - JsonMappingException, IOException { + public void clientUsesContractToGetToken() throws ServiceException, URISyntaxException, IOException { // Arrange doIncrementingTokens(); @@ -97,7 +93,7 @@ public void clientUsesContractToGetToken() throws ServiceException, URISyntaxExc @Test public void clientWillNotCallMultipleTimesWhileAccessTokenIsValid() throws ServiceException, URISyntaxException, - JsonParseException, JsonMappingException, IOException { + IOException { // Arrange doIncrementingTokens(); @@ -117,7 +113,7 @@ public void clientWillNotCallMultipleTimesWhileAccessTokenIsValid() throws Servi @Test public void clientWillBeCalledWhenTokenIsHalfwayToExpiring() throws ServiceException, URISyntaxException, - JsonParseException, JsonMappingException, IOException { + IOException { // Arrange doIncrementingTokens();