/**
* 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.encryption.keyvault.keyrotation.gettingstarted;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.URISyntaxException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Random;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
import com.microsoft.azure.keyvault.core.IKey;
import com.microsoft.azure.keyvault.extensions.CachingKeyResolver;
import com.microsoft.azure.keyvault.extensions.KeyVaultKeyResolver;
import com.microsoft.azure.storage.CloudStorageAccount;
import com.microsoft.azure.storage.StorageException;
import com.microsoft.azure.storage.blob.BlobEncryptionPolicy;
import com.microsoft.azure.storage.blob.BlobRequestOptions;
import com.microsoft.azure.storage.blob.CloudBlobClient;
import com.microsoft.azure.storage.blob.CloudBlobContainer;
import com.microsoft.azure.storage.blob.CloudBlockBlob;
import com.microsoft.azure.storage.util.KeyVaultUtility;
import com.microsoft.azure.storage.util.Utility;
public class KeyRotationGettingStarted {
public static void main(String[] args) throws StorageException,
InterruptedException, ExecutionException, URISyntaxException,
NoSuchAlgorithmException, InvalidKeyException, IOException {
Utility.printSampleStartInfo("KeyRotationGettingStarted");
// Create two secrets and obtain their IDs. This is normally a one-time
// setup step.
// Although it is possible to use keys (rather than secrets) stored in
// Key Vault, this prevents caching.
// Therefore it is recommended to use secrets along with a caching
// resolver (see below).
String keyID1 = Utility.keyVaultKeyIDForRotation1;
String keyID2 = Utility.keyVaultKeyIDForRotation2;
if (keyID1 == null || keyID1.isEmpty()) {
keyID1 = KeyVaultUtility.createSecret("KeyRotationSampleSecret1");
}
if (keyID2 == null || keyID2.isEmpty()) {
keyID2 = KeyVaultUtility.createSecret("KeyRotationSampleSecret2");
}
// Retrieve storage account information from connection string
// How to create a storage connection string -
// https://azure.microsoft.com/en-us/documentation/articles/storage-configure-connection-string/
CloudStorageAccount storageAccount = CloudStorageAccount
.parse(Utility.storageConnectionString);
CloudBlobClient client = storageAccount.createCloudBlobClient();
CloudBlobContainer container = client
.getContainerReference("blobencryptioncontainer"
+ UUID.randomUUID().toString().replace("-", ""));
// Construct a resolver capable of looking up keys and secrets stored in
// Key Vault.
KeyVaultKeyResolver cloudResolver = new KeyVaultKeyResolver(
KeyVaultUtility.GetKeyVaultClient());
// Set up a caching resolver so the secrets can be cached on the client.
// This is the recommended usage
// pattern since the throttling targets for Storage and Key Vault
// services are orders of magnitude
// different.
CachingKeyResolver cachingResolver = new CachingKeyResolver(2,
cloudResolver);
// Create key instances corresponding to the key IDs. This will cache
// the secrets.
IKey cloudKey1 = cachingResolver.resolveKeyAsync(keyID1).get();
IKey cloudKey2 = cachingResolver.resolveKeyAsync(keyID2).get();
// We begin with cloudKey1, and a resolver capable of resolving and
// caching Key Vault secrets.
BlobEncryptionPolicy encryptionPolicy = new BlobEncryptionPolicy(
cloudKey1, cachingResolver);
BlobRequestOptions defaultRequestOptions = client
.getDefaultRequestOptions();
defaultRequestOptions.setEncryptionPolicy(encryptionPolicy);
defaultRequestOptions.setRequireEncryption(true);
try {
container.createIfNotExists();
int size = 5 * 1024 * 1024;
byte[] buffer1 = new byte[size];
byte[] buffer2 = new byte[size];
Random rand = new Random();
rand.nextBytes(buffer1);
rand.nextBytes(buffer2);
// Upload the first blob using the secret stored in Azure Key Vault.
CloudBlockBlob blob = container.getBlockBlobReference("blockblob1");
System.out.println("Uploading Blob 1 using Key 1.");
// Upload the encrypted contents to the first blob.
ByteArrayInputStream inputStream = new ByteArrayInputStream(buffer1);
blob.upload(inputStream, size);
System.out.println("Downloading and decrypting Blob 1.");
// Download and decrypt the encrypted contents from the first blob.
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
blob.download(outputStream);
// At this point we will rotate our keys so new encrypted content
// will use the
// second key. Note that the same resolver is used, as this resolver
// is capable
// of decrypting blobs encrypted using either key.
System.out.println("Rotating the active encryption key to Key 2.");
client.getDefaultRequestOptions().setEncryptionPolicy(
new BlobEncryptionPolicy(cloudKey2, cachingResolver));
// Upload the second blob using the key stored in Azure Key Vault.
CloudBlockBlob blob2 = container
.getBlockBlobReference("blockblob2");
System.out.println("Uploading Blob 2 using Key 2.");
// Upload the encrypted contents to the second blob.
inputStream = new ByteArrayInputStream(buffer2);
blob2.upload(inputStream, size);
System.out.println("Downloading and decrypting Blob 2.");
// Download and decrypt the encrypted contents from the second blob.
outputStream = new ByteArrayOutputStream();
blob2.download(outputStream);
// Here we download and re-upload the first blob. This has the
// effect of updating
// the blob to use the new key.
System.out.println("Downloading and decrypting Blob 1.");
outputStream = new ByteArrayOutputStream();
blob.download(outputStream);
System.out.println("Re-uploading Blob 1 using Key 2.");
inputStream = new ByteArrayInputStream(outputStream.toByteArray());
blob.upload(inputStream, size);
// For the purposes of demonstration, we now override the encryption
// policy to only recognize key 2.
BlobEncryptionPolicy key2OnlyPolicy = new BlobEncryptionPolicy(
cloudKey2, null);
BlobRequestOptions key2OnlyOptions = new BlobRequestOptions();
key2OnlyOptions.setEncryptionPolicy(key2OnlyPolicy);
System.out.println("Downloading and decrypting Blob 1.");
outputStream = new ByteArrayOutputStream();
blob.download(outputStream, null, key2OnlyOptions, null);
// The first blob can still be decrypted because it is using the
// second key.
} finally {
container.deleteIfExists();
Utility.printSampleCompleteInfo("KeyRotationGettingStarted");
}
}
}