7
1

More than 1 year has passed since last update.

Azure Functions (App Service) 上で X509Certificate2 クラスを作成するときには

Last updated at Posted at 2021-12-14

結論

アプリケーション設定より、 WEBSITE_LOAD_USER_PROFILE = 1 と設定しましょう。
設定しない場合、下記のようなエラーメッセージが出力され、インスタンス作成に失敗する可能性があります。

Exception calling ".ctor" with "3" argument(s): "The system cannot find the file specified.

※ この記事は Microsoft Azure Tech Advent Calendar 2021 の 15 日目の記事です。

事象

例えば、 Azure Key Vault から証明書情報を読み込み、 X509Certificate2 クラスを作成するような Azure Functions の関数があったとします。
アプリケーション設定で WEBSITE_LOAD_USER_PROFILE = 1 を設定していない場合、関数を実行すると下記のエラーが発生してしまう場合があります。

Exception calling ".ctor" with "3" argument(s): "The system cannot find the file specified.

エラー メッセージから見ると、引数の証明書情報に不備があるように見えますが、 WEBSITE_LOAD_USER_PROFILE = 1 が設定されていない場合の Azure Functions (App Service) の想定された挙動となりますのでお気を付けください。

サンプル

関数の実行環境として Powershell を選択した場合のサンプル コードは下記のようなものです。
内容はシンプルで Azure Key Vault から証明書の情報を取得し、それに基づいてX509Certificate2 クラスを作成するというものです。

$containerName = '<キーコンテナ名>'

$certName = '<証明書名>'

$certificate = Get-AzKeyVaultCertificate -VaultName $containerName -Name $certName

$secret = Get-AzKeyVaultSecret -VaultName $vaultName -Name $certificate.Name

$secretText = '';

$ptr = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($secret.SecretValue)

try {

    $secretValueText = [System.Runtime.InteropServices.Marshal]::PtrToStringBSTR($ptr)

}

finally {

    [System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($ssPtr)

}

$secretByte = [Convert]::FromBase64String($secretText)

$Certificate = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2($secretByte, '', 'Exportable,PersistKeySet')

なお、上記テスト コードを実行する際には Get-AzKeyVaultCertificate Get-AzKeyVaultSecret コマンドを利用できるよう、依存関係を追加する必要があります。
下記のドキュメントを参考に host.json および requirements.psd1 を編集してください。

なお、 host.json はデフォルトでmanagedDependency プロパティが true になっています。
また、requirements.psd1 はデフォルトで Az コマンドがコメント アウトされているので、コメント インすれば OK です。
Azure ポータルから、[アプリ ファイル] から編集可能です (画像はコメント イン後)。

image.png

依存関係の追加後、 Azure Functions を再起動しましょう。再起動後、追加された依存関係のダウンロードが開始されます。

※上記サンプルは KeyVault にアクセスする際に必要な認証情報は省略しております。
手軽に設定したい場合は Functions にマネージド ID を付与する方法が簡単です。
1. ID ブレードからシステム割り当てマネージド ID をオンに
image.png
2. KeyVault 側でアクセス ポリシー ブレードより 1 で登録したマネージド ID に対してアクセス許可を付与する
image.png
3. 関数コード冒頭に Connect-AzAccount -Identity を追加する

実行結果

依存関係を追加して再起動後、上記の関数コードを実行し、結果を[モニター]より確認するとエラーが発生していることがわかります。

image.png

原因

WEBSITE_LOAD_USER_PROFILE = 1 が設定されていない場合、 Functions(App Service) ホストはユーザー プロファイルおよびその配下の証明書ストアの情報をロードしません。
しかし、 X509Certificate2 クラスのコンストラクタはユーザー プロファイル内から証明書情報をインポートしようとします。
その結果、上述のエラーが発生した、ということになります。

ちなみに下記の記事にある通り、 X509Certificate2 クラスを作成した際に作成される証明書情報はデフォルトでは X509KeyStorageFlags.UserKeySet が設定されているためユーザー プロファイル内の証明書ストアに保存されます。

まとめ

Azure Functions (App Service) 上で X509Certificate2 クラスを作成するときには、アプリケーション設定よりWEBSITE_LOAD_USER_PROFILE = 1 を設定すると、不可解なエラーに遭遇せず済む場合がありますので、ご検討ください。
局所的な内容となってしまいましたが、こちらの記事がエラーで困っているどなたかの助けとなればと思います。

参考文献

7
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
1