Azure Blob Storage マウントのための暗号のカスタマイズ
このセクションでは、Azure Blob Storage にコンテンツを暗号化されたフォーマットで保管するための、暗号のカスタマイズについて説明します。既成では、Azure Blob Storage に保管されているコンテンツは serverSideEncryption タイプの暗号を使用して暗号化されています。ファイルシステムベースボルトに保管されているコンテンツはプレーンフォーマットです。
Windchill は次の暗号タイプをサポートしています。
1. サーバー側 - 次の 2 つのオプションから選択します。
a. Azure 管理のキー - これがデフォルトの暗号タイプであり、Azure システムによって自動的に設定されます。
b. Azure Key Vault 管理のキー - Azure ポータルからコンテナの Key Vault キーを設定できます。
2. クライアント側 - 次の 2 つのオプションから選択します。
a. clientSideEncryption-keyvault - この暗号タイプでは Azure Key Vault 管理のキーが使用されます。
b. clientSideEncryption - この暗号タイプでは顧客管理のキーが使用されます。
クライアント側暗号用の委任実装の詳細については、「クライアント側暗号用の委任実装の記述」のセクションを参照してください。
|
暗号オプションの管理はユーザーが行います。暗号オプションを変更した場合には、そのコンテンツにアクセスできることをユーザーが確認する必要があります。
|
クライアント側暗号用の委任実装の記述
Azure Blob Storage でのコンテンツの暗号をカスタマイズするには、次のような委任の実装を記述します。
1. Blob 暗号コンフィギュレータ委任 - デフォルトの委任は
com.ptc.windchill.objectstorage.azureblob.encryption.DefaultBlobEncryptConfigurator です。このデフォルトの委任は暗号タイプとして
serverSideEncryption を返します。別のタイプの暗号を使用する場合、必要な暗号タイプを返す委任を記述し、クラスファイルを展開し、コマンドラインツールを使用して設定します。コンフィギュレーションの詳細については、
Azure Blob Storage を使用するための Windchill ファイルボルトの設定を参照してください。
サンプルの委任を以下に示します。
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 - 顧客提供のキーによるクライアント側暗号を使用してコンテンツが保管されます。
2. CSEKeyVaultKeyProvider Delegate - この委任は、暗号タイプが
clientSideEncryption-keyvault として設定されている場合に適用されます。この委任にはデフォルトの既成の実装はありません。想定される Azure キーボルト接続パラメータを返す委任を記述し、クラスファイルを展開し、コマンドラインツールを使用して設定します。コンフィギュレーションの詳細については、
Azure Blob Storage を使用するための Windchill ファイルボルトの設定を参照してください。
|
次のサンプルの実装を本番環境でそのまま使用しないでください。
|
サンプルの委任を以下に示します。
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 を管理する委任を記述し、クラスファイルを展開し、コマンドラインツールを使用して設定します。コンフィギュレーションの詳細については、
Azure Blob Storage を使用するための Windchill ファイルボルトの設定を参照してください。
サンプルの委任を以下に示します。
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. 再ボルトスケジュールを実行します。