Azure Blob Storage 装载的加密自定义
本部分介绍在 Azure Blob Storage 上以加密格式存储内容的加密自定义。Azure Blob Storage 上存储的系统自带内容使用 serverSideEncryption 加密类型进行加密。基于文件系统的电子仓库中存储的内容采用了纯文本格式。
Windchill 支持以下加密类型:
1. 服务器端 - 有两个选项可供选择:
a. Azure 托管的密钥 - 这是默认加密类型,由 Azure 系统自动设置。
b. Azure Key Vault 托管的密钥 - 您可以从 Azure 门户设置容器的 Key Vault 密钥。
2. 客户端 - 有两个选项可供选择:
a. clientSideEncryption-keyvault - 此加密类型使用 Azure Key Vault 托管的密钥。
b. clientSideEncryption - 此加密类型使用客户托管的密钥。
有关客户端加密委派实施的详细信息,请参阅“写入客户端加密的委派实施”一节。
|
用户负责管理加密选项。必须确保内容在加密选项发生更改的情况下仍可访问。
|
写入客户端加密的委派实施
要自定义 Azure Blob Storage 上的内容加密,请写入如下委派实施:
1. Blob 加密配置器委派 - 默认委派为
com.ptc.windchill.objectstorage.azureblob.encryption.DefaultBlobEncryptConfigurator。默认委派返回的加密类型为
serverSideEncryption。如果要使用其他加密类型,则需要写入一个可返回预期加密类型的委派,然后部署类文件并使用此命令行工具进行配置。有关配置的详细信息,请参阅
将 Windchill 文件电子仓库配置为使用 Azure Blob Storage。
以下是示例委派。
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;
}
}
}
根据装载路径 (Blob 名称),SampleBlobEncryptConfigurator 会返回 serverSideEncryption、clientSideEncryption 或 clientSideEncryption-keyvault 作为加密类型。使用这些加密类型对 Azure Blob Storage 上存储的内容进行加密。API getBlobEncryptionType 会接收存储帐户名称、容器名称和 Blob 名称作为自变量。您可以使用存储帐户名称、容器名称和 Blob 名称对加密类型进行编程。例如,对于容器 c1,返回的加密类型为 serverSideEncryption,而对于容器 c2,返回的加密类型为 clientSideEncryption-keyvault。
加密类型的有效值为:
a. serverSideEncryption - 利用服务器端加密与 Azure 托管的密钥来存储内容。
b. clientSideEncryption-keyvault - 利用客户端加密与 Azure Key Vault 托管的密钥来存储内容。
c. clientSideEncryption - 利用客户端加密与客户提供的密钥来存储内容。
示例委派如下:
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";
String tenantId = “xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx”;
KeyVaultConnectionParams connectionParams = new KeyVaultConnectionParams(keyVaultKeyIdUrl, appId,
appAuthKey, tenantId);
return connectionParams;
}
}
|
• 从 Windchill 12.1.2.0 版本开始,对于 clientSideEncryption-keyvault 加密类型,租户 (目录) ID 是需要提供给 KeyVaultConnectionParams 构造函数的强制输入。
• 如果要避免租户 (目录) ID 的代码发生更改,那么可以使用 wt 特性 wt.objectStorage.blob.csekeyvault.appTenantId 设置租户 (目录) ID。
|
3. CSESecKeyGenerator Delegate - 如果将加密类型配置为
clientSideEncryption,则必须写入此委派的实施并配置此委派。仅当加密类型配置为
clientSideEncryption 时,此委派才适用。此委派不存在默认的即用型实施。写入一个可管理
KeyPair 和
KeyId 的委派,然后部署类文件并使用命令行工具进行配置。有关配置的详细信息,请参阅
将 Windchill 文件电子仓库配置为使用 Azure Blob Storage。
示例委派如下:
SampleCSESecKeyGenerator.java:此示例实施演示了如何生成 KeyPair 和 KeyId。此示例实施可搜索现有 KeyPair。如有搜索到,则将返回 KeyPair。否则生成一个新的 KeyPair,将其存储在磁盘上的文件中并返回。您可以决定安全存储 KeyPair 的策略。此外,此示例实施对电子仓库中的所有文件使用单个 KeyPair。您可以编写自己的算法来管理 Azure 中存储的容器或 Blob 的多个 KeyPair。
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;
}
}
管理加密类型
如果想要更改文件电子仓库的加密类型,请执行下列步骤:
1. 创建新电子仓库,并使用委派实施将其配置为所需的加密类型。有关详细信息,请参阅“写入客户端加密的委派实施”一节。
2. 更新存储规则,以将内容定向到新电子仓库。
3. 运行重新存储排程。