/**
* 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.util;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import org.apache.commons.codec.binary.Base64;
import org.apache.http.impl.client.HttpClientBuilder;
import com.microsoft.azure.keyvault.KeyVaultClient;
import com.microsoft.azure.keyvault.KeyVaultClientImpl;
import com.microsoft.azure.keyvault.models.Secret;
import com.microsoft.windowsazure.exception.ServiceException;
/**
* A utility class for interacting with KeyVault
*/
public class KeyVaultUtility {
/**
* Creates a secret in Azure Key Vault and returns its ID.
*
* @param secretName
* The name of the secret to create
* @return The ID of the created secret
* @throws InterruptedException
* @throws ExecutionException
* @throws NoSuchAlgorithmException
* @throws URISyntaxException
* @throws MalformedURLException
*/
public static String SetUpKeyVaultSecret(String secretName)
throws InterruptedException, ExecutionException,
NoSuchAlgorithmException, URISyntaxException, MalformedURLException {
KeyVaultClient cloudVault = GetKeyVaultClient();
if (Utility.vaultURL == null || Utility.vaultURL.isEmpty()) {
throw new IllegalArgumentException("No Keyvault URL specified.");
}
try {
// Delete the secret if it exists.
cloudVault.deleteSecretAsync(Utility.vaultURL, secretName).get();
} catch (ExecutionException ex) {
boolean keyNotFound = false;
if (ex.getCause().getClass() == ServiceException.class) {
ServiceException serviceException = (ServiceException) ex
.getCause();
if (serviceException.getHttpStatusCode() == 404) {
keyNotFound = true;
}
}
if (!keyNotFound) {
System.out
.println("Unable to access the specified vault. Please confirm the KVClientId, KVClientKey, and VaultUri are valid in the app.config file.");
System.out
.println("Also ensure that the client ID has previously been granted full permissions for Key Vault secrets using the Set-AzureKeyVaultAccessPolicy command with the -PermissionsToSecrets parameter.");
System.out.println("Press any key to exit");
Scanner input = new Scanner(System.in);
input.nextLine();
input.close();
throw ex;
}
}
// Create a 256bit symmetric key and convert it to Base64.
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(256); // Note that we cannot use SymmetricKey.KeySize256,
// because this resolves to '0x20'.
SecretKey wrapKey = keyGen.generateKey();
// Store the Base64 of the key in the key vault. Note that the
// content-type of the secret must
// be application/octet-stream or the KeyVaultKeyResolver will not load
// it as a key.
Map<String, String> headers = new HashMap<String, String>();
headers.put("Content-Type", "application/octet-stream");
Secret cloudSecret = cloudVault.setSecretAsync(Utility.vaultURL,
secretName, Base64.encodeBase64String(wrapKey.getEncoded()),
"application/octet-stream", null, null).get();
// Return the base identifier of the secret. This will be resolved to
// the current version of the secret.
return cloudSecret.getSecretIdentifier().getBaseIdentifier();
}
/**
* Creates the KeyVaultClient using the credentials specified in the Utility
* class.
*
* @return
* @throws URISyntaxException
* @throws MalformedURLException
* @throws InterruptedException
* @throws ExecutionException
*/
public static KeyVaultClient GetKeyVaultClient() throws URISyntaxException,
MalformedURLException, InterruptedException, ExecutionException {
if (Utility.AuthClientId == null || Utility.AuthClientId.isEmpty()
|| Utility.AuthClientSecret == null
|| Utility.AuthClientSecret.isEmpty()) {
throw new IllegalArgumentException(
"Invalid AuthClientID or AuthClientSecret specified.");
}
HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
ExecutorService executorService = Executors.newCachedThreadPool();
KVCredentials creds = new KVCredentials(Utility.AuthClientId,
Utility.AuthClientSecret);
KeyVaultClient cloudVault = new KeyVaultClientImpl(httpClientBuilder,
executorService, creds);
return cloudVault;
}
/**
* Helper method to create a new secret on the KeyVault service.
*
* @param defaultKeyName
* The default key name to use if the user does not provide one
* @return The keyID for the newly-created secret (or the existing secret,
* if one was passed in.)
* @throws InterruptedException
* @throws ExecutionException
* @throws NoSuchAlgorithmException
* @throws URISyntaxException
* @throws MalformedURLException
*/
public static String createSecret(String defaultKeyName)
throws InterruptedException, ExecutionException,
NoSuchAlgorithmException, URISyntaxException, MalformedURLException {
System.out.println("No secret specified in Utility class.");
System.out
.println("Please enter the name of a new secret to create in Key Vault.");
System.out
.println("WARNING: This will delete any existing secret with the same name.");
System.out.println("If nothing is entered, the value \""
+ defaultKeyName + "\" will be used.");
@SuppressWarnings("resource")
Scanner input = new Scanner(System.in);
String newSecretName = input.nextLine();
if (newSecretName == null || newSecretName.isEmpty()) {
newSecretName = defaultKeyName;
}
// 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 keyID = KeyVaultUtility.SetUpKeyVaultSecret(newSecretName);
System.out.println();
System.out.println("Created a secret with ID: " + keyID);
System.out.println("Copy the secret ID to App.config to reuse.");
System.out.println();
return keyID;
}
}