Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions microsoft-azure-api/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,12 @@
<artifactId>commons-lang3</artifactId>
<version>3.1</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk16</artifactId>
<version>1.46</version>
<scope>test</scope>
</dependency>
</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ public <T> T action(EntityTypeActionOperation<T> 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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 an instance of (@link RebindContentKeyType).
*
* @return the rebind content key type instance.
*/
public RebindContentKeyType createRebindContentKeyType() {
return new RebindContentKeyType();
}
}
Original file line number Diff line number Diff line change
@@ -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;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,30 @@

package com.microsoft.windowsazure.services.media.models;

import java.io.InputStream;
import java.io.UnsupportedEncodingException;
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;
import com.microsoft.windowsazure.services.media.entityoperations.DefaultGetOperation;
import com.microsoft.windowsazure.services.media.entityoperations.DefaultListOperation;
import com.microsoft.windowsazure.services.media.entityoperations.EntityCreateOperation;
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.microsoft.windowsazure.services.media.implementation.content.RebindContentKeyType;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.GenericType;

/**
Expand Down Expand Up @@ -213,4 +229,82 @@ 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 EntityTypeActionOperation<String> 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 EntityTypeActionOperation<String> rebind(String contentKeyId) {
return rebind(contentKeyId, "");
}

private static class RebindContentKeyActionOperation extends DefaultEntityTypeActionOperation<String> {
private final JAXBContext jaxbContext;

private final Unmarshaller unmarshaller;

public RebindContentKeyActionOperation(String contentKeyId, String 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("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);
RebindContentKeyType rebindContentKeyType = parseResponse(clientResponse);
return rebindContentKeyType.getContentKey();
}

private RebindContentKeyType parseResponse(ClientResponse clientResponse) {
InputStream inputStream = clientResponse.getEntityInputStream();
JAXBElement<RebindContentKeyType> rebindContentKeyTypeJaxbElement;
try {
rebindContentKeyTypeJaxbElement = unmarshaller.unmarshal(new StreamSource(inputStream),
RebindContentKeyType.class);
}
catch (JAXBException e) {
throw new RuntimeException(e);
}
return rebindContentKeyTypeJaxbElement.getValue();
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,19 @@

import static org.junit.Assert.*;

import java.net.URL;
import java.net.URLEncoder;
import java.security.PrivateKey;
import java.security.Security;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
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;
Expand All @@ -36,6 +42,57 @@ 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;

ContentKeyInfo contentKeyInfo = service.create(ContentKey.create(testContentKeyId, testContentKeyType,
testEncryptedContentKey).setName(testContentKeyName));
return contentKeyInfo;
}

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));

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 ContentKeyInfo createValidTestContentKey(String contentKeyNameSuffix) throws Exception {
byte[] aesKey = createTestAesKey();
return createValidTestContentKeyWithAesKey(contentKeyNameSuffix, aesKey);
}

private byte[] createTestAesKey() {
byte[] aesKey = new byte[32];
int i;
for (i = 0; i < 32; i++) {
aesKey[i] = 1;
}

return aesKey;
}

private String createRandomContentKeyId() {
UUID uuid = UUID.randomUUID();
String randomContentKey = String.format("nb:kid:UUID:%s", uuid);
Expand Down Expand Up @@ -65,7 +122,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";
String protectionKeyId = service.action(ProtectionKey.getProtectionKeyId(testContentKeyType));

// Act
Expand Down Expand Up @@ -195,4 +252,53 @@ public void cannotDeleteContentKeyByNonexistId() throws Exception {
service.delete(ContentKey.delete(validButNonexistContentKeyId));
}

@Test
public void rebindContentKeyNoX509CertificateSuccess() throws Exception {

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()));

}

@Test
public void rebindContentKeyWithX509CertficateSuccess() throws Exception {
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
byte[] aesKey = createTestAesKey();
ContentKeyInfo contentKeyInfo = createValidTestContentKeyWithAesKey("rebindContentKeyWithX509Success", aesKey);
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")));
byte[] decryptedAesKey = EncryptionHelper.decryptSymmetricKey(rebindedContentKey, privateKey);
assertByteArrayEquals(aesKey, decryptedAesKey);
}

@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"));
}
}
Loading