com.microsoft.azure.storage.queue.CloudQueueEncryptionTests.java Source code

Java tutorial

Introduction

Here is the source code for com.microsoft.azure.storage.queue.CloudQueueEncryptionTests.java

Source

/**
 * 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.azure.storage.queue;

import static org.junit.Assert.*;

import java.io.IOException;
import java.net.URISyntaxException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.EnumSet;
import java.util.Random;
import java.util.UUID;
import java.util.concurrent.ExecutionException;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.lang3.StringUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.experimental.categories.Category;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.microsoft.azure.keyvault.core.IKey;
import com.microsoft.azure.keyvault.extensions.RsaKey;
import com.microsoft.azure.keyvault.extensions.SymmetricKey;
import com.microsoft.azure.storage.DictionaryKeyResolver;
import com.microsoft.azure.storage.StorageException;
import com.microsoft.azure.storage.TestHelper;
import com.microsoft.azure.storage.TestRunners.CloudTests;
import com.microsoft.azure.storage.TestRunners.DevFabricTests;
import com.microsoft.azure.storage.TestRunners.DevStoreTests;
import com.microsoft.azure.storage.core.Base64;
import com.microsoft.azure.storage.core.EncryptionData;

/**
 * Queue Tests
 */
@Category({ CloudTests.class, DevFabricTests.class, DevStoreTests.class })
public class CloudQueueEncryptionTests {

    private CloudQueue queue;

    @Before
    public void queueTestMethodSetUp() throws URISyntaxException, StorageException {
        this.queue = QueueTestHelper.getRandomQueueReference();
        this.queue.createIfNotExists();
    }

    @After
    public void queueTestMethodTearDown() throws StorageException {
        this.queue.deleteIfExists();
    }

    @Test
    public void testQueueAddUpdateEncryptedMessage()
            throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, StorageException {
        // Create the Key to be used for wrapping.
        SymmetricKey aesKey = TestHelper.getSymmetricKey();
        RsaKey rsaKey = TestHelper.getRSAKey();

        // Create the resolver to be used for unwrapping.
        DictionaryKeyResolver resolver = new DictionaryKeyResolver();
        resolver.add(aesKey);
        resolver.add(rsaKey);

        doQueueAddUpdateEncryptedMessage(aesKey, resolver);
        doQueueAddUpdateEncryptedMessage(rsaKey, resolver);
    }

    private void doQueueAddUpdateEncryptedMessage(IKey key, DictionaryKeyResolver keyResolver)
            throws StorageException {
        String messageStr = UUID.randomUUID().toString();
        CloudQueueMessage message = new CloudQueueMessage(messageStr);

        QueueRequestOptions createOptions = new QueueRequestOptions();
        createOptions.setEncryptionPolicy(new QueueEncryptionPolicy(key, null));

        // add message
        this.queue.addMessage(message, 0, 0, createOptions, null);

        // Retrieve message
        QueueRequestOptions retrieveOptions = new QueueRequestOptions();
        retrieveOptions.setEncryptionPolicy(new QueueEncryptionPolicy(null, keyResolver));

        CloudQueueMessage retrMessage = this.queue.retrieveMessage(30, retrieveOptions, null);
        assertEquals(messageStr, retrMessage.getMessageContentAsString());

        // Update message
        String updatedMessage = UUID.randomUUID().toString();
        retrMessage.setMessageContent(updatedMessage);
        this.queue.updateMessage(retrMessage, 0,
                EnumSet.of(MessageUpdateFields.CONTENT, MessageUpdateFields.VISIBILITY), createOptions, null);

        // Retrieve updated message
        retrMessage = this.queue.retrieveMessage(30, retrieveOptions, null);
        assertEquals(updatedMessage, retrMessage.getMessageContentAsString());
    }

    @Test
    public void testQueueAddUpdateEncryptedBinaryMessage()
            throws StorageException, InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException {
        // Create the Key to be used for wrapping.
        SymmetricKey aesKey = TestHelper.getSymmetricKey();

        byte[] messageBytes = new byte[100];
        Random rand = new Random();
        rand.nextBytes(messageBytes);

        CloudQueueMessage message = new CloudQueueMessage(messageBytes);

        QueueRequestOptions options = new QueueRequestOptions();
        options.setEncryptionPolicy(new QueueEncryptionPolicy(aesKey, null));

        // add message
        this.queue.addMessage(message, 0, 0, options, null);

        // Retrieve message
        CloudQueueMessage retrMessage = this.queue.retrieveMessage(30, options, null);
        assertArrayEquals(messageBytes, retrMessage.getMessageContentAsByte());
    }

    @Test
    public void testQueueAddUpdateEncryptedEncodedMessage()
            throws StorageException, InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException {
        // Create the Key to be used for wrapping.
        SymmetricKey aesKey = TestHelper.getSymmetricKey();

        byte[] messageBytes = new byte[100];
        Random rand = new Random();
        rand.nextBytes(messageBytes);

        String inputMessage = Base64.encode(messageBytes);
        CloudQueueMessage message = new CloudQueueMessage(inputMessage);
        this.queue.setShouldEncodeMessage(false);

        QueueRequestOptions options = new QueueRequestOptions();
        options.setEncryptionPolicy(new QueueEncryptionPolicy(aesKey, null));

        // add message
        this.queue.addMessage(message, 0, 0, options, null);

        // Retrieve message
        CloudQueueMessage retrMessage = this.queue.retrieveMessage(30, options, null);
        assertEquals(inputMessage, retrMessage.getMessageContentAsString());
    }

    @Test
    public void testQueueAddEncrypted64KMessage()
            throws StorageException, InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException {
        // Create the Key to be used for wrapping.
        SymmetricKey aesKey = TestHelper.getSymmetricKey();

        String inputMessage = StringUtils.repeat('a', 64 * 1024);
        CloudQueueMessage message = new CloudQueueMessage(inputMessage);
        this.queue.setShouldEncodeMessage(false);

        QueueRequestOptions options = new QueueRequestOptions();
        options.setEncryptionPolicy(new QueueEncryptionPolicy(aesKey, null));

        // add message
        this.queue.addMessage(message);

        // add encrypted Message
        try {
            this.queue.addMessage(message, 0, 0, options, null);
            fail("Adding an encrypted message that exceeds message limits should throw.");
        } catch (IllegalArgumentException e) {

        }
    }

    @Test
    public void testQueueMessageValidateEncryption() throws StorageException, JsonProcessingException, IOException,
            InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException,
            NoSuchAlgorithmException, NoSuchPaddingException, InterruptedException, ExecutionException {
        // Create the Key to be used for wrapping.
        SymmetricKey aesKey = TestHelper.getSymmetricKey();

        byte[] messageBytes = new byte[100];
        Random rand = new Random();
        rand.nextBytes(messageBytes);

        String inputMessage = Base64.encode(messageBytes);
        CloudQueueMessage message = new CloudQueueMessage(inputMessage);
        this.queue.setShouldEncodeMessage(false);

        QueueRequestOptions options = new QueueRequestOptions();
        options.setEncryptionPolicy(new QueueEncryptionPolicy(aesKey, null));

        // add message
        this.queue.addMessage(message, 0, 0, options, null);

        // Retrieve message without decrypting
        CloudQueueMessage retrMessage = this.queue.retrieveMessage();

        // Decrypt locally
        CloudQueueMessage decryptedMessage;
        CloudQueueEncryptedMessage encryptedMessage = CloudQueueEncryptedMessage
                .deserialize(retrMessage.getMessageContentAsString());
        EncryptionData encryptionData = encryptedMessage.getEncryptionData();

        byte[] contentEncryptionKey = aesKey.unwrapKeyAsync(encryptionData.getWrappedContentKey().getEncryptedKey(),
                encryptionData.getWrappedContentKey().getAlgorithm()).get();
        SecretKey keySpec = new SecretKeySpec(contentEncryptionKey, 0, contentEncryptionKey.length, "AES");

        Cipher myAes = Cipher.getInstance("AES/CBC/PKCS5Padding");
        myAes.init(Cipher.DECRYPT_MODE, keySpec, new IvParameterSpec(encryptionData.getContentEncryptionIV()));

        byte[] src = Base64.decode(encryptedMessage.getEncryptedMessageContents());

        decryptedMessage = new CloudQueueMessage(myAes.doFinal(src, 0, src.length));

        assertArrayEquals(message.getMessageContentAsByte(), decryptedMessage.getMessageContentAsByte());
    }

    @Test
    public void testQueueMessageEncryptionWithStrictMode()
            throws StorageException, InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException {
        // Create the Key to be used for wrapping.
        SymmetricKey aesKey = TestHelper.getSymmetricKey();

        // Create the resolver to be used for unwrapping.
        DictionaryKeyResolver resolver = new DictionaryKeyResolver();
        resolver.add(aesKey);

        String messageStr = UUID.randomUUID().toString();
        CloudQueueMessage message = new CloudQueueMessage(messageStr);

        // Add message with policy.
        QueueRequestOptions createOptions = new QueueRequestOptions();
        createOptions.setEncryptionPolicy(new QueueEncryptionPolicy(aesKey, null));
        createOptions.setRequireEncryption(true);

        this.queue.addMessage(message, 0, 0, createOptions, null);

        // Set policy to null and add message while RequireEncryption flag is still set to true. This should throw.
        createOptions.setEncryptionPolicy(null);

        try {
            this.queue.addMessage(message, 0, 0, createOptions, null);
            fail("Not specifying a policy when RequireEnryption is set to true should throw.");
        } catch (IllegalArgumentException ex) {
        }

        // Retrieve message
        QueueRequestOptions retrieveOptions = new QueueRequestOptions();
        retrieveOptions.setEncryptionPolicy(new QueueEncryptionPolicy(null, resolver));
        retrieveOptions.setRequireEncryption(true);

        CloudQueueMessage retrMessage = queue.retrieveMessage(30, retrieveOptions, null);

        // Update message with plain text.
        String updatedMessage = UUID.randomUUID().toString();
        retrMessage.setMessageContent(updatedMessage);

        this.queue.updateMessage(retrMessage, 0,
                EnumSet.of(MessageUpdateFields.CONTENT, MessageUpdateFields.VISIBILITY), null, null);

        // Retrieve updated message with RequireEncryption flag but no metadata on the service. This should throw.
        try {
            this.queue.retrieveMessage(30, retrieveOptions, null);
            fail("Retrieving with RequireEncryption set to true and no metadata on the service should fail.");
        } catch (StorageException ex) {
        }

        // Set RequireEncryption to false and retrieve.
        retrieveOptions.setRequireEncryption(false);
        queue.retrieveMessage(30, retrieveOptions, null);
    }
}