AWS Encryption SDK for .NET がGAになったので使ってみた

2022.05.23

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

いわさです。

先日、AWS Encryption SDK for .NET がGAとなりました。

サーバーへデータを送信する前などにクライアントサイドでの暗号化を行う際には、AWS Encryption SDKを活用することが出来ます。
例えば、通常自前で暗号化/復号化を実装する場合はマスターキーやデータキー、ローテーションなど考慮しなければならないことが多いですが、そのあたりはSDKにお任せして暗号化と復号化の仕組みを利用する部分の実装に専念することが出来ます。

従来まで.NETではAmazon S3のクライアントサイド暗号化を行う際には以下のようなパッケージも提供されていました。

しかし、データベースへ格納するデータなど汎用的なデータに関しては、AWS Encryption SDK を利用する必要があったのですが、これまでは.NET/.NET Coreでは利用することは出来ませんでした。(C, Java, JavaScript, Pythonのみ)

今回のアップデートで.NET Frameworkでも.NET Coreでも利用できるようになりました。 Windowsで.NET Frameworkを利用する場合は 4.5.2~4.8がサポートされています。
クロスプラットフォームで利用する場合は、.NETCore3.0以降および.NET5.0以降がサポートされています。

つかってみる

本日は、AWS Encryption SDKの最も基本的な利用方法である、単独のKMSを使った暗号化と復号化を行ってみたいと思います。
事前にKMSでキーを作成しておきます。

事前にクライアントではKMSへアクセス可能なIAM認証情報も構成をしておきます。

利用する.NETクライアント側ではプラットフォームの制限はありません。
この記事では.NET6のコンソールアプリに追加してみたいと思います。

C:\Users\iwasa.takahito\hoge>dotnet new console

.NET 6.0 へようこそ!
---------------------
SDK バージョン: 6.0.202

テレメトリ
---------
.NET ツールは、エクスペリエンスの向上のために利用状況データを収集します。データは Microsoft によって収集され、コミュニティと共有されます。テレメトリをオプトアウトするには、好みのシェルを使用して、DOTNET_CLI_TELEMETRY_OPTOUT 環境変数を '1' または 'true' に設定できます。

.NET CLI ツールのテレメトリの詳細をご覧ください: https://aka.ms/dotnet-cli-telemetry

----------------
ASP.NET Core の HTTPS 開発証明書をインストールしました。
証明書を信頼するには、'dotnet dev-certs https --trust' (Windows および macOS のみ) を実行します。
HTTPS の詳細については、https://aka.ms/dotnet-https を参照してください
----------------
最初のアプリを作成するには、https://aka.ms/dotnet-hello-world を参照してください
最新情報については、https://aka.ms/dotnet-whats-new を参照してください
ドキュメントを探索するには、https://aka.ms/dotnet-docs を参照してください
GitHub で問題の報告とソースの検索を行うには、https://github.com/dotnet/core を参照してください
'dotnet --help' を使用して使用可能なコマンドを確認するか、https://aka.ms/dotnet-cli にアクセスしてください
--------------------------------------------------------------------------------------
テンプレート "コンソール アプリ" が正常に作成されました。

作成後の操作を処理しています...
C:\Users\iwasa.takahito\hoge\0523dotnet.csproj で ' dotnet restore ' を実行しています...
  復元対象のプロジェクトを決定しています...
  C:\Users\iwasa.takahito\hoge\0523dotnet.csproj を復元しました (52 ms)。
正常に復元されました。

C:\Users\iwasa.takahito\hoge>dotnet run
Hello, World!

パッケージ導入

NuGetパッケージマネージャーを使って導入が出来ます。

PS C:\Users\iwasa.takahito\hoge> dotnet add package AWS.EncryptionSDK --version 3.0.0
  復元対象のプロジェクトを決定しています...
  Writing C:\Users\iwasa.takahito\AppData\Local\Temp\tmp90DB.tmp
info : パッケージ 'AWS.EncryptionSDK' の PackageReference をプロジェクト 'C:\Users\iwasa.takahito\hoge\0523dotnet.csproj' に追加しています。
info : C:\Users\iwasa.takahito\hoge\0523dotnet.csproj のパッケージを復元しています...

:

info : MSBuild ファイル C:\Users\iwasa.takahito\hoge\obj\0523dotnet.csproj.nuget.g.props を生成しています。
info : アセット ファイルをディスクに書き込んでいます。パス: C:\Users\iwasa.takahito\hoge\obj\project.assets.json
log  : C:\Users\iwasa.takahito\hoge\0523dotnet.csproj を復元しました (6.4 sec)。

AWS Encryption SDKではKMSを使わない暗号化/復号化も可能ですが、モジュールがKMSに依存しているのでAWS SDK .NETも自動でインストールされます。

暗号化

以下が暗号化の処理です。
EncryptInputに暗号化のための情報を設定します。オプションで暗号化コンテキストを設定することが出来ます。オプションではありますがセキュリティ向上のために推奨されています。
Plaintextという名称で騙されそうになりましたが、MemoryStream型なのでご注意ください。

main.cs

using System.Text;
using Amazon.KeyManagementService;
using AWS.EncryptionSDK;
using AWS.EncryptionSDK.Core;

var encryptionSdk = AwsEncryptionSdkFactory.CreateDefaultAwsEncryptionSdk();
var materialProviders = 
    AwsCryptographicMaterialProvidersFactory.CreateDefaultAwsCryptographicMaterialProviders();

var kmsKeyringInput = new CreateAwsKmsKeyringInput
{
    KmsClient = new AmazonKeyManagementServiceClient(),
    KmsKeyId = "arn:aws:kms:ap-northeast-1:123456789012:key/b4863dc1-8226-44fc-b17d-48b889ac20d8"
};
var keyring = materialProviders.CreateAwsKmsKeyring(kmsKeyringInput);

var encryptionContext = new Dictionary<string, string>()
{
    {"hogecontext", "classmethod"}
};

var plaintext = new MemoryStream(Encoding.UTF8.GetBytes("hogehogeiwasa"));
var encryptInput = new EncryptInput
{
    Plaintext = plaintext,
    Keyring = keyring,
    EncryptionContext = encryptionContext
};

var encryptOutput = encryptionSdk.Encrypt(encryptInput);
Console.WriteLine(Convert.ToBase64String(encryptOutput.Ciphertext.ToArray()));

暗号化されたものはCiphertextから取得出来ます。
バイナリになっているので、ここではBase64エンコード文字列に変換しています。

PS C:\Users\iwasa.takahito\hoge> dotnet run
AgV43aXoEhtLPpy1TBHnjGuAo+XZh6AoLmysa2W6ZnEA0RgAeQACABVhd3MtY3J5cHRvLXB1YmxpYy1rZXkAREFxTjVxNWxGbjBpUytSY1J6aHpuUDB0cXBNQ0xNVU9uMFplRG1wVVFzR1E3RGdUMlNpanBZWVlqTUFiL1RuNEExdz09AAtob2dlY29udGV4dAALY2xhc3NtZXRob2QAAQAHYXdzLWttcwBQYXJuOmF3czprbXM6YXAtbm9ydGhlYXN0LTE6NTUwNjY5NDY3MDg4OmtleS9iNDg2M2RjMS04MjI2LTQ0ZmMtYjE3ZC00OGI4ODlhYzIwZDgAuAECAQB4YFQ7+Up8FKUs+nRvhllvFeLWmDp8pZbCQxfuDo+c05YB+coN1/StV72MSpbiODfvxwAAAH4wfAYJKoZIhvcNAQcGoG8wbQIBADBoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDI6JeacSBnpkr8pwKAIBEIA7mgpmiOnVSBBxVAGqDqu+L3yfMwtpLnbDqXGtVyFZU3w85ELM4GG6DqKxjR5ZLUOTgp8tU364DUdjzt0CAAAQAAaSUjaSg6rAXOR9twaAKOMXkCjwaUvAvKuOEFYZ4rM8PNwa6BvwgVSOwB1eKLkH6/////8AAAABAAAAAAAAAAAAAAABAAAADa0OUao8e6RqR2J+NP83/1H0ZkFLyBRmh+xZcSVwAGcwZQIwZWI62xoHVO3vkTXNYbDb1t1koj51jwMfw8IjZNsRmzwl3U6xzhRQX9FwPSYFpF/wAjEA2A4zniBC9qrWGFZIUyoygN5Os+1OsEKxNVP5h5XosKyS+VrzH2CsWH9xeEXPwMxh

復号化

次に、先程のBase64エンコード文字列を、同じKMSキーを使って復号化してみましょう。
クライアントSDKやキー情報は同じものを使います。

main.cs

using System.Text;
using Amazon.KeyManagementService;
using AWS.EncryptionSDK;
using AWS.EncryptionSDK.Core;

var encryptionSdk = AwsEncryptionSdkFactory.CreateDefaultAwsEncryptionSdk();
var materialProviders = 
    AwsCryptographicMaterialProvidersFactory.CreateDefaultAwsCryptographicMaterialProviders();
var kmsKeyringInput = new CreateAwsKmsKeyringInput
{
    KmsClient = new AmazonKeyManagementServiceClient(),
    KmsKeyId = "arn:aws:kms:ap-northeast-1:123456789012:key/b4863dc1-8226-44fc-b17d-48b889ac20d8"
};
var keyring = materialProviders.CreateAwsKmsKeyring(kmsKeyringInput);

var encryptBase64 = "AgV43aXoEhtLPpy1TBHnjGuAo+XZh6AoLmysa2W6ZnEA0RgAeQACABVhd3MtY3J5cHRvLXB1YmxpYy1rZXkAREFxTjVxNWxGbjBpUytSY1J6aHpuUDB0cXBNQ0xNVU9uMFplRG1wVVFzR1E3RGdUMlNpanBZWVlqTUFiL1RuNEExdz09AAtob2dlY29udGV4dAALY2xhc3NtZXRob2QAAQAHYXdzLWttcwBQYXJuOmF3czprbXM6YXAtbm9ydGhlYXN0LTE6NTUwNjY5NDY3MDg4OmtleS9iNDg2M2RjMS04MjI2LTQ0ZmMtYjE3ZC00OGI4ODlhYzIwZDgAuAECAQB4YFQ7+Up8FKUs+nRvhllvFeLWmDp8pZbCQxfuDo+c05YB+coN1/StV72MSpbiODfvxwAAAH4wfAYJKoZIhvcNAQcGoG8wbQIBADBoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDI6JeacSBnpkr8pwKAIBEIA7mgpmiOnVSBBxVAGqDqu+L3yfMwtpLnbDqXGtVyFZU3w85ELM4GG6DqKxjR5ZLUOTgp8tU364DUdjzt0CAAAQAAaSUjaSg6rAXOR9twaAKOMXkCjwaUvAvKuOEFYZ4rM8PNwa6BvwgVSOwB1eKLkH6/////8AAAABAAAAAAAAAAAAAAABAAAADa0OUao8e6RqR2J+NP83/1H0ZkFLyBRmh+xZcSVwAGcwZQIwZWI62xoHVO3vkTXNYbDb1t1koj51jwMfw8IjZNsRmzwl3U6xzhRQX9FwPSYFpF/wAjEA2A4zniBC9qrWGFZIUyoygN5Os+1OsEKxNVP5h5XosKyS+VrzH2CsWH9xeEXPwMxh";
var decryptInput = new DecryptInput
{
    Ciphertext = new MemoryStream(Convert.FromBase64String(encryptBase64)),
    Keyring = keyring
};
var decryptOutput = encryptionSdk.Decrypt(decryptInput);

if(!decryptOutput.EncryptionContext.TryGetValue("hogecontext", out var decryptedValue))
    throw new Exception("not-include hogecontext");
Console.WriteLine(decryptedValue);

Console.WriteLine(Encoding.UTF8.GetString(decryptOutput.Plaintext.ToArray()));

暗号化されたメッセージのヘッダーから暗号化コンテキストを抽出することが出来ます。
暗号化コンテキストはオプションであり必須ではありませんが、セキュリティ向上のために暗号化コンテキストを含めることが推奨であり、ベストプラクティスとされています。
言語ごとにSDKを使った復号化を実装する場合は、独自にコンテキストのキー値が存在することを検証します。
AWS Encryption SDK CLIに関しては、復号化時に暗号化コンテキストを指定することで検証させることが出来ます。

PS C:\Users\iwasa.takahito\hoge> dotnet run
classmethod
hogehogeiwasa

さいごに

本日は、GAとなった AWS Encryption SDK for .NET を使った基本的な暗号化/復号化の方法を紹介しました。
マルチキーリング、検出キーリングなどの多様な機能もあるので今後それらの使い方も紹介出来ればと思っています。

なお、.NETの場合は、他のプログラミング言語の場合と異なり以下がサポートされていないのでご注意ください。

  • データキーキャッシュ
  • ストリーミングデータへの利用
  • Encryption SDK for .NETのロギング/スタックトレース

また、以下ではAWS Encryption SDK for .NET の公式サンプルも用意されていますので、実装時の参考にしてください。