Encryption Customization for Azure Blob Storage Mounts
This section explains the encryption customization to store content in an encrypted format on Azure Blob Storage. Out of the box, the content stored on Azure Blob Storage is encrypted using serverSideEncryption encryption type. The content stored in file system based vaults are in plain format.
Windchill supports following encryption types:
1. Server Side — There are two options to choose from:
a. Azure managed key — This is the default encryption type and is automatically set by the Azure system.
b. Azure Key Vault managed key — You can set the Key Vault key for a Container from the Azure portal.
2. Client Side — There are two options to choose from:
a. clientSideEncryption-keyvault — This encryption type uses the Azure Key Vault managed keys.
b. clientSideEncryption — This encryption type uses the customer managed keys.
The details of the delegate implementations for client side encryption are provided in the section “Writing Delegate Implementations for Client Side Encryption”.
|
User is responsible for managing the encryption options. You must ensure that the content is accessible in case of changes in encryption options.
|
Writing Delegate Implementations for Client Side Encryption
To customize the encryption of content on Azure Blob Storage, write delegate implementations as follows:
1. Blob Encryption Configurator Delegate — The default delegate is
com.ptc.windchill.objectstorage.azureblob.encryption.DefaultBlobEncryptConfigurator. The default delegate returns the encryption type as
serverSideEncryption. If you wish to use other encryption type, write a delegate that returns the expected encryption type, deploy the class files and configure using command line tool. For more details on configuration, see
Configuring Windchill File Vaults to use Azure Blob Storage.
Following is a sample delegate.
SampleBlobEncryptConfigurator.java:
package com.abc.windchill.objectstorage.azureblob.encryption;
import com.ptc.windchill.objectstorage.azureblob.exception.BlobException;
public class SampleBlobEncryptConfigurator implements BlobEncryptConfigurator {
private static final String CSE_KEYVAULT_FOLDER = "keyvault";
private static final String CSE_FOLDER = "cse";
@Override
public String getBlobEncryptionType(String storageAccountName, String containerName, String blobName) throws BlobException {
if (blobName != null && blobName.contains(CSE_KEYVAULT_FOLDER)) {
return BlobEncryptionType.CSE_KEYVAULT;
} else if (blobName != null && blobName.contains(CSE_FOLDER)) {
return BlobEncryptionType.CSE;
} else {
return BlobEncryptionType.SSE;
}
}
}
Depending upon mount path (Blob name), the SampleBlobEncryptConfigurator returns the serverSideEncryption, clientSideEncryption or clientSideEncryption-keyvault as the encryption type. The content stored on Azure Blob Storage gets encrypted using these encryption types. The API getBlobEncryptionType receives the Storage Account Name, Container Name and Blob Name as argument. You can program the encryption type using the Storage Account Name, Container Name and Blob Name. For example, for Container c1, return encryption type as serverSideEncryption, and for Container c2, return the encryption type as clientSideEncryption-keyvault.
Valid values of encryption type are:
a. serverSideEncryption — The content is stored using server side encryption with Azure managed keys.
b. clientSideEncryption-keyvault — The content is stored using client side encryption with Azure Key Vault managed keys.
c. clientSideEncryption — The content is stored using client side encryption with customer provided keys.
2. CSEKeyVaultKeyProvider Delegate — This delegate is applicable if encryption type is configured as
clientSideEncryption-keyvault. There is no default out-of-the-box implementation for this delegate. You must write a delegate that returns the expected Azure Key Vault connection parameters, deploy the class files and configure using the command line tool. For more details on configuration, see
Configuring Windchill File Vaults to use Azure Blob Storage
|
Do not use the following sample implementations as is in a production environment.
|
Sample delegate is as follows:
SampleCSEKeyVaultKeyProvider.java: This sample implementation demonstrates how you can return the Azure Key Vault connection parameters. For more details on Key Vault and Application registration, see the section “Setting the Key Vault Encryption in Azure Blob” in
Configuring Windchill File Vaults to use Azure Blob Storage.
package com.abc.windchill.objectstorage.azureblob.encryption.csekeyvault;
import com.ptc.windchill.objectstorage.azureblob.encryption.csekeyvault.CSEKeyVaultKeyProvider;
import com.ptc.windchill.objectstorage.azureblob.encryption.csekeyvault.KeyVaultConnectionParams;
import com.ptc.windchill.objectstorage.azureblob.exception.BlobException;
/**
* Sample implementation of CSEKeyVaultKeyProvider.
*/
public class SampleCSEKeyVaultKeyProvider implements CSEKeyVaultKeyProvider {
@Override
public KeyVaultConnectionParams getKeyVaultConnectionDetails(String containerName, String blobName)
throws BlobException {
String keyVaultKeyIdUrl = "https://mykeyvault.vault.azure.net/keys/MyKeyVaultKey";
String appId = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx";
String appAuthKey = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
KeyVaultConnectionParams connectionParams = new KeyVaultConnectionParams(keyVaultKeyIdUrl, appId,
appAuthKey);
return connectionParams;
}
}
3. CSESecKeyGenerator Delegate — If you configure the encryption type as
clientSideEncryption, you must write the implementation for this delegate and configure the delegate. This delegate is applicable only if the encryption type is configured as
clientSideEncryption. There is no default out-of-the-box implementation for this delegate. Write a delegate that manages the
KeyPair and
KeyId, deploy the class files and configure using command line tool. For more details on configuration, see
Configuring Windchill File Vaults to use Azure Blob Storage.
Sample delegate is as follows:
SampleCSESecKeyGenerator.java: This is a sample implementation that demonstrates how to generate a KeyPair and KeyId. This sample implementation searches for an existing KeyPair. If found, the KeyPair is returned. Otherwise generates a new KeyPair, stores it in a file on disk and returns. You can decide the strategy to securely store the KeyPair. Also, this sample implementation uses a single KeyPair for all the files in Vault. You can write your own algorithm to manage multiple KeyPairs for Containers or for Blobs stored in Azure.
package com.abc.windchill.objectstorage.azureblob.encryption.cse;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import com.ptc.windchill.objectstorage.azureblob.encryption.cse.CSESecKeyGenerator;
import com.ptc.windchill.objectstorage.azureblob.exception.BlobException;
import wt.util.WTException;
import wt.util.WTProperties;
import wt.wrmf.delivery.ObjectIS;
import wt.wrmf.delivery.PayloadShippingItem;
import wt.wrmf.delivery.ShippingHelper;
/**
* Sample implementation of CSESecKeyGenerator.
*/
public class SampleCSESecKeyGenerator implements CSESecKeyGenerator {
private File keyPairFile;
public static String keyPairFileName;
static {
try {
WTProperties props = WTProperties.getLocalProperties();
String windchillHome = props.getProperty("wt.home");
keyPairFileName = windchillHome + File.separator + "RsaKey";
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
@Override
public KeyPair getKeyPair(String storageAccountName, String containerName, String blobName) throws BlobException {
try{
KeyPair wrapKey = getKeyPairFromFileStore();
if(wrapKey == null){
// Create the IKey used for encryption.
final KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(1024);
wrapKey = keyGen.generateKeyPair();
storeKeyPairInFileStore(containerName, blobName, wrapKey);
storeKeyPairInFileStoreOnReplica(containerName, blobName, wrapKey);
}
return wrapKey;
}
catch(Exception e){
throw new BlobException(e);
}
}
@Override
public String getKeyId(String storageAccountName, String containerName, String blobName) throws BlobException {
return containerName;//using container name as key id
}
private KeyPair getKeyPairFromFileStore()throws IOException, ClassNotFoundException{
KeyPair keyPair = null;
File keyPairFile = getKeyPairFile();
try(
FileInputStream fis = new FileInputStream(keyPairFile);
ObjectInputStream ois = new ObjectInputStream(fis);){
keyPair = (KeyPair)ois.readObject();
}catch (IOException ignore){
ignore.printStackTrace();
}
return keyPair;
}
public synchronized void storeKeyPairInFileStore(String containerName, String blobName, KeyPair keyPair)throws IOException{
File keyPairFile = getKeyPairFile();
try(FileOutputStream fos = new FileOutputStream(keyPairFile, true);
ObjectOutputStream oos = new ObjectOutputStream(fos);){
oos.writeObject(keyPair);
oos.flush();
}
}
private synchronized void storeKeyPairInFileStoreOnReplica(String containerName, String blobName, KeyPair keyPair)
throws Exception {
//this is a private method that invokes method call on replica site
invokeMethodOnReplica(getReplicaSite(),
SampleCSESecKeyGenerator.class.getName(), "setKeyPairOnReplica",
new String[] { String.class.getName(), String.class.getName(), KeyPair.class.getName() },
new Serializable[] { containerName, blobName, keyPair });
}
private synchronized File getKeyPairFile() throws IOException {
if (keyPairFile == null) {
keyPairFile = new File(keyPairFileName);
if (!keyPairFile.exists()) {
keyPairFile.createNewFile();
}
}
return keyPairFile;
}
@Override
public boolean removeKeyPair(String storageAccountName, String containerName, String blobName) {
return true;//do not remove the KeyPair, since sample code uses single KeyPair for all files in vault.
}
public static PayloadShippingItem setKeyPairOnReplica(String storageAccountName,
String containerName, String blobName, KeyPair keyPair) throws WTException {
PayloadShippingItem psi = ShippingHelper.service.createPayloadShippingItem();
try {
File keyPairFile = new File(keyPairFileName);
if (!keyPairFile.exists()) {
keyPairFile.createNewFile();
}
try (FileOutputStream fos = new FileOutputStream(keyPairFile, false);
ObjectOutputStream oos = new ObjectOutputStream(fos);) {
oos.writeObject(keyPair);
oos.flush();
}
Object[] valuesToReturn = new Object[] {};
ObjectIS retValsIS = new ObjectIS(valuesToReturn);
psi.setPayload(retValsIS);
} catch (Exception e) {
throw new WTException(e);
}
return psi;
}
}
Managing Encryption Type
If you wish to change the encryption type for a file vault, perform the following steps:
1. Create a new Vault and configure it to the desired encryption type using delegate implementation. For more details, refer to the section “Writing Delegate Implementations for Client Side Encryption”.
2. Update the vaulting rule to direct the content to the new vault.
3. Run the re-vaulting schedule.