以前、Always Encryptedで暗号化されたカラムを復号するサンプルプログラムについて記載したが、今回は、システム割り当てマネージドIDを利用して同等の処理を実行してみたので、そのサンプルプログラムを共有する。
なお、マネージド ID とは、Azureリソース自身で認証を行うことのできる、Azure ADで管理されるIDのことをいう。マネージドIDを利用すると、サービスプリンシパルの利用は不要になり、application.propertiesからサービスプリンシパルのクライアントID・パスワードを削除することができる。
前提条件
下記記事の実装が完了していること。
また、Azure FunctionsのKey Vaultのアクセスポリシーが以下のようになっていること。


作成したサンプルプログラム(Azure Functions側)の内容
作成したサンプルプログラム(Azure Functions側)の構成は以下の通り。なお、App Service側のプログラムは修正していない。

なお、上記の赤枠は、前提条件のプログラムから追加・変更したプログラムである。
<2021年4月13日 追記>
spring-cloud-function-dependenciesのバージョンは、2021年3月16日にリリースしたバージョン3.1.2を利用すると、1つのAzure Functions内に複数のファンクションを含む場合の不具合が解消できている。
その場合、Handlerクラスの継承するクラスを「AzureSpringBootRequestHandler」クラスから「FunctionInvoker」クラスに変更する。
spring-cloud-function-dependenciesの3.1.2を利用した実装サンプルは、以下の記事を参照のこと。
pom.xmlの内容は以下の通りで、AppServiceMSICredentialsクラスを利用するための以下の設定を追加している。
<!-- AppServiceMSICredentialsクラスを利用するための設定 -->
<dependency>
<groupId>com.microsoft.azure</groupId>
<artifactId>azure</artifactId>
<version>1.36.3</version>
</dependency>また、application.propertiesの内容は以下の通りで、サービスプリンシパルを利用してKey Vaultにアクセスするための設定を削除している。
# DB接続設定 spring.datasource.url=jdbc:sqlserver://azure-db-purinit.database.windows.net:1433;database=azureSqlDatabase;columnEncryptionSetting=Enabled #spring.datasource.url=jdbc:sqlserver://azure-db-purinit.database.windows.net:1433;database=azureSqlDatabase spring.datasource.username=purinit@azure-db-purinit spring.datasource.password=(DBのパスワード) spring.datasource.driverClassName=com.microsoft.sqlserver.jdbc.SQLServerDriver
さらに、Azure Functionsのメインクラスは以下の通りで、postConstructメソッドのakvProviderオブジェクトの生成方法を変更している。
package com.example;
import java.io.IOException;
import java.net.URISyntaxException;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import com.example.model.GetUserPassParam;
import com.example.model.GetUserPassResult;
import com.example.service.GetUserPassService;
import com.microsoft.azure.AzureEnvironment;
import com.microsoft.azure.credentials.AppServiceMSICredentials;
import com.microsoft.sqlserver.jdbc.SQLServerColumnEncryptionAzureKeyVaultProvider;
import com.microsoft.sqlserver.jdbc.SQLServerColumnEncryptionKeyStoreProvider;
import com.microsoft.sqlserver.jdbc.SQLServerConnection;
import com.microsoft.sqlserver.jdbc.SQLServerException;
import com.microsoft.sqlserver.jdbc.SQLServerKeyVaultAuthenticationCallback;
@SpringBootApplication
public class DemoAzureFunction {
/** Key Vaultの認証の接続設定が終わっているかどうかを判定するフラグ */
private static boolean keyVaultProviderFlg = false;
/** USER_PASSデータ取得サービスクラスのオブジェクト */
@Autowired
private GetUserPassService getUserPassService;
public static void main(String[] args) throws Exception {
SpringApplication.run(DemoAzureFunction.class, args);
}
/**
* Key Vaultの認証の接続設定を登録する
* @throws SQLException SQL例外
* @throws URISyntaxException URI文法エラー
*/
@PostConstruct
public void postConstruct() throws SQLException, URISyntaxException {
if (!keyVaultProviderFlg) {
SQLServerColumnEncryptionAzureKeyVaultProvider akvProvider
= new SQLServerColumnEncryptionAzureKeyVaultProvider(
tryAuthenticationCallback());
Map<String, SQLServerColumnEncryptionKeyStoreProvider> keyStoreMap
= new HashMap<String, SQLServerColumnEncryptionKeyStoreProvider>();
keyStoreMap.put(akvProvider.getName(), akvProvider);
SQLServerConnection.registerColumnEncryptionKeyStoreProviders(keyStoreMap);
keyVaultProviderFlg = true;
}
}
/**
* USER_PASSテーブルのデータを取得し結果を返却する関数
* @return USER_PASSテーブルデータ取得サービスクラスの呼出結果
*/
@Bean
public Function<GetUserPassParam, GetUserPassResult> getUserPass() {
return getUserPassParam -> getUserPassService.getUserPass(getUserPassParam);
}
/**
* SQLServerKeyVaultAuthenticationCallbackオブジェクトを生成する
* @return SQLServerKeyVaultAuthenticationCallbackオブジェクト
* @throws URISyntaxException URI文法エラー
* @throws SQLServerException SQLServerエラー
*/
private static SQLServerKeyVaultAuthenticationCallback tryAuthenticationCallback()
throws URISyntaxException, SQLServerException {
SQLServerKeyVaultAuthenticationCallback authenticationCallback
= new SQLServerKeyVaultAuthenticationCallback() {
@Override
public String getAccessToken(String authority, String resource
, String scope) {
AppServiceMSICredentials credentials
= new AppServiceMSICredentials(AzureEnvironment.AZURE);
try {
return credentials.getToken(resource);
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
};
return authenticationCallback;
}
}その他のソースコード内容は、以下のサイトを参照のこと。
https://github.com/purin-it/azure/tree/master/always-encrypted-decrypt-2/demoAzureFunc
サンプルプログラムの実行結果
サンプルプログラムの実行結果は、以下の通り。
1) App Service側のapplication.propertiesは以下の通り。

2) Azure Functions側のapplication.propertiesは以下の通り。

3)「mvn azure-functions:deploy」コマンドによって、Azure Functions上にサンプルプログラムをデプロイする。

なお、Azure Functionsにデプロイする過程は、以下の記事の「Azure FunctionsへのSpring Bootを利用したJavaアプリケーションのデプロイ」を参照のこと。
4) Azure App ServiceのURL「https://azureappdemoservice.azurewebsites.net/」とアクセスした場合の実行結果は、以下の通り。

なお、上記URLは、下記Azure App ServiceのURLから確認できる。

5)「getUserPassの値を取得」ボタンを押下すると、以下の画面が表示され、Always Encryptedで暗号化したカラムが復号化し表示されることが確認できる。


要点まとめ
- SQLServerColumnEncryptionAzureKeyVaultProviderクラスのコンストラクタの引数に、SQLServerKeyVaultAuthenticationCallbackクラスのオブジェクトを指定することで、システム割り当てマネージドIDを利用してAlways Encryptedで暗号化されたカラムを復号することができる。





