mirror of https://github.com/dapr/dotnet-sdk.git
Implementing Cryptography building block in .NET (#1217)
* Added method to DaprClient and GRPC implementation to call cryptography proto endpoints Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * First pass at implementing all exposed Cryptography methods on Go interface Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Added examples for Cryptography block Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Added missing copyright statements Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Updated to properly support Crypto API this time Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Added copyright statements Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Removed deprecated examples as the subtle APIs are presently disabled Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Updated example to reflect new API shape Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Updated example and readme Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Added overloads for encrypting/decrypting streams instead of just fixed byte arrays. Added example demonstrating the same encrypting a file via a FileStream and decrypting from a MemoryStream. Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Added some unit tests to pair with the implementation Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Added null check for the stream argument Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Changed case of the arguments as they should read "plaintext" and not "plainText" Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Reduced number of encryption implementations by just wrapping byte array into memory stream Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Constrainted returned member types per review suggestion Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Updated methods to use ReadOnlyMemory<byte> instead of byte[] - updated implementations to use low-allocation spans where possible (though ToArray is necessary to wrap with MemoryStream). Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Updated to use encryption/decryption options instead of lots of method overload variations. Simplified gRPC implementation to use fewer methods. Applied argument name updates applied previously (plainText -> plaintext). Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Updated tests Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Removed unused reference Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Updated examples to reflect new method shapes. Downgraded package to .net 6 instead of .net 8 per review suggestion. Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Updated to reflect non-aliased values per review suggestion Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Update to ensure that both send/receive streams run at the same time instead of sequentially. Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Updated to support streamed results in addition to fixed byte arrays. Refactored implementation to minimize duplicative code. Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Updated example to fix compile issue Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Removed encrypt/decrypt methods that accepted streams and returned ReadOnlyMemory<byte>. Marked implementations that use this on the gRPC class as private instead. Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Added missing Obsolete attributes on Encrypt/Decrypt methods. Added overloads on decrypt methods that do not require a DecryptionOptions to be passed in. Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Updated encrypt/decrypt options so the streaming block size no longer uses a uint. Added validation in its place to ensure the value provided is never less than or equal to 0. Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Updated how validation works in the options to accommodate lack of the shorter variation in .NET 6 Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Updated names of encrypt/decrypt streaming methods so everything uses just EncryptAsync or DecryptAsync Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Fixed regression that would have prevented data from being sent entirely to the sidecar. Also simplified operation per suggestion in review. Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Updated examples to reflect changed API Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Updated so IAsyncEnumerable methods (encrypt and decrypt) return IAsyncEnumerable<ReadOnlyMemory<byte>> instead of IAsyncEnumerable<byte[]>. Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Updated example to reflect change from IAsyncEnumerable<byte> to IAsyncEnumerable<ReadOnlyMemory<byte>> Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Avoiding allocation by using MemoryMarshal instead of .ToArray() to create MemoryStream from ReadOnlyMemory<byte>. Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Performance updates to minimize unnecessary byte array copies and eliminate unnecessary allocations. Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Removed unnecessary return from SendPlaintextStreamAsync and SendCiphertextStreamAsync methods Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Updated exception text to be more specific as to what's wrong with the input value. Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Minor tweak to prefer using using a Memory Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Deduplicated some of the Decrypt methods, simplifying the implementation Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Eliminated duplicate encryption method, simplifying implementation Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Updated to eliminate an unnecessary `await` and `async foreach`. Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Updated stream example to reflect the changes to the API shape Signed-off-by: Whit Waldo <whit.waldo@innovian.net> * Added notes about operations with stream-based data Signed-off-by: Whit Waldo <whit.waldo@innovian.net> --------- Signed-off-by: Whit Waldo <whit.waldo@innovian.net>
This commit is contained in:
parent
348d1430ba
commit
ca2fab2567
11
all.sln
11
all.sln
|
@ -104,6 +104,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BulkPublishEventExample", "
|
|||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowUnitTest", "examples\Workflow\WorkflowUnitTest\WorkflowUnitTest.csproj", "{8CA09061-2BEF-4506-A763-07062D2BD6AC}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cryptography", "examples\Client\Cryptography\Cryptography.csproj", "{C74FBA78-13E8-407F-A173-4555AEE41FF3}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
@ -248,6 +250,14 @@ Global
|
|||
{DDC41278-FB60-403A-B969-2AEBD7C2D83C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{8CA09061-2BEF-4506-A763-07062D2BD6AC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{8CA09061-2BEF-4506-A763-07062D2BD6AC}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C74FBA78-13E8-407F-A173-4555AEE41FF3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{C74FBA78-13E8-407F-A173-4555AEE41FF3}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C74FBA78-13E8-407F-A173-4555AEE41FF3}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C74FBA78-13E8-407F-A173-4555AEE41FF3}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{D1786E2B-CAA0-4B2D-A974-9845EB9E420F}.Debug|Any CPU.ActiveCfg = Debug
|
||||
{D1786E2B-CAA0-4B2D-A974-9845EB9E420F}.Debug|Any CPU.Build.0 = Debug
|
||||
{D1786E2B-CAA0-4B2D-A974-9845EB9E420F}.Release|Any CPU.ActiveCfg = Release
|
||||
{D1786E2B-CAA0-4B2D-A974-9845EB9E420F}.Release|Any CPU.Build.0 = Release
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
@ -293,6 +303,7 @@ Global
|
|||
{4A175C27-EAFE-47E7-90F6-873B37863656} = {0EF6EA64-D7C3-420D-9890-EAE8D54A57E6}
|
||||
{DDC41278-FB60-403A-B969-2AEBD7C2D83C} = {0EF6EA64-D7C3-420D-9890-EAE8D54A57E6}
|
||||
{8CA09061-2BEF-4506-A763-07062D2BD6AC} = {BF3ED6BF-ADF3-4D25-8E89-02FB8D945CA9}
|
||||
{C74FBA78-13E8-407F-A173-4555AEE41FF3} = {A7F41094-8648-446B-AECD-DCC2CC871F73}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {65220BF2-EAE1-4CB2-AA58-EBE80768CB40}
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
apiVersion: dapr.io/v1alpha1
|
||||
kind: Component
|
||||
metadata:
|
||||
name: azurekeyvault
|
||||
spec:
|
||||
type: crypto.azure.keyvault
|
||||
metadata:
|
||||
- name: vaultName
|
||||
value: "<changeMe>"
|
||||
- name: azureEnvironment
|
||||
value: AZUREPUBLICCLOUD
|
||||
- name: azureTenantId
|
||||
secretKeyRef:
|
||||
name: read_azure_tenant_id
|
||||
key: read_azure_tenant_id
|
||||
- name: azureClientId
|
||||
secretKeyRef:
|
||||
name: read_azure_client_id
|
||||
key: read_azure_client_id
|
||||
- name: azureClientSecret
|
||||
secretKeyRef:
|
||||
name: read_azure_client_secret
|
||||
key: read_azure_client_secret
|
||||
auth:
|
||||
secureStore: envvar-secret-store
|
|
@ -0,0 +1,7 @@
|
|||
apiVersion: dapr.io/v1alpha1
|
||||
kind: Component
|
||||
metadata:
|
||||
name: envvar-secret-store
|
||||
spec:
|
||||
type: secretstores.local.env
|
||||
version: v1
|
|
@ -0,0 +1,25 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<LangVersion>latest</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Remove="C:\Users\whit_\source\repos\dapr-dotnet-sdk\properties\\IsExternalInit.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\src\Dapr.Client\Dapr.Client.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="file.txt">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -0,0 +1,22 @@
|
|||
// ------------------------------------------------------------------------
|
||||
// Copyright 2023 The Dapr Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
namespace Cryptography
|
||||
{
|
||||
internal abstract class Example
|
||||
{
|
||||
public abstract string DisplayName { get; }
|
||||
|
||||
public abstract Task RunAsync(CancellationToken cancellationToken);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
// ------------------------------------------------------------------------
|
||||
// Copyright 2023 The Dapr Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
using System.Buffers;
|
||||
using Dapr.Client;
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
|
||||
namespace Cryptography.Examples
|
||||
{
|
||||
internal class EncryptDecryptFileStreamExample : Example
|
||||
{
|
||||
public override string DisplayName => "Use Cryptography to encrypt and decrypt a file";
|
||||
public override async Task RunAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
using var client = new DaprClientBuilder().Build();
|
||||
|
||||
const string componentName = "azurekeyvault"; // Change this to match the name of the component containing your vault
|
||||
const string keyName = "myKey";
|
||||
|
||||
// The name of the file we're using as an example
|
||||
const string fileName = "file.txt";
|
||||
|
||||
Console.WriteLine("Original file contents:");
|
||||
foreach (var line in await File.ReadAllLinesAsync(fileName, cancellationToken))
|
||||
{
|
||||
Console.WriteLine(line);
|
||||
}
|
||||
Console.WriteLine();
|
||||
|
||||
//Encrypt from a file stream and buffer the resulting bytes to an in-memory buffer
|
||||
await using var encryptFs = new FileStream(fileName, FileMode.Open);
|
||||
|
||||
var bufferedEncryptedBytes = new ArrayBufferWriter<byte>();
|
||||
await foreach (var bytes in (await client.EncryptAsync(componentName, encryptFs, keyName,
|
||||
new EncryptionOptions(KeyWrapAlgorithm.Rsa), cancellationToken))
|
||||
.WithCancellation(cancellationToken))
|
||||
{
|
||||
bufferedEncryptedBytes.Write(bytes.Span);
|
||||
}
|
||||
|
||||
Console.WriteLine($"Encrypted bytes: {Convert.ToBase64String(bufferedEncryptedBytes.GetSpan())}");
|
||||
Console.WriteLine();
|
||||
|
||||
//We'll write to a temporary file via a FileStream
|
||||
var tempDecryptedFile = Path.GetTempFileName();
|
||||
await using var decryptFs = new FileStream(tempDecryptedFile, FileMode.Create);
|
||||
|
||||
//We'll stream the decrypted bytes from a MemoryStream into the above temporary file
|
||||
await using var encryptedMs = new MemoryStream(bufferedEncryptedBytes.WrittenMemory.ToArray());
|
||||
await foreach (var result in (await client.DecryptAsync(componentName, encryptedMs, keyName,
|
||||
cancellationToken)).WithCancellation(cancellationToken))
|
||||
{
|
||||
decryptFs.Write(result.Span);
|
||||
}
|
||||
|
||||
decryptFs.Close();
|
||||
|
||||
//Let's confirm the value as written to the file
|
||||
var decryptedValue = await File.ReadAllTextAsync(tempDecryptedFile, cancellationToken);
|
||||
Console.WriteLine($"Decrypted value: ");
|
||||
Console.WriteLine(decryptedValue);
|
||||
|
||||
//And some cleanup to delete our temp file
|
||||
File.Delete(tempDecryptedFile);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
// ------------------------------------------------------------------------
|
||||
// Copyright 2023 The Dapr Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
using System.Text;
|
||||
using Dapr.Client;
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
|
||||
namespace Cryptography.Examples
|
||||
{
|
||||
internal class EncryptDecryptStringExample : Example
|
||||
{
|
||||
public override string DisplayName => "Using Cryptography to encrypt and decrypt a string";
|
||||
|
||||
public override async Task RunAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
using var client = new DaprClientBuilder().Build();
|
||||
|
||||
const string componentName = "azurekeyvault"; //Change this to match the name of the component containing your vault
|
||||
const string keyName = "myKey"; //Change this to match the name of the key in your Vault
|
||||
|
||||
|
||||
const string plaintextStr = "This is the value we're going to encrypt today";
|
||||
Console.WriteLine($"Original string value: '{plaintextStr}'");
|
||||
|
||||
//Encrypt the string
|
||||
var plaintextBytes = Encoding.UTF8.GetBytes(plaintextStr);
|
||||
var encryptedBytesResult = await client.EncryptAsync(componentName, plaintextBytes, keyName, new EncryptionOptions(KeyWrapAlgorithm.Rsa),
|
||||
cancellationToken);
|
||||
|
||||
Console.WriteLine($"Encrypted bytes: '{Convert.ToBase64String(encryptedBytesResult.Span)}'");
|
||||
|
||||
//Decrypt the string
|
||||
var decryptedBytes = await client.DecryptAsync(componentName, encryptedBytesResult, keyName, new DecryptionOptions(), cancellationToken);
|
||||
Console.WriteLine($"Decrypted string: '{Encoding.UTF8.GetString(decryptedBytes.ToArray())}'");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
// ------------------------------------------------------------------------
|
||||
// Copyright 2023 The Dapr Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
using Cryptography;
|
||||
using Cryptography.Examples;
|
||||
|
||||
namespace Samples.Client
|
||||
{
|
||||
class Program
|
||||
{
|
||||
private static readonly Example[] Examples = new Example[]
|
||||
{
|
||||
new EncryptDecryptStringExample(),
|
||||
new EncryptDecryptFileStreamExample()
|
||||
};
|
||||
|
||||
static async Task<int> Main(string[] args)
|
||||
{
|
||||
if (args.Length > 0 && int.TryParse(args[0], out var index) && index >= 0 && index < Examples.Length)
|
||||
{
|
||||
var cts = new CancellationTokenSource();
|
||||
Console.CancelKeyPress += (object? sender, ConsoleCancelEventArgs e) => cts.Cancel();
|
||||
|
||||
await Examples[index].RunAsync(cts.Token);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Console.WriteLine("Hello, please choose a sample to run:");
|
||||
for (var i = 0; i < Examples.Length; i++)
|
||||
{
|
||||
Console.WriteLine($"{i}: {Examples[i].DisplayName}");
|
||||
}
|
||||
Console.WriteLine();
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
# Dapr .NET SDK Cryptography example
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- [.NET 8+](https://dotnet.microsoft.com/download) installed
|
||||
- [Dapr CLI](https://docs.dapr.io/getting-started/install-dapr-cli)
|
||||
- [Initialized Dapr environment](https://docs.dapr.io/getting-started/installation)
|
||||
- [Dapr .NET SDK](https://docs.dapr.io/developing-applications/sdks/dotnet/)
|
||||
- [Azure Key Vault instance](https://learn.microsoft.com/en-us/azure/key-vault/general/quick-create-portal)
|
||||
- [Entra Service Principal](https://learn.microsoft.com/en-us/entra/identity-platform/quickstart-register-app)
|
||||
|
||||
### Service Principal/Environment Variables Setup
|
||||
In your Azure portal, open Microsoft Entra ID and click `App Registrations`. Click the button at the top to create a new registration. Select a name for your service principal
|
||||
and click register, noting this name for later.
|
||||
|
||||
Once the registration is completed, open it from the list and select Certificates & Secrets from the left navigation. Select "Client secrets" from the page body (middle column)
|
||||
and click the button to add a new client secret giving it an optional description and changing the expiry date as you desire. Click Add to create the secret. Record the secret
|
||||
value it shows you - it will not be shown to you again without creating another client secret.
|
||||
|
||||
Click Overview from the left navigation and record the "Application (client) ID" and the "Directory (tenant) ID" values.
|
||||
|
||||
On your computer (assuming Windows), open your start menu and type "Environment Variables". An option should appear named "Edit the system environment variables". Select this
|
||||
and your System Properties window will open. Click the "Environment Variables" button in the bottom and said window will appear. Click the "New..." button under System variables
|
||||
to add the requisite service principal values to your environment variables. You can change these names as to want by updating the `./Components/azurekeyvault.yaml` names, but for now
|
||||
configure as follows:
|
||||
|
||||
| Variable Name | Value |
|
||||
|--|--|
|
||||
| read_azure_client_id | Paste the value from your app registration overview for "Application (client) ID" |
|
||||
| read_azure_client_secret | Paste the value of the client secret you generated for your app registration |
|
||||
| read_azure_tenant_id | Paste the valeu from your app registration overview for "Directory (tenant) ID" |
|
||||
|
||||
Click OK to save your environment variables and to close your System Properties window. You may need to close restart your command line tool for it to recognize the new values.
|
||||
|
||||
### Azure Key Vault Setup
|
||||
|
||||
This example is implemented using the Azure Key Vault and will not work without it. Assuming you have a Key Vault instance configured, ensure that
|
||||
you have the `Key Vault Crypto Officer` role assigned to yourself as you'll need to in order to generate a new key in the instance. After selecting Keys
|
||||
under the Objects header, click the `Generate/Import` button at the top of the instance panel.
|
||||
|
||||
Under options, select `Generate` and name your key. This example is pre-configured to assume a key name of 'myKey', but feel free to change this (but also update the name in the example
|
||||
you wish to run). The other default options are fine for our purposes, so click Create at the bottom and if you've got the appropriate roles, it will show up in the list of Keys.
|
||||
|
||||
Update your `./Components/azurekeyvault.yaml` file with the name of your Key Vault under `vaultName` where it currently reads "changeMe". This sample assumes authentication
|
||||
via a service principal, so you might also need to set this up.
|
||||
|
||||
Back in the Azure Portal, assign at least the `Key Vault Crypto User` role to the service principal you previously created in the last step. Do this by clicking
|
||||
`Access Control (IAM)` from the left navigation, clicking "Add" from the top and clicking "Add Role Assignment". Select `Key Vault Crypto User` from the list and click the Next
|
||||
button. Ensuring that the "User, group or service principal" option is selected, click the "Select members" link and search for the name of the app registration you created. Click
|
||||
Add to add this service principal to the list of members for the new role assignment and click Review + Assign twice to assign the role. This will take effect within a few seconds
|
||||
or minutes. This step ensures that while Dapr can authenticate as your service principal, that it also has permission to access and use the key in your Key Vault.
|
||||
|
||||
## Running the example
|
||||
|
||||
To run the sample locally, run this command in the DaprClient directory:
|
||||
|
||||
```sh
|
||||
dapr run --resources-path ./Components --app-id DaprClient -- dotnet run <zero-indexed sample number>
|
||||
```
|
||||
|
||||
Running the following command will output a list of the samples included:
|
||||
|
||||
```sh
|
||||
dapr run --resources-path ./Components --app-id DaprClient -- dotnet run
|
||||
```
|
||||
|
||||
Press Ctrl+C to exit, and then run the command again and provide a sample number to run the samples.
|
||||
|
||||
For example, run this command to run the first sample from the list produced earlier (the 0th example):
|
||||
|
||||
```sh
|
||||
dapr run --resources-path ./Components --app-id DaprClient -- dotnet run 0
|
||||
```
|
||||
|
||||
## Encryption/Decryption with strings
|
||||
See [EncryptDecryptStringExample.cs](./EncryptDecryptStringExample.cs) for an example of using `DaprClient` for basic
|
||||
string-based encryption and decryption operations as performed against UTF-8 encoded byte arrays.
|
||||
|
||||
## Encryption/Decryption with streams
|
||||
See [EncryptDecryptFileStreamExample.cs](./EncryptDecryptFileStreamExample.cs) for an example of using `DaprClient`
|
||||
to perform an encrypt and decrypt operation against a stream of data. In the example, we stream a local file to the
|
||||
sidecar to encrypt and write the result (as it's streamed back) to an in-memory buffer. Once the operation fully
|
||||
completes, we perform the decrypt operation against this in-memory buffer and write the decrypted result back out to a
|
||||
temporary file.
|
||||
|
||||
In either operation, rather than load the entire stream into memory and send all at once to the
|
||||
sidecar as we do in the other string-based example (as this might cause you to run out of memory either on the
|
||||
node the app is running on or do the same to the sidecar itself), this example instead breaks the input stream into
|
||||
more manageable 4KB chunks (a value you can override via the `EncryptionOptions` or `DecryptionOptions` parameters
|
||||
respectively up to 64KB. Further, rather than waiting for the entire stream to send to the sidecar before the
|
||||
encryption operation proceeds, it immediately works to process the sidecar response, continuing to minimize resource
|
||||
usage.
|
|
@ -0,0 +1,26 @@
|
|||
# The Road Not Taken
|
||||
## By Robert Lee Frost
|
||||
|
||||
Two roads diverged in a yellow wood,
|
||||
And sorry I could not travel both
|
||||
And be one traveler, long I stood
|
||||
And looked down one as far as I could
|
||||
To where it bent in the undergrowth;
|
||||
|
||||
Then took the other, as just as fair
|
||||
And having perhaps the better claim,
|
||||
Because it was grassy and wanted wear;
|
||||
Though as for that, the passing there
|
||||
Had worn them really about the same,
|
||||
|
||||
And both that morning equally lay
|
||||
In leaves no step had trodden black
|
||||
Oh, I kept the first for another day!
|
||||
Yet knowing how way leads on to way,
|
||||
I doubted if I should ever come back.
|
||||
|
||||
I shall be telling this with a sigh
|
||||
Somewhere ages and ages hence:
|
||||
Two roads diverged in a wood, and I,
|
||||
I took the one less traveled by,
|
||||
And that has made all the difference.
|
|
@ -0,0 +1,76 @@
|
|||
// ------------------------------------------------------------------------
|
||||
// Copyright 2023 The Dapr Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Dapr.Client
|
||||
{
|
||||
/// <summary>
|
||||
/// The cipher used for data encryption operations.
|
||||
/// </summary>
|
||||
public enum DataEncryptionCipher
|
||||
{
|
||||
/// <summary>
|
||||
/// The default data encryption cipher used, this represents AES GCM.
|
||||
/// </summary>
|
||||
[EnumMember(Value = "aes-gcm")]
|
||||
AesGcm,
|
||||
/// <summary>
|
||||
/// Represents the ChaCha20-Poly1305 data encryption cipher.
|
||||
/// </summary>
|
||||
[EnumMember(Value = "chacha20-poly1305")]
|
||||
ChaCha20Poly1305
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// The algorithm used for key wrapping cryptographic operations.
|
||||
/// </summary>
|
||||
public enum KeyWrapAlgorithm
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the AES key wrap algorithm.
|
||||
/// </summary>
|
||||
[EnumMember(Value="A256KW")]
|
||||
Aes,
|
||||
/// <summary>
|
||||
/// An alias for the AES key wrap algorithm.
|
||||
/// </summary>
|
||||
[EnumMember(Value="A256KW")]
|
||||
A256kw,
|
||||
/// <summary>
|
||||
/// Represents the AES 128 CBC key wrap algorithm.
|
||||
/// </summary>
|
||||
[EnumMember(Value="A128CBC")]
|
||||
A128cbc,
|
||||
/// <summary>
|
||||
/// Represents the AES 192 CBC key wrap algorithm.
|
||||
/// </summary>
|
||||
[EnumMember(Value="A192CBC")]
|
||||
A192cbc,
|
||||
/// <summary>
|
||||
/// Represents the AES 256 CBC key wrap algorithm.
|
||||
/// </summary>
|
||||
[EnumMember(Value="A256CBC")]
|
||||
A256cbc,
|
||||
/// <summary>
|
||||
/// Represents the RSA key wrap algorithm.
|
||||
/// </summary>
|
||||
[EnumMember(Value= "RSA-OAEP-256")]
|
||||
Rsa,
|
||||
/// <summary>
|
||||
/// An alias for the RSA key wrap algorithm.
|
||||
/// </summary>
|
||||
[EnumMember(Value= "RSA-OAEP-256")]
|
||||
RsaOaep256 //Alias for RSA
|
||||
}
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
#nullable enable
|
||||
using System;
|
||||
|
||||
namespace Dapr.Client
|
||||
{
|
||||
/// <summary>
|
||||
/// A collection of options used to configure how encryption cryptographic operations are performed.
|
||||
/// </summary>
|
||||
public class EncryptionOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="EncryptionOptions"/>.
|
||||
/// </summary>
|
||||
/// <param name="keyWrapAlgorithm"></param>
|
||||
public EncryptionOptions(KeyWrapAlgorithm keyWrapAlgorithm)
|
||||
{
|
||||
KeyWrapAlgorithm = keyWrapAlgorithm;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The name of the algorithm used to wrap the encryption key.
|
||||
/// </summary>
|
||||
public KeyWrapAlgorithm KeyWrapAlgorithm { get; set; }
|
||||
|
||||
private int streamingBlockSizeInBytes = 4 * 1024; // 4 KB
|
||||
/// <summary>
|
||||
/// The size of the block in bytes used to send data to the sidecar for cryptography operations.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This defaults to 4KB and generally should not exceed 64KB.
|
||||
/// </remarks>
|
||||
public int StreamingBlockSizeInBytes
|
||||
{
|
||||
get => streamingBlockSizeInBytes;
|
||||
set
|
||||
{
|
||||
if (value <= 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(value));
|
||||
}
|
||||
|
||||
streamingBlockSizeInBytes = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The optional name (and optionally a version) of the key specified to use during decryption.
|
||||
/// </summary>
|
||||
public string? DecryptionKeyName { get; set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// The name of the cipher to use for the encryption operation.
|
||||
/// </summary>
|
||||
public DataEncryptionCipher EncryptionCipher { get; set; } = DataEncryptionCipher.AesGcm;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A collection fo options used to configure how decryption cryptographic operations are performed.
|
||||
/// </summary>
|
||||
public class DecryptionOptions
|
||||
{
|
||||
private int streamingBlockSizeInBytes = 4 * 1024; // 4KB
|
||||
/// <summary>
|
||||
/// The size of the block in bytes used to send data to the sidecar for cryptography operations.
|
||||
/// </summary>
|
||||
public int StreamingBlockSizeInBytes
|
||||
{
|
||||
get => streamingBlockSizeInBytes;
|
||||
set
|
||||
{
|
||||
if (value <= 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(value));
|
||||
}
|
||||
|
||||
streamingBlockSizeInBytes = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,7 +3,7 @@
|
|||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
@ -13,6 +13,7 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
|
@ -939,6 +940,286 @@ namespace Dapr.Client
|
|||
string id,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
#region Cryptography
|
||||
|
||||
/// <summary>
|
||||
/// Encrypts an array of bytes using the Dapr Cryptography encryption functionality.
|
||||
/// </summary>
|
||||
/// <param name="vaultResourceName">The name of the vault resource used by the operation.</param>
|
||||
/// <param name="plaintextBytes">The bytes of the plaintext value to encrypt.</param>
|
||||
/// <param name="keyName">The name of the key to use from the Vault for the encryption operation.</param>
|
||||
/// <param name="encryptionOptions">Options informing how the encryption operation should be configured.</param>
|
||||
/// <param name="cancellationToken">A <see cref="CancellationToken"/> that can be used to cancel the operation.</param>
|
||||
/// <returns>An array of encrypted bytes.</returns>
|
||||
[Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
public abstract Task<ReadOnlyMemory<byte>> EncryptAsync(string vaultResourceName,
|
||||
ReadOnlyMemory<byte> plaintextBytes, string keyName, EncryptionOptions encryptionOptions,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Encrypts a stream using the Dapr Cryptography encryption functionality.
|
||||
/// </summary>
|
||||
/// <param name="vaultResourceName">The name of the vault resource used by the operation.</param>
|
||||
/// <param name="plaintextStream">The stream containing the bytes of the plaintext value to encrypt.</param>
|
||||
/// <param name="keyName">The name of the key to use from the Vault for the encryption operation.</param>
|
||||
/// <param name="encryptionOptions">Options informing how the encryption operation should be configured.</param>
|
||||
/// <param name="cancellationToken">A <see cref="CancellationToken"/> that can be used to cancel the operation.</param>
|
||||
/// <returns>An array of encrypted bytes.</returns>
|
||||
[Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
public abstract Task<IAsyncEnumerable<ReadOnlyMemory<byte>>> EncryptAsync(string vaultResourceName, Stream plaintextStream, string keyName,
|
||||
EncryptionOptions encryptionOptions, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Decrypts the specified ciphertext bytes using the Dapr Cryptography encryption functionality.
|
||||
/// </summary>
|
||||
/// <param name="vaultResourceName">The name of the vault resource used by the operation.</param>
|
||||
/// <param name="ciphertextBytes">The bytes of the ciphertext value to decrypt.</param>
|
||||
/// <param name="keyName">The name of the key to use from the Vault for the decryption operation.</param>
|
||||
/// <param name="options">Options informing how the decryption operation should be configured.</param>
|
||||
/// <param name="cancellationToken">A <see cref="CancellationToken"/> that can be used to cancel the operation.</param>
|
||||
/// <returns>An array of decrypted bytes.</returns>
|
||||
[Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
public abstract Task<ReadOnlyMemory<byte>> DecryptAsync(string vaultResourceName, ReadOnlyMemory<byte> ciphertextBytes, string keyName, DecryptionOptions options,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Decrypts the specified ciphertext bytes using the Dapr Cryptography encryption functionality.
|
||||
/// </summary>
|
||||
/// <param name="vaultResourceName">The name of the vault resource used by the operation.</param>
|
||||
/// <param name="ciphertextBytes">The bytes of the ciphertext value to decrypt.</param>
|
||||
/// <param name="keyName">The name of the key to use from the Vault for the decryption operation.</param>
|
||||
/// <param name="cancellationToken">A <see cref="CancellationToken"/> that can be used to cancel the operation.</param>
|
||||
/// <returns>An array of decrypted bytes.</returns>
|
||||
[Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
public abstract Task<ReadOnlyMemory<byte>> DecryptAsync(string vaultResourceName,
|
||||
ReadOnlyMemory<byte> ciphertextBytes, string keyName, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Decrypts the specified stream of ciphertext using the Dapr Cryptography encryption functionality.
|
||||
/// </summary>
|
||||
/// <param name="vaultResourceName">The name of the vault resource used by the operation.</param>
|
||||
/// <param name="ciphertextStream">The stream containing the bytes of the ciphertext value to decrypt.</param>
|
||||
/// <param name="keyName">The name of the key to use from the Vault for the decryption operation.</param>
|
||||
/// <param name="options">Options informing how the decryption operation should be configured.</param>
|
||||
/// <param name="cancellationToken">A <see cref="CancellationToken"/> that can be used to cancel the operation.</param>
|
||||
/// <returns>An asynchronously enumerable array of decrypted bytes.</returns>
|
||||
[Obsolete(
|
||||
"The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
public abstract Task<IAsyncEnumerable<ReadOnlyMemory<byte>>> DecryptAsync(string vaultResourceName, Stream ciphertextStream,
|
||||
string keyName, DecryptionOptions options, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Decrypts the specified stream of ciphertext using the Dapr Cryptography encryption functionality.
|
||||
/// </summary>
|
||||
/// <param name="vaultResourceName">The name of the vault resource used by the operation.</param>
|
||||
/// <param name="ciphertextStream">The stream containing the bytes of the ciphertext value to decrypt.</param>
|
||||
/// <param name="keyName">The name of the key to use from the Vault for the decryption operation.</param>
|
||||
/// <param name="cancellationToken">A <see cref="CancellationToken"/> that can be used to cancel the operation.</param>
|
||||
/// <returns>An asynchronously enumerable array of decrypted bytes.</returns>
|
||||
[Obsolete(
|
||||
"The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
public abstract Task<IAsyncEnumerable<ReadOnlyMemory<byte>>> DecryptAsync(string vaultResourceName, Stream ciphertextStream,
|
||||
string keyName, CancellationToken cancellationToken = default);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Cryptography - Subtle API
|
||||
|
||||
///// <summary>
|
||||
///// Retrieves the value of the specified key from the vault.
|
||||
///// </summary>
|
||||
///// <param name="vaultResourceName">The name of the vault resource used by the operation.</param>
|
||||
///// <param name="keyName">The name of the key to retrieve the value of.</param>
|
||||
///// <param name="keyFormat">The format to use for the key result.</param>
|
||||
///// <param name="cancellationToken">A <see cref="CancellationToken"/> that can be used to cancel the operation.</param>
|
||||
///// <returns>The name (and possibly version as name/version) of the key and its public key.</returns>
|
||||
//[Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
//public abstract Task<(string Name, string PublicKey)> GetKeyAsync(string vaultResourceName, string keyName, SubtleGetKeyRequest.Types.KeyFormat keyFormat,
|
||||
// CancellationToken cancellationToken = default);
|
||||
|
||||
///// <summary>
|
||||
///// Encrypts an array of bytes using the Dapr Cryptography functionality.
|
||||
///// </summary>
|
||||
///// <param name="vaultResourceName">The name of the vault resource used by the operation.</param>
|
||||
///// <param name="plainTextBytes">The bytes of the plaintext value to encrypt.</param>
|
||||
///// <param name="algorithm">The name of the algorithm that should be used to perform the encryption.</param>
|
||||
///// <param name="keyName">The name of the key used to perform the encryption operation.</param>
|
||||
///// <param name="nonce">The bytes comprising the nonce.</param>
|
||||
///// <param name="associatedData">Any associated data when using AEAD ciphers.</param>
|
||||
///// <param name="cancellationToken">A <see cref="CancellationToken"/> that can be used to cancel the operation.</param>
|
||||
///// <returns>The array of encrypted bytes.</returns>
|
||||
//[Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
//public abstract Task<(byte[] CipherTextBytes, byte[] AuthenticationTag)> EncryptAsync(
|
||||
// string vaultResourceName,
|
||||
// byte[] plainTextBytes,
|
||||
// string algorithm,
|
||||
// string keyName,
|
||||
// byte[] nonce,
|
||||
// byte[] associatedData,
|
||||
// CancellationToken cancellationToken = default);
|
||||
|
||||
///// <summary>
|
||||
///// Encrypts an array of bytes using the Dapr Cryptography functionality.
|
||||
///// </summary>
|
||||
///// <param name="vaultResourceName">The name of the vault resource used by the operation.</param>
|
||||
///// <param name="plainTextBytes">The bytes of the plaintext value to encrypt.</param>
|
||||
///// <param name="algorithm">The name of the algorithm that should be used to perform the encryption.</param>
|
||||
///// <param name="keyName">The name of the key used to perform the encryption operation.</param>
|
||||
///// <param name="nonce">The bytes comprising the nonce.</param>
|
||||
///// <param name="cancellationToken">A <see cref="CancellationToken"/> that can be used to cancel the operation.</param>
|
||||
///// <returns>The array of encrypted bytes.</returns>
|
||||
//[Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
//public async Task<(byte[] CipherTextBytes, byte[] AuthenticationTag)> EncryptAsync(
|
||||
// string vaultResourceName,
|
||||
// byte[] plainTextBytes,
|
||||
// string algorithm,
|
||||
// string keyName,
|
||||
// byte[] nonce,
|
||||
// CancellationToken cancellationToken = default) =>
|
||||
// await EncryptAsync(vaultResourceName, plainTextBytes, algorithm, keyName, nonce, Array.Empty<byte>(),
|
||||
// cancellationToken);
|
||||
|
||||
///// <summary>
|
||||
///// Decrypts an array of bytes using the Dapr Cryptography functionality.
|
||||
///// </summary>
|
||||
///// <param name="vaultResourceName">The name of the vault the key is retrieved from for the decryption operation.</param>
|
||||
///// <param name="cipherTextBytes">The array of bytes to decrypt.</param>
|
||||
///// <param name="cancellationToken">A <see cref="CancellationToken"/> that can be used to cancel the operation.</param>
|
||||
///// <param name="algorithm">The algorithm to use to perform the decryption operation.</param>
|
||||
///// <param name="keyName">The name of the key used for the decryption.</param>
|
||||
///// <param name="nonce">The nonce value used.</param>
|
||||
///// <param name="tag"></param>
|
||||
///// <param name="associatedData">Any associated data when using AEAD ciphers.</param>
|
||||
///// <returns>The array of plaintext bytes.</returns>
|
||||
//[Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
//public abstract Task<byte[]> DecryptAsync(string vaultResourceName, byte[] cipherTextBytes,
|
||||
// string algorithm, string keyName, byte[] nonce, byte[] tag, byte[] associatedData,
|
||||
// CancellationToken cancellationToken = default);
|
||||
|
||||
///// <summary>
|
||||
///// Decrypts an array of bytes using the Dapr Cryptography functionality.
|
||||
///// </summary>
|
||||
///// <param name="vaultResourceName">The name of the vault the key is retrieved from for the decryption operation.</param>
|
||||
///// <param name="cipherTextBytes">The array of bytes to decrypt.</param>
|
||||
///// <param name="cancellationToken">A <see cref="CancellationToken"/> that can be used to cancel the operation.</param>
|
||||
///// <param name="algorithm">The algorithm to use to perform the decryption operation.</param>
|
||||
///// <param name="keyName">The name of the key used for the decryption.</param>
|
||||
///// <param name="nonce">The nonce value used.</param>
|
||||
///// <param name="tag"></param>
|
||||
///// <returns>The array of plaintext bytes.</returns>
|
||||
//[Obsolete(
|
||||
// "The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
//public async Task<byte[]> DecryptAsync(string vaultResourceName, byte[] cipherTextBytes,
|
||||
// string algorithm, string keyName, byte[] nonce, byte[] tag, CancellationToken cancellationToken = default) =>
|
||||
// await DecryptAsync(vaultResourceName, cipherTextBytes, algorithm, keyName, nonce, tag, Array.Empty<byte>(), cancellationToken);
|
||||
|
||||
///// <summary>
|
||||
///// Wraps the plaintext key using another.
|
||||
///// </summary>
|
||||
///// <param name="vaultResourceName">The name of the vault to retrieve the key from.</param>
|
||||
///// <param name="plainTextKey">The plaintext bytes comprising the key to wrap.</param>
|
||||
///// <param name="keyName">The name of the key used to wrap the <paramref name="plainTextKey"/> value.</param>
|
||||
///// <param name="algorithm">The algorithm to use to perform the wrap operation.</param>
|
||||
///// <param name="nonce">The none used.</param>
|
||||
///// <param name="associatedData">Any associated data when using AEAD ciphers.</param>
|
||||
///// <param name="cancellationToken">A <see cref="CancellationToken"/> that can be used to cancel the operation.</param>
|
||||
///// <returns>The bytes comprising the wrapped plain-text key and the authentication tag, if applicable.</returns>
|
||||
//[Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
//public abstract Task<(byte[] WrappedKey, byte[] AuthenticationTag)> WrapKeyAsync(string vaultResourceName, byte[] plainTextKey, string keyName, string algorithm, byte[] nonce, byte[] associatedData,
|
||||
// CancellationToken cancellationToken = default);
|
||||
|
||||
///// <summary>
|
||||
///// Wraps the plaintext key using another.
|
||||
///// </summary>
|
||||
///// <param name="vaultResourceName">The name of the vault to retrieve the key from.</param>
|
||||
///// <param name="plainTextKey">The plaintext bytes comprising the key to wrap.</param>
|
||||
///// <param name="keyName">The name of the key used to wrap the <paramref name="plainTextKey"/> value.</param>
|
||||
///// <param name="algorithm">The algorithm to use to perform the wrap operation.</param>
|
||||
///// <param name="nonce">The none used.</param>
|
||||
///// <param name="cancellationToken">A <see cref="CancellationToken"/> that can be used to cancel the operation.</param>
|
||||
///// <returns>The bytes comprising the unwrapped key and the authentication tag, if applicable.</returns>
|
||||
//[Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
//public async Task<(byte[] WrappedKey, byte[] AuthenticationTag)> WrapKeyAsync(string vaultResourceName, byte[] plainTextKey, string keyName, string algorithm,
|
||||
// byte[] nonce, CancellationToken cancellationToken = default) => await WrapKeyAsync(vaultResourceName, plainTextKey,
|
||||
// keyName, algorithm, nonce, Array.Empty<byte>(), cancellationToken);
|
||||
|
||||
///// <summary>
|
||||
///// Used to unwrap the specified key.
|
||||
///// </summary>
|
||||
///// <param name="vaultResourceName">The name of the vault to retrieve the key from.</param>
|
||||
///// <param name="wrappedKey">The byte comprising the wrapped key.</param>
|
||||
///// <param name="algorithm">The algorithm to use in unwrapping the key.</param>
|
||||
///// <param name="keyName">The name of the key used to unwrap the wrapped key bytes.</param>
|
||||
///// <param name="nonce">The nonce value.</param>
|
||||
///// <param name="tag">The bytes comprising the authentication tag.</param>
|
||||
///// <param name="associatedData">Any associated data when using AEAD ciphers.</param>
|
||||
///// <param name="cancellationToken">A <see cref="CancellationToken"/> that can be used to cancel the operation.</param>
|
||||
///// <returns>The bytes comprising the unwrapped key.</returns>
|
||||
//[Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
//public abstract Task<byte[]> UnwrapKeyAsync(string vaultResourceName, byte[] wrappedKey, string algorithm, string keyName, byte[] nonce, byte[] tag, byte[] associatedData,
|
||||
// CancellationToken cancellationToken = default);
|
||||
|
||||
///// <summary>
|
||||
///// Used to unwrap the specified key.
|
||||
///// </summary>
|
||||
///// <param name="vaultResourceName">The name of the vault to retrieve the key from.</param>
|
||||
///// <param name="wrappedKey">The byte comprising the wrapped key.</param>
|
||||
///// <param name="algorithm">The algorithm to use in unwrapping the key.</param>
|
||||
///// <param name="keyName">The name of the key used to unwrap the wrapped key bytes.</param>
|
||||
///// <param name="nonce">The nonce value.</param>
|
||||
///// <param name="tag">The bytes comprising the authentication tag.</param>
|
||||
///// <param name="cancellationToken">A <see cref="CancellationToken"/> that can be used to cancel the operation.</param>
|
||||
///// <returns>The bytes comprising the unwrapped key.</returns>
|
||||
//[Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
//public async Task<byte[]> UnwrapKeyAsync(string vaultResourceName, byte[] wrappedKey, string algorithm, string keyName,
|
||||
// byte[] nonce, byte[] tag,
|
||||
// CancellationToken cancellationToken = default) => await UnwrapKeyAsync(vaultResourceName,
|
||||
// wrappedKey, algorithm, keyName, nonce, Array.Empty<byte>(), Array.Empty<byte>(), cancellationToken);
|
||||
|
||||
///// <summary>
|
||||
///// Used to unwrap the specified key.
|
||||
///// </summary>
|
||||
///// <param name="vaultResourceName">The name of the vault to retrieve the key from.</param>
|
||||
///// <param name="wrappedKey">The byte comprising the wrapped key.</param>
|
||||
///// <param name="algorithm">The algorithm to use in unwrapping the key.</param>
|
||||
///// <param name="keyName">The name of the key used to unwrap the wrapped key bytes.</param>
|
||||
///// <param name="nonce">The nonce value.</param>
|
||||
///// <param name="cancellationToken">A <see cref="CancellationToken"/> that can be used to cancel the operation.</param>
|
||||
///// <returns>The bytes comprising the unwrapped key.</returns>
|
||||
//[Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
//public async Task<byte[]> UnwrapKeyAsync(string vaultResourceName, byte[] wrappedKey, string algorithm, string keyName,
|
||||
// byte[] nonce, CancellationToken cancellationToken = default) => await UnwrapKeyAsync(vaultResourceName,
|
||||
// wrappedKey, algorithm, keyName, nonce, Array.Empty<byte>(), Array.Empty<byte>(), cancellationToken);
|
||||
|
||||
///// <summary>
|
||||
///// Creates a signature of a digest value.
|
||||
///// </summary>
|
||||
///// <param name="vaultResourceName">The name of the vault to retrieve the key from.</param>
|
||||
///// <param name="digest">The digest value to create the signature for.</param>
|
||||
///// <param name="algorithm">The algorithm used to create the signature.</param>
|
||||
///// <param name="keyName">The name of the key used.</param>
|
||||
///// <param name="cancellationToken">A <see cref="CancellationToken"/> that can be used to cancel the operation.</param>
|
||||
///// <returns>The bytes comprising the signature.</returns>
|
||||
//[Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
//public abstract Task<byte[]> SignAsync(string vaultResourceName, byte[] digest, string algorithm, string keyName,
|
||||
// CancellationToken cancellationToken = default);
|
||||
|
||||
///// <summary>
|
||||
///// Validates a signature.
|
||||
///// </summary>
|
||||
///// <param name="vaultResourceName">The name of the vault to retrieve the key from.</param>
|
||||
///// <param name="digest">The digest to validate the signature with.</param>
|
||||
///// <param name="signature">The signature to validate.</param>
|
||||
///// <param name="algorithm">The algorithm to validate the signature with.</param>
|
||||
///// <param name="keyName">The name of the key used.</param>
|
||||
///// <param name="cancellationToken">A <see cref="CancellationToken"/> that can be used to cancel the operation.</param>
|
||||
///// <returns><c>True</c> if the signature verification is successful; otherwise <c>false</c>.</returns>
|
||||
//[Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
//public abstract Task<bool> VerifyAsync(string vaultResourceName, byte[] digest, byte[] signature, string algorithm, string keyName,
|
||||
// CancellationToken cancellationToken = default);
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Attempt to lock the given resourceId with response indicating success.
|
||||
/// </summary>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
@ -14,10 +14,14 @@
|
|||
namespace Dapr.Client
|
||||
{
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Json;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
@ -1373,6 +1377,498 @@ namespace Dapr.Client
|
|||
var resp = await client.UnsubscribeConfigurationAsync(request, options);
|
||||
return new UnsubscribeConfigurationResponse(resp.Ok, resp.Message);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Cryptography
|
||||
|
||||
/// <inheritdoc />
|
||||
[Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
public override async Task<ReadOnlyMemory<byte>> EncryptAsync(string vaultResourceName,
|
||||
ReadOnlyMemory<byte> plaintextBytes, string keyName, EncryptionOptions encryptionOptions,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (MemoryMarshal.TryGetArray(plaintextBytes, out var plaintextSegment) && plaintextSegment.Array != null)
|
||||
{
|
||||
var encryptionResult = await EncryptAsync(vaultResourceName, new MemoryStream(plaintextSegment.Array), keyName, encryptionOptions,
|
||||
cancellationToken);
|
||||
|
||||
var bufferedResult = new ArrayBufferWriter<byte>();
|
||||
|
||||
await foreach (var item in encryptionResult.WithCancellation(cancellationToken))
|
||||
{
|
||||
bufferedResult.Write(item.Span);
|
||||
}
|
||||
|
||||
return bufferedResult.WrittenMemory;
|
||||
}
|
||||
|
||||
throw new ArgumentException("The input instance doesn't have a valid underlying data store.", nameof(plaintextBytes));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
[Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
public override async Task<IAsyncEnumerable<ReadOnlyMemory<byte>>> EncryptAsync(string vaultResourceName, Stream plaintextStream,
|
||||
string keyName, EncryptionOptions encryptionOptions, CancellationToken cancellationToken = default)
|
||||
{
|
||||
ArgumentVerifier.ThrowIfNullOrEmpty(vaultResourceName, nameof(vaultResourceName));
|
||||
ArgumentVerifier.ThrowIfNullOrEmpty(keyName, nameof(keyName));
|
||||
ArgumentVerifier.ThrowIfNull(plaintextStream, nameof(plaintextStream));
|
||||
ArgumentVerifier.ThrowIfNull(encryptionOptions, nameof(encryptionOptions));
|
||||
|
||||
var shouldOmitDecryptionKeyName = string.IsNullOrWhiteSpace(encryptionOptions.DecryptionKeyName); //Whitespace isn't likely a valid key name either
|
||||
|
||||
var encryptRequestOptions = new Autogenerated.EncryptRequestOptions
|
||||
{
|
||||
ComponentName = vaultResourceName,
|
||||
DataEncryptionCipher = encryptionOptions.EncryptionCipher.GetValueFromEnumMember(),
|
||||
KeyName = keyName,
|
||||
KeyWrapAlgorithm = encryptionOptions.KeyWrapAlgorithm.GetValueFromEnumMember(),
|
||||
OmitDecryptionKeyName = shouldOmitDecryptionKeyName
|
||||
};
|
||||
|
||||
if (!shouldOmitDecryptionKeyName)
|
||||
{
|
||||
ArgumentVerifier.ThrowIfNullOrEmpty(encryptionOptions.DecryptionKeyName, nameof(encryptionOptions.DecryptionKeyName));
|
||||
encryptRequestOptions.DecryptionKeyName = encryptRequestOptions.DecryptionKeyName;
|
||||
}
|
||||
|
||||
var options = CreateCallOptions(headers: null, cancellationToken);
|
||||
var duplexStream = client.EncryptAlpha1(options);
|
||||
|
||||
//Run both operations at the same time, but return the output of the streaming values coming from the operation
|
||||
var receiveResult = Task.FromResult(RetrieveEncryptedStreamAsync(duplexStream, cancellationToken));
|
||||
return await Task.WhenAll(
|
||||
//Stream the plaintext data to the sidecar in chunks
|
||||
SendPlaintextStreamAsync(plaintextStream, encryptionOptions.StreamingBlockSizeInBytes,
|
||||
duplexStream, encryptRequestOptions, cancellationToken),
|
||||
//At the same time, retrieve the encrypted response from the sidecar
|
||||
receiveResult).ContinueWith(_ => receiveResult.Result, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends the plaintext bytes in chunks to the sidecar to be encrypted.
|
||||
/// </summary>
|
||||
private async Task SendPlaintextStreamAsync(Stream plaintextStream,
|
||||
int streamingBlockSizeInBytes,
|
||||
AsyncDuplexStreamingCall<Autogenerated.EncryptRequest, Autogenerated.EncryptResponse> duplexStream,
|
||||
Autogenerated.EncryptRequestOptions encryptRequestOptions,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
//Start with passing the metadata about the encryption request itself in the first message
|
||||
await duplexStream.RequestStream.WriteAsync(
|
||||
new Autogenerated.EncryptRequest {Options = encryptRequestOptions}, cancellationToken);
|
||||
|
||||
//Send the plaintext bytes in blocks in subsequent messages
|
||||
await using (var bufferedStream = new BufferedStream(plaintextStream, streamingBlockSizeInBytes))
|
||||
{
|
||||
var buffer = new byte[streamingBlockSizeInBytes];
|
||||
int bytesRead;
|
||||
ulong sequenceNumber = 0;
|
||||
|
||||
while ((bytesRead =
|
||||
await bufferedStream.ReadAsync(buffer.AsMemory(0, streamingBlockSizeInBytes), cancellationToken)) !=
|
||||
0)
|
||||
{
|
||||
await duplexStream.RequestStream.WriteAsync(
|
||||
new Autogenerated.EncryptRequest
|
||||
{
|
||||
Payload = new Autogenerated.StreamPayload
|
||||
{
|
||||
Data = ByteString.CopyFrom(buffer, 0, bytesRead), Seq = sequenceNumber
|
||||
}
|
||||
}, cancellationToken);
|
||||
|
||||
//Increment the sequence number
|
||||
sequenceNumber++;
|
||||
}
|
||||
}
|
||||
|
||||
//Send the completion message
|
||||
await duplexStream.RequestStream.CompleteAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the encrypted bytes from the encryption operation on the sidecar and returns as an enumerable stream.
|
||||
/// </summary>
|
||||
private async IAsyncEnumerable<ReadOnlyMemory<byte>> RetrieveEncryptedStreamAsync(AsyncDuplexStreamingCall<Autogenerated.EncryptRequest, Autogenerated.EncryptResponse> duplexStream, [EnumeratorCancellation] CancellationToken cancellationToken)
|
||||
{
|
||||
await foreach (var encryptResponse in duplexStream.ResponseStream.ReadAllAsync(cancellationToken)
|
||||
.ConfigureAwait(false))
|
||||
{
|
||||
yield return encryptResponse.Payload.Data.Memory;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
[Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
public override async Task<IAsyncEnumerable<ReadOnlyMemory<byte>>> DecryptAsync(string vaultResourceName, Stream ciphertextStream, string keyName,
|
||||
DecryptionOptions decryptionOptions, CancellationToken cancellationToken = default)
|
||||
{
|
||||
ArgumentVerifier.ThrowIfNullOrEmpty(vaultResourceName, nameof(vaultResourceName));
|
||||
ArgumentVerifier.ThrowIfNullOrEmpty(keyName, nameof(keyName));
|
||||
ArgumentVerifier.ThrowIfNull(ciphertextStream, nameof(ciphertextStream));
|
||||
ArgumentVerifier.ThrowIfNull(decryptionOptions, nameof(decryptionOptions));
|
||||
|
||||
var decryptRequestOptions = new Autogenerated.DecryptRequestOptions
|
||||
{
|
||||
ComponentName = vaultResourceName,
|
||||
KeyName = keyName
|
||||
};
|
||||
|
||||
var options = CreateCallOptions(headers: null, cancellationToken);
|
||||
var duplexStream = client.DecryptAlpha1(options);
|
||||
|
||||
//Run both operations at the same time, but return the output of the streaming values coming from the operation
|
||||
var receiveResult = Task.FromResult(RetrieveDecryptedStreamAsync(duplexStream, cancellationToken));
|
||||
return await Task.WhenAll(
|
||||
//Stream the ciphertext data to the sidecar in chunks
|
||||
SendCiphertextStreamAsync(ciphertextStream, decryptionOptions.StreamingBlockSizeInBytes,
|
||||
duplexStream, decryptRequestOptions, cancellationToken),
|
||||
//At the same time, retrieve the decrypted response from the sidecar
|
||||
receiveResult)
|
||||
//Return only the result of the `RetrieveEncryptedStreamAsync` method
|
||||
.ContinueWith(t => receiveResult.Result, cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
[Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
public override Task<IAsyncEnumerable<ReadOnlyMemory<byte>>> DecryptAsync(string vaultResourceName,
|
||||
Stream ciphertextStream, string keyName, CancellationToken cancellationToken = default) =>
|
||||
DecryptAsync(vaultResourceName, ciphertextStream, keyName, new DecryptionOptions(),
|
||||
cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Sends the ciphertext bytes in chunks to the sidecar to be decrypted.
|
||||
/// </summary>
|
||||
private async Task SendCiphertextStreamAsync(Stream ciphertextStream,
|
||||
int streamingBlockSizeInBytes,
|
||||
AsyncDuplexStreamingCall<Autogenerated.DecryptRequest, Autogenerated.DecryptResponse> duplexStream,
|
||||
Autogenerated.DecryptRequestOptions decryptRequestOptions,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
//Start with passing the metadata about the decryption request itself in the first message
|
||||
await duplexStream.RequestStream.WriteAsync(
|
||||
new Autogenerated.DecryptRequest { Options = decryptRequestOptions }, cancellationToken);
|
||||
|
||||
//Send the ciphertext bytes in blocks in subsequent messages
|
||||
await using (var bufferedStream = new BufferedStream(ciphertextStream, streamingBlockSizeInBytes))
|
||||
{
|
||||
var buffer = new byte[streamingBlockSizeInBytes];
|
||||
int bytesRead;
|
||||
ulong sequenceNumber = 0;
|
||||
|
||||
while ((bytesRead = await bufferedStream.ReadAsync(buffer.AsMemory(0, streamingBlockSizeInBytes), cancellationToken)) != 0)
|
||||
{
|
||||
await duplexStream.RequestStream.WriteAsync(new Autogenerated.DecryptRequest
|
||||
{
|
||||
Payload = new Autogenerated.StreamPayload
|
||||
{
|
||||
Data = ByteString.CopyFrom(buffer, 0, bytesRead),
|
||||
Seq = sequenceNumber
|
||||
}
|
||||
}, cancellationToken);
|
||||
|
||||
//Increment the sequence number
|
||||
sequenceNumber++;
|
||||
}
|
||||
}
|
||||
|
||||
//Send the completion message
|
||||
await duplexStream.RequestStream.CompleteAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the decrypted bytes from the decryption operation on the sidecar and returns as an enumerable stream.
|
||||
/// </summary>
|
||||
private async IAsyncEnumerable<ReadOnlyMemory<byte>> RetrieveDecryptedStreamAsync(
|
||||
AsyncDuplexStreamingCall<Autogenerated.DecryptRequest, Autogenerated.DecryptResponse> duplexStream,
|
||||
[EnumeratorCancellation] CancellationToken cancellationToken)
|
||||
{
|
||||
await foreach (var decryptResponse in duplexStream.ResponseStream.ReadAllAsync(cancellationToken)
|
||||
.ConfigureAwait(false))
|
||||
{
|
||||
yield return decryptResponse.Payload.Data.Memory;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
[Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
public override async Task<ReadOnlyMemory<byte>> DecryptAsync(string vaultResourceName,
|
||||
ReadOnlyMemory<byte> ciphertextBytes, string keyName, DecryptionOptions decryptionOptions,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (MemoryMarshal.TryGetArray(ciphertextBytes, out var ciphertextSegment) && ciphertextSegment.Array != null)
|
||||
{
|
||||
var decryptionResult = await DecryptAsync(vaultResourceName, new MemoryStream(ciphertextSegment.Array),
|
||||
keyName, decryptionOptions, cancellationToken);
|
||||
|
||||
var bufferedResult = new ArrayBufferWriter<byte>();
|
||||
await foreach (var item in decryptionResult.WithCancellation(cancellationToken))
|
||||
{
|
||||
bufferedResult.Write(item.Span);
|
||||
}
|
||||
|
||||
return bufferedResult.WrittenMemory;
|
||||
}
|
||||
|
||||
throw new ArgumentException("The input instance doesn't have a valid underlying data store", nameof(ciphertextBytes));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
[Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
public override async Task<ReadOnlyMemory<byte>> DecryptAsync(string vaultResourceName,
|
||||
ReadOnlyMemory<byte> ciphertextBytes, string keyName, CancellationToken cancellationToken = default) =>
|
||||
await DecryptAsync(vaultResourceName, ciphertextBytes, keyName,
|
||||
new DecryptionOptions(), cancellationToken);
|
||||
|
||||
#region Subtle Crypto Implementation
|
||||
|
||||
///// <inheritdoc/>
|
||||
//[Obsolete("This API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
//public override async Task<(string Name, string PublicKey)> GetKeyAsync(string vaultResourceName, string keyName, Autogenerated.SubtleGetKeyRequest.Types.KeyFormat keyFormat,
|
||||
// CancellationToken cancellationToken = default)
|
||||
//{
|
||||
// ArgumentVerifier.ThrowIfNullOrEmpty(vaultResourceName, nameof(vaultResourceName));
|
||||
// ArgumentVerifier.ThrowIfNullOrEmpty(keyName, nameof(keyName));
|
||||
|
||||
// var envelope = new Autogenerated.SubtleGetKeyRequest()
|
||||
// {
|
||||
// ComponentName = vaultResourceName, Format = keyFormat, Name = keyName
|
||||
// };
|
||||
|
||||
// var options = CreateCallOptions(headers: null, cancellationToken);
|
||||
// Autogenerated.SubtleGetKeyResponse response;
|
||||
|
||||
// try
|
||||
// {
|
||||
// response = await client.SubtleGetKeyAlpha1Async(envelope, options);
|
||||
// }
|
||||
// catch (RpcException ex)
|
||||
// {
|
||||
// throw new DaprException(
|
||||
// "Cryptography operation failed: the Dapr endpoint indicated a failure. See InnerException for details", ex);
|
||||
// }
|
||||
|
||||
// return (response.Name, response.PublicKey);
|
||||
//}
|
||||
|
||||
///// <inheritdoc/>
|
||||
//[Obsolete("This API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
//public override async Task<(byte[] CipherTextBytes, byte[] AuthenticationTag)> EncryptAsync(string vaultResourceName, byte[] plainTextBytes, string algorithm,
|
||||
// string keyName, byte[] nonce, byte[] associatedData, CancellationToken cancellationToken = default)
|
||||
//{
|
||||
// ArgumentVerifier.ThrowIfNullOrEmpty(vaultResourceName, nameof(vaultResourceName));
|
||||
// ArgumentVerifier.ThrowIfNullOrEmpty(algorithm, nameof(algorithm));
|
||||
// ArgumentVerifier.ThrowIfNullOrEmpty(keyName, nameof(keyName));
|
||||
|
||||
// var envelope = new Autogenerated.SubtleEncryptRequest
|
||||
// {
|
||||
// ComponentName = vaultResourceName,
|
||||
// Algorithm = algorithm,
|
||||
// KeyName = keyName,
|
||||
// Nonce = ByteString.CopyFrom(nonce),
|
||||
// Plaintext = ByteString.CopyFrom(plainTextBytes),
|
||||
// AssociatedData = ByteString.CopyFrom(associatedData)
|
||||
// };
|
||||
|
||||
// var options = CreateCallOptions(headers: null, cancellationToken);
|
||||
// Autogenerated.SubtleEncryptResponse response;
|
||||
|
||||
// try
|
||||
// {
|
||||
// response = await client.SubtleEncryptAlpha1Async(envelope, options);
|
||||
// }
|
||||
// catch (RpcException ex)
|
||||
// {
|
||||
// throw new DaprException(
|
||||
// "Cryptography operation failed: the Dapr endpoint indicated a failure. See InnerException for details",
|
||||
// ex);
|
||||
// }
|
||||
|
||||
// return (response.Ciphertext.ToByteArray(), response.Tag.ToByteArray() ?? Array.Empty<byte>());
|
||||
//}
|
||||
|
||||
///// <inheritdoc/>
|
||||
//[Obsolete("This API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
//public override async Task<byte[]> DecryptAsync(string vaultResourceName, byte[] cipherTextBytes, string algorithm, string keyName, byte[] nonce, byte[] tag,
|
||||
// byte[] associatedData, CancellationToken cancellationToken = default)
|
||||
//{
|
||||
// ArgumentVerifier.ThrowIfNullOrEmpty(vaultResourceName, nameof(vaultResourceName));
|
||||
// ArgumentVerifier.ThrowIfNullOrEmpty(algorithm, nameof(algorithm));
|
||||
// ArgumentVerifier.ThrowIfNullOrEmpty(keyName, nameof(keyName));
|
||||
|
||||
// var envelope = new Autogenerated.SubtleDecryptRequest
|
||||
// {
|
||||
// ComponentName = vaultResourceName,
|
||||
// Algorithm = algorithm,
|
||||
// KeyName = keyName,
|
||||
// Nonce = ByteString.CopyFrom(nonce),
|
||||
// Ciphertext = ByteString.CopyFrom(cipherTextBytes),
|
||||
// AssociatedData = ByteString.CopyFrom(associatedData),
|
||||
// Tag = ByteString.CopyFrom(tag)
|
||||
// };
|
||||
|
||||
// var options = CreateCallOptions(headers: null, cancellationToken);
|
||||
// Autogenerated.SubtleDecryptResponse response;
|
||||
|
||||
// try
|
||||
// {
|
||||
// response = await client.SubtleDecryptAlpha1Async(envelope, options);
|
||||
// }
|
||||
// catch (RpcException ex)
|
||||
// {
|
||||
// throw new DaprException(
|
||||
// "Cryptography operation failed: the Dapr endpoint included a failure. See InnerException for details", ex);
|
||||
// }
|
||||
|
||||
// return response.Plaintext.ToByteArray();
|
||||
//}
|
||||
|
||||
///// <inheritdoc/>
|
||||
//[Obsolete("This API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
//public override async Task<(byte[] WrappedKey, byte[] AuthenticationTag)> WrapKeyAsync(string vaultResourceName, byte[] plainTextKey, string keyName,
|
||||
// string algorithm, byte[] nonce, byte[] associatedData, CancellationToken cancellationToken = default)
|
||||
//{
|
||||
// ArgumentVerifier.ThrowIfNullOrEmpty(vaultResourceName, nameof(vaultResourceName));
|
||||
// ArgumentVerifier.ThrowIfNullOrEmpty(keyName, nameof(keyName));
|
||||
// ArgumentVerifier.ThrowIfNullOrEmpty(algorithm, nameof(algorithm));
|
||||
|
||||
// var envelope = new Autogenerated.SubtleWrapKeyRequest
|
||||
// {
|
||||
// ComponentName = vaultResourceName,
|
||||
// Algorithm = algorithm,
|
||||
// KeyName = keyName,
|
||||
// Nonce = ByteString.CopyFrom(nonce),
|
||||
// PlaintextKey = ByteString.CopyFrom(plainTextKey),
|
||||
// AssociatedData = ByteString.CopyFrom(associatedData)
|
||||
// };
|
||||
|
||||
// var options = CreateCallOptions(headers: null, cancellationToken);
|
||||
// Autogenerated.SubtleWrapKeyResponse response;
|
||||
|
||||
// try
|
||||
// {
|
||||
// response = await client.SubtleWrapKeyAlpha1Async(envelope, options);
|
||||
// }
|
||||
// catch (RpcException ex)
|
||||
// {
|
||||
// throw new DaprException(
|
||||
// "Cryptography operation failed: the Dapr endpoint included a failure. See InnerException for details",
|
||||
// ex);
|
||||
// }
|
||||
|
||||
// return (response.WrappedKey.ToByteArray(), response.Tag.ToByteArray() ?? Array.Empty<byte>());
|
||||
//}
|
||||
|
||||
///// <inheritdoc/>
|
||||
//[Obsolete("This API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
//public override async Task<byte[]> UnwrapKeyAsync(string vaultResourceName, byte[] wrappedKey, string algorithm,
|
||||
// string keyName, byte[] nonce, byte[] tag, byte[] associatedData, CancellationToken cancellationToken = default)
|
||||
//{
|
||||
// ArgumentVerifier.ThrowIfNullOrEmpty(vaultResourceName, nameof(vaultResourceName));
|
||||
// ArgumentVerifier.ThrowIfNullOrEmpty(algorithm, nameof(algorithm));
|
||||
// ArgumentVerifier.ThrowIfNullOrEmpty(keyName, nameof(keyName));
|
||||
|
||||
// var envelope = new Autogenerated.SubtleUnwrapKeyRequest
|
||||
// {
|
||||
// ComponentName = vaultResourceName,
|
||||
// WrappedKey = ByteString.CopyFrom(wrappedKey),
|
||||
// AssociatedData = ByteString.CopyFrom(associatedData),
|
||||
// Algorithm = algorithm,
|
||||
// KeyName = keyName,
|
||||
// Nonce = ByteString.CopyFrom(nonce),
|
||||
// Tag = ByteString.CopyFrom(tag)
|
||||
// };
|
||||
|
||||
// var options = CreateCallOptions(headers: null, cancellationToken);
|
||||
// Autogenerated.SubtleUnwrapKeyResponse response;
|
||||
|
||||
// try
|
||||
// {
|
||||
// response = await client.SubtleUnwrapKeyAlpha1Async(envelope, options);
|
||||
// }
|
||||
// catch (RpcException ex)
|
||||
// {
|
||||
// throw new DaprException(
|
||||
// "Cryptography operation failed: the Dapr endpoint included a failure. See InnerException for details",
|
||||
// ex);
|
||||
// }
|
||||
|
||||
// return response.PlaintextKey.ToByteArray();
|
||||
//}
|
||||
|
||||
///// <inheritdoc/>
|
||||
//[Obsolete("This API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
//public override async Task<byte[]> SignAsync(string vaultResourceName, byte[] digest, string algorithm, string keyName, CancellationToken cancellationToken = default)
|
||||
//{
|
||||
// ArgumentVerifier.ThrowIfNullOrEmpty(vaultResourceName, nameof(vaultResourceName));
|
||||
// ArgumentVerifier.ThrowIfNullOrEmpty(algorithm, nameof(algorithm));
|
||||
// ArgumentVerifier.ThrowIfNullOrEmpty(keyName, nameof(keyName));
|
||||
|
||||
// var envelope = new Autogenerated.SubtleSignRequest
|
||||
// {
|
||||
// ComponentName = vaultResourceName,
|
||||
// Digest = ByteString.CopyFrom(digest),
|
||||
// Algorithm = algorithm,
|
||||
// KeyName = keyName
|
||||
// };
|
||||
|
||||
// var options = CreateCallOptions(headers: null, cancellationToken);
|
||||
// Autogenerated.SubtleSignResponse response;
|
||||
|
||||
// try
|
||||
// {
|
||||
// response = await client.SubtleSignAlpha1Async(envelope, options);
|
||||
// }
|
||||
// catch (RpcException ex)
|
||||
// {
|
||||
// throw new DaprException(
|
||||
// "Cryptography operation failed: the Dapr endpoint included a failure. See InnerException for details",
|
||||
// ex);
|
||||
// }
|
||||
|
||||
// return response.Signature.ToByteArray();
|
||||
//}
|
||||
|
||||
///// <inheritdoc/>
|
||||
//[Obsolete("This API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
//public override async Task<bool> VerifyAsync(string vaultResourceName, byte[] digest, byte[] signature,
|
||||
// string algorithm, string keyName, CancellationToken cancellationToken = default)
|
||||
//{
|
||||
// ArgumentVerifier.ThrowIfNullOrEmpty(vaultResourceName, nameof(vaultResourceName));
|
||||
// ArgumentVerifier.ThrowIfNullOrEmpty(algorithm, nameof(algorithm));
|
||||
// ArgumentVerifier.ThrowIfNullOrEmpty(keyName, nameof(keyName));
|
||||
|
||||
// var envelope = new Autogenerated.SubtleVerifyRequest
|
||||
// {
|
||||
// ComponentName = vaultResourceName,
|
||||
// Algorithm = algorithm,
|
||||
// KeyName = keyName,
|
||||
// Signature = ByteString.CopyFrom(signature),
|
||||
// Digest = ByteString.CopyFrom(digest)
|
||||
// };
|
||||
|
||||
// var options = CreateCallOptions(headers: null, cancellationToken);
|
||||
// Autogenerated.SubtleVerifyResponse response;
|
||||
|
||||
// try
|
||||
// {
|
||||
// response = await client.SubtleVerifyAlpha1Async(envelope, options);
|
||||
// }
|
||||
// catch (RpcException ex)
|
||||
// {
|
||||
// throw new DaprException(
|
||||
// "Cryptography operation failed: the Dapr endpoint included a failure. See InnerException for details",
|
||||
// ex);
|
||||
// }
|
||||
|
||||
// return response.Valid;
|
||||
//}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region Distributed Lock API
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
// ------------------------------------------------------------------------
|
||||
// Copyright 2023 The Dapr Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Dapr.Client
|
||||
{
|
||||
internal static class EnumExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Reads the value of an enum out of the attached <see cref="EnumMemberAttribute"/> attribute.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The enum.</typeparam>
|
||||
/// <param name="value">The value of the enum to pull the value for.</param>
|
||||
/// <returns></returns>
|
||||
public static string GetValueFromEnumMember<T>(this T value) where T : Enum
|
||||
{
|
||||
var memberInfo = typeof(T).GetMember(value.ToString(), BindingFlags.Static | BindingFlags.Public | BindingFlags.DeclaredOnly);
|
||||
if (memberInfo.Length <= 0)
|
||||
return value.ToString();
|
||||
|
||||
var attributes = memberInfo[0].GetCustomAttributes(typeof(EnumMemberAttribute), false);
|
||||
return attributes.Length > 0 ? ((EnumMemberAttribute)attributes[0]).Value : value.ToString();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
|
||||
namespace Dapr.Client.Test
|
||||
{
|
||||
public class CryptographyApiTest
|
||||
{
|
||||
[Fact]
|
||||
public async Task EncryptAsync_ByteArray_VaultResourceName_ArgumentVerifierException()
|
||||
{
|
||||
var client = new DaprClientBuilder().Build();
|
||||
const string vaultResourceName = "";
|
||||
//Get response and validate
|
||||
await Assert.ThrowsAsync<ArgumentException>(async () => await client.EncryptAsync(vaultResourceName,
|
||||
(ReadOnlyMemory<byte>)Array.Empty<byte>(), "MyKey", new EncryptionOptions(KeyWrapAlgorithm.Rsa),
|
||||
CancellationToken.None));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task EncryptAsync_ByteArray_KeyName_ArgumentVerifierException()
|
||||
{
|
||||
var client = new DaprClientBuilder().Build();
|
||||
const string keyName = "";
|
||||
//Get response and validate
|
||||
await Assert.ThrowsAsync<ArgumentException>(async () => await client.EncryptAsync( "myVault",
|
||||
(ReadOnlyMemory<byte>) Array.Empty<byte>(), keyName, new EncryptionOptions(KeyWrapAlgorithm.Rsa), CancellationToken.None));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task EncryptAsync_Stream_VaultResourceName_ArgumentVerifierException()
|
||||
{
|
||||
var client = new DaprClientBuilder().Build();
|
||||
const string vaultResourceName = "";
|
||||
//Get response and validate
|
||||
await Assert.ThrowsAsync<ArgumentException>(async () => await client.EncryptAsync(vaultResourceName,
|
||||
new MemoryStream(), "MyKey", new EncryptionOptions(KeyWrapAlgorithm.Rsa),
|
||||
CancellationToken.None));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task EncryptAsync_Stream_KeyName_ArgumentVerifierException()
|
||||
{
|
||||
var client = new DaprClientBuilder().Build();
|
||||
const string keyName = "";
|
||||
//Get response and validate
|
||||
await Assert.ThrowsAsync<ArgumentException>(async () => await client.EncryptAsync("myVault",
|
||||
(Stream) new MemoryStream(), keyName, new EncryptionOptions(KeyWrapAlgorithm.Rsa),
|
||||
CancellationToken.None));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DecryptAsync_ByteArray_VaultResourceName_ArgumentVerifierException()
|
||||
{
|
||||
var client = new DaprClientBuilder().Build();
|
||||
const string vaultResourceName = "";
|
||||
//Get response and validate
|
||||
await Assert.ThrowsAsync<ArgumentException>(async () => await client.DecryptAsync(vaultResourceName,
|
||||
Array.Empty<byte>(), "myKey", new DecryptionOptions(), CancellationToken.None));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DecryptAsync_ByteArray_KeyName_ArgumentVerifierException()
|
||||
{
|
||||
var client = new DaprClientBuilder().Build();
|
||||
const string keyName = "";
|
||||
//Get response and validate
|
||||
await Assert.ThrowsAsync<ArgumentException>(async () => await client.DecryptAsync("myVault",
|
||||
Array.Empty<byte>(), keyName, new DecryptionOptions(), CancellationToken.None));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DecryptAsync_Stream_VaultResourceName_ArgumentVerifierException()
|
||||
{
|
||||
var client = new DaprClientBuilder().Build();
|
||||
const string vaultResourceName = "";
|
||||
//Get response and validate
|
||||
await Assert.ThrowsAsync<ArgumentException>(async () => await client.DecryptAsync(vaultResourceName,
|
||||
new MemoryStream(), "MyKey", new DecryptionOptions(), CancellationToken.None));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DecryptAsync_Stream_KeyName_ArgumentVerifierException()
|
||||
{
|
||||
var client = new DaprClientBuilder().Build();
|
||||
const string keyName = "";
|
||||
//Get response and validate
|
||||
await Assert.ThrowsAsync<ArgumentException>(async () => await client.DecryptAsync("myVault",
|
||||
new MemoryStream(), keyName, new DecryptionOptions(), CancellationToken.None));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
using System.Runtime.Serialization;
|
||||
using Xunit;
|
||||
|
||||
namespace Dapr.Client.Test
|
||||
{
|
||||
public class EnumExtensionTest
|
||||
{
|
||||
[Fact]
|
||||
public void GetValueFromEnumMember_RedResolvesAsExpected()
|
||||
{
|
||||
var value = TestEnum.Red.GetValueFromEnumMember();
|
||||
Assert.Equal("red", value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetValueFromEnumMember_YellowResolvesAsExpected()
|
||||
{
|
||||
var value = TestEnum.Yellow.GetValueFromEnumMember();
|
||||
Assert.Equal("YELLOW", value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetValueFromEnumMember_BlueResolvesAsExpected()
|
||||
{
|
||||
var value = TestEnum.Blue.GetValueFromEnumMember();
|
||||
Assert.Equal("Blue", value);
|
||||
}
|
||||
}
|
||||
|
||||
public enum TestEnum
|
||||
{
|
||||
[EnumMember(Value="red")]
|
||||
Red,
|
||||
[EnumMember(Value="YELLOW")]
|
||||
Yellow,
|
||||
Blue
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue