From 41ca6e681ad1fc3184a8fa84eca67f2febf4e4a3 Mon Sep 17 00:00:00 2001 From: Junjie Gao Date: Mon, 24 Apr 2023 21:54:53 +0800 Subject: [PATCH 01/17] test&ci: add test for get-plugin-metadata command & add test CI Signed-off-by: Junjie Gao --- .github/workflows/workflow.yml | 83 +++++++++++-------- Makefile | 26 ++---- .../GetPluginMetadataTests.cs | 31 +++++++ ...Notation.Plugin.AzureKeyVault.Tests.csproj | 27 ++++++ .../Notation.Plugin.AzureKeyVault.csproj | 3 +- 5 files changed, 114 insertions(+), 56 deletions(-) create mode 100644 Notation.Plugin.AzureKeyVault.Tests/GetPluginMetadataTests.cs create mode 100644 Notation.Plugin.AzureKeyVault.Tests/Notation.Plugin.AzureKeyVault.Tests.csproj diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index 4aef8b83..b75503ea 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -10,52 +10,65 @@ on: - main - release-* +############### +# Set the Job # +############### jobs: lint: - strategy: - matrix: - go-version: ['1.20'] - - name: "Lint" + # Name the Job + name: Lint Code Base + # Set the agent to run on runs-on: ubuntu-latest - timeout-minutes: 5 + + ############################################ + # Grant status permission for MULTI_STATUS # + ############################################ permissions: contents: read + packages: read + statuses: write + + ################## + # Load all steps # + ################## steps: - - name: Set up Go ${{ matrix.go-version }} - uses: actions/setup-go@v3 - with: - go-version: ${{ matrix.go-version }} - check-latest: true - - name: Check out code into the Go module directory - uses: actions/checkout@v3 - - name: Run golangci-lint - uses: golangci/golangci-lint-action@v3 - with: - version: latest - args: --fast=false + ########################## + # Checkout the code base # + ########################## + - name: Checkout Code + uses: actions/checkout@v3 + with: + # Full git history is needed to get a proper + # list of changed files within `super-linter` + fetch-depth: 0 - build: - strategy: - matrix: - go-version: ['1.20'] + ################################ + # Run Linter against code base # + ################################ + - name: Lint Code Base + uses: github/super-linter@v5 + env: + VALIDATE_ALL_CODEBASE: false + DEFAULT_BRANCH: main + DEFAULT_WORKSPACE: ./Notation.Plugin.AzureKeyVault + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + build: name: "Build" runs-on: ubuntu-latest timeout-minutes: 5 permissions: contents: read steps: - - name: Set up Go ${{ matrix.go-version }} - uses: actions/setup-go@v3 - with: - go-version: ${{ matrix.go-version }} - check-latest: true - - name: Check out code into the Go module directory - uses: actions/checkout@v3 - - name: Run unit tests - run: make test - - name: Build testing - run: make build - - name: Upload coverage reports to Codecov - uses: codecov/codecov-action@v3 + - name : Setup .NET + uses: actions/setup-dotnet@v3 + with: + dotnet-version: '6.0.x' + - name: Check out code into the Go module directory + uses: actions/checkout@v3 + - name: Run unit tests + run: make test + - name: Build testing + run: make build + - name: Upload coverage reports to Codecov + uses: codecov/codecov-action@v3 diff --git a/Makefile b/Makefile index 23953d8d..45117191 100644 --- a/Makefile +++ b/Makefile @@ -1,15 +1,7 @@ # Notation Azure KeyVault Plugin Makefile for Linux -MODULE = github.com/Azure/notation-azure-kv -COMMANDS = notation-azure-kv -GIT_TAG = $(shell git describe --tags --abbrev=0 --exact-match 2>/dev/null) -BUILD_METADATA = -ifeq ($(GIT_TAG),) # unreleased build - GIT_COMMIT = $(shell git rev-parse HEAD) - GIT_STATUS = $(shell test -n "`git status --porcelain`" && echo "dirty" || echo "unreleased") - BUILD_METADATA = $(GIT_COMMIT).$(GIT_STATUS) -endif -LDFLAGS = -X $(MODULE)/internal/version.BuildMetadata=$(BUILD_METADATA) -GO_BUILD_FLAGS = --ldflags="$(LDFLAGS)" +BUILD_DIR = ./bin +PROJECT_DIR = ./Notation.Plugin.AzureKeyVault +TEST_PROJECT_DIR = ./Notation.Plugin.AzureKeyVault.Tests .PHONY: help help: @@ -21,19 +13,13 @@ all: build .PHONY: FORCE FORCE: -bin/%: cmd/% FORCE - go build $(GO_BUILD_FLAGS) -o $@ ./$< - -.PHONY: download -download: ## download dependencies via go mod - go mod download - .PHONY: build -build: $(addprefix bin/,$(COMMANDS)) ## builds binaries +build: ## builds binaries + dotnet build $(PROJECT_DIR) -c Release -o $(BUILD_DIR) --self-contained true /p:PublishSingleFile=true .PHONY: test test: ## run unit test - go test -race -v -coverprofile=coverage.txt -covermode=atomic ./... + dotnet test $(TEST_PROJECT_DIR) --collect:"XPlat Code Coverage" --logger trx --results-directory $(BUILD_DIR)/TestResults .PHONY: install install: bin/notation-azure-kv ## installs the plugin diff --git a/Notation.Plugin.AzureKeyVault.Tests/GetPluginMetadataTests.cs b/Notation.Plugin.AzureKeyVault.Tests/GetPluginMetadataTests.cs new file mode 100644 index 00000000..77a50ac1 --- /dev/null +++ b/Notation.Plugin.AzureKeyVault.Tests/GetPluginMetadataTests.cs @@ -0,0 +1,31 @@ +using Xunit; +using System.Threading.Tasks; +using Notation.Plugin.Protocol; +using Notation.Plugin.AzureKeyVault.Command; + +namespace Notation.Plugin.AzureKeyVault.Tests +{ + public class GetPluginMetadataTests + { + [Fact] + public async Task RunAsync_ReturnsExpectedMetadata() + { + // Arrange + var getPluginMetadata = new GetPluginMetadata(); + + // Act + var result = await getPluginMetadata.RunAsync(""); + + // Assert + Assert.IsType(result); + + var metadata = result as GetMetadataResponse; + Assert.Equal("azure-kv", metadata?.Name); + Assert.Equal("Notation Azure Key Vault plugin", metadata?.Description); + Assert.Equal("1.0.0", metadata?.Version); + Assert.Equal("https://github.com/Azure/notation-azure-kv", metadata?.Url); + Assert.Contains("1.0", metadata?.SupportedContractVersions ?? new string[0]); + Assert.Contains("SIGNATURE_GENERATOR.RAW", metadata?.Capabilities ?? new string[0]); + } + } +} diff --git a/Notation.Plugin.AzureKeyVault.Tests/Notation.Plugin.AzureKeyVault.Tests.csproj b/Notation.Plugin.AzureKeyVault.Tests/Notation.Plugin.AzureKeyVault.Tests.csproj new file mode 100644 index 00000000..7b708c6d --- /dev/null +++ b/Notation.Plugin.AzureKeyVault.Tests/Notation.Plugin.AzureKeyVault.Tests.csproj @@ -0,0 +1,27 @@ + + + + net6.0 + enable + Library + false + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + diff --git a/Notation.Plugin.AzureKeyVault/Notation.Plugin.AzureKeyVault.csproj b/Notation.Plugin.AzureKeyVault/Notation.Plugin.AzureKeyVault.csproj index fedf661f..f2016daa 100644 --- a/Notation.Plugin.AzureKeyVault/Notation.Plugin.AzureKeyVault.csproj +++ b/Notation.Plugin.AzureKeyVault/Notation.Plugin.AzureKeyVault.csproj @@ -4,6 +4,7 @@ Exe net6.0 Notation.Plugin.AzureKeyVault + linux-x64 enable enable true @@ -19,4 +20,4 @@ - + \ No newline at end of file From a05710255f255f326b5cc0d0c316b3ba57c0763d Mon Sep 17 00:00:00 2001 From: Junjie Gao Date: Mon, 24 Apr 2023 22:03:56 +0800 Subject: [PATCH 02/17] fix: update code Signed-off-by: Junjie Gao --- .github/workflows/workflow.yml | 27 +++---------------- .../GetPluginMetadataTests.cs | 2 +- .../Notation.Plugin.AzureKeyVault.csproj | 2 +- 3 files changed, 5 insertions(+), 26 deletions(-) diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index b75503ea..deea3828 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -1,3 +1,5 @@ +--- +# Github workflow for Notation Azure Key Vault plugin name: test on: @@ -10,41 +12,19 @@ on: - main - release-* -############### -# Set the Job # -############### jobs: lint: - # Name the Job name: Lint Code Base - # Set the agent to run on runs-on: ubuntu-latest - - ############################################ - # Grant status permission for MULTI_STATUS # - ############################################ permissions: contents: read packages: read statuses: write - - ################## - # Load all steps # - ################## steps: - ########################## - # Checkout the code base # - ########################## - name: Checkout Code uses: actions/checkout@v3 with: - # Full git history is needed to get a proper - # list of changed files within `super-linter` fetch-depth: 0 - - ################################ - # Run Linter against code base # - ################################ - name: Lint Code Base uses: github/super-linter@v5 env: @@ -52,7 +32,6 @@ jobs: DEFAULT_BRANCH: main DEFAULT_WORKSPACE: ./Notation.Plugin.AzureKeyVault GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - build: name: "Build" runs-on: ubuntu-latest @@ -60,7 +39,7 @@ jobs: permissions: contents: read steps: - - name : Setup .NET + - name: Setup .NET uses: actions/setup-dotnet@v3 with: dotnet-version: '6.0.x' diff --git a/Notation.Plugin.AzureKeyVault.Tests/GetPluginMetadataTests.cs b/Notation.Plugin.AzureKeyVault.Tests/GetPluginMetadataTests.cs index 77a50ac1..8e4a9782 100644 --- a/Notation.Plugin.AzureKeyVault.Tests/GetPluginMetadataTests.cs +++ b/Notation.Plugin.AzureKeyVault.Tests/GetPluginMetadataTests.cs @@ -15,7 +15,7 @@ public async Task RunAsync_ReturnsExpectedMetadata() // Act var result = await getPluginMetadata.RunAsync(""); - + // Assert Assert.IsType(result); diff --git a/Notation.Plugin.AzureKeyVault/Notation.Plugin.AzureKeyVault.csproj b/Notation.Plugin.AzureKeyVault/Notation.Plugin.AzureKeyVault.csproj index f2016daa..9718a71f 100644 --- a/Notation.Plugin.AzureKeyVault/Notation.Plugin.AzureKeyVault.csproj +++ b/Notation.Plugin.AzureKeyVault/Notation.Plugin.AzureKeyVault.csproj @@ -20,4 +20,4 @@ - \ No newline at end of file + From 8b22838617ff6a0520e62db134998f9a00f69ee4 Mon Sep 17 00:00:00 2001 From: Junjie Gao Date: Mon, 24 Apr 2023 22:11:10 +0800 Subject: [PATCH 03/17] fix: update Signed-off-by: Junjie Gao --- .github/.codecov.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/.codecov.yml b/.github/.codecov.yml index 0a986a97..62a20543 100644 --- a/.github/.codecov.yml +++ b/.github/.codecov.yml @@ -1,4 +1,5 @@ coverage: + require_ci_to_pass: false status: project: default: From 5269e7c76213bc8eb06e886b9b1931c93de4ecfa Mon Sep 17 00:00:00 2001 From: Junjie Gao Date: Mon, 24 Apr 2023 22:12:52 +0800 Subject: [PATCH 04/17] fix: update code Signed-off-by: Junjie Gao --- .../{ => Command}/GetPluginMetadataTests.cs | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename Notation.Plugin.AzureKeyVault.Tests/{ => Command}/GetPluginMetadataTests.cs (100%) diff --git a/Notation.Plugin.AzureKeyVault.Tests/GetPluginMetadataTests.cs b/Notation.Plugin.AzureKeyVault.Tests/Command/GetPluginMetadataTests.cs similarity index 100% rename from Notation.Plugin.AzureKeyVault.Tests/GetPluginMetadataTests.cs rename to Notation.Plugin.AzureKeyVault.Tests/Command/GetPluginMetadataTests.cs From 65c4b0b522f22683aa0f9ff7fed04eccaa4c7865 Mon Sep 17 00:00:00 2001 From: Junjie Gao Date: Tue, 25 Apr 2023 09:33:09 +0800 Subject: [PATCH 05/17] fix: update code Signed-off-by: Junjie Gao --- .../Command/GetPluginMetadataTests.cs | 1 - Notation.Plugin.AzureKeyVault/Command/GetPluginMetadata.cs | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Notation.Plugin.AzureKeyVault.Tests/Command/GetPluginMetadataTests.cs b/Notation.Plugin.AzureKeyVault.Tests/Command/GetPluginMetadataTests.cs index 8e4a9782..43f4ac6f 100644 --- a/Notation.Plugin.AzureKeyVault.Tests/Command/GetPluginMetadataTests.cs +++ b/Notation.Plugin.AzureKeyVault.Tests/Command/GetPluginMetadataTests.cs @@ -22,7 +22,6 @@ public async Task RunAsync_ReturnsExpectedMetadata() var metadata = result as GetMetadataResponse; Assert.Equal("azure-kv", metadata?.Name); Assert.Equal("Notation Azure Key Vault plugin", metadata?.Description); - Assert.Equal("1.0.0", metadata?.Version); Assert.Equal("https://github.com/Azure/notation-azure-kv", metadata?.Url); Assert.Contains("1.0", metadata?.SupportedContractVersions ?? new string[0]); Assert.Contains("SIGNATURE_GENERATOR.RAW", metadata?.Capabilities ?? new string[0]); diff --git a/Notation.Plugin.AzureKeyVault/Command/GetPluginMetadata.cs b/Notation.Plugin.AzureKeyVault/Command/GetPluginMetadata.cs index 46847e4d..10433fab 100644 --- a/Notation.Plugin.AzureKeyVault/Command/GetPluginMetadata.cs +++ b/Notation.Plugin.AzureKeyVault/Command/GetPluginMetadata.cs @@ -7,12 +7,13 @@ namespace Notation.Plugin.AzureKeyVault.Command /// public class GetPluginMetadata : IPluginCommand { + public const string Version = "1.0.0"; public async Task RunAsync(string _) { return await Task.FromResult(new GetMetadataResponse( name: "azure-kv", description: "Notation Azure Key Vault plugin", - version: "1.0.0", + version: Version, url: "https://github.com/Azure/notation-azure-kv", supportedContractVersions: new[] { Protocol.Protocol.ContractVersion }, capabilities: new[] { "SIGNATURE_GENERATOR.RAW" } From 6502d5a92b19e0e84ee1f4dae885ef03b73e5477 Mon Sep 17 00:00:00 2001 From: Junjie Gao Date: Tue, 25 Apr 2023 16:44:08 +0800 Subject: [PATCH 06/17] refactor: refactor for unit test Signed-off-by: Junjie Gao --- .../Command/DescribeKeyTests.cs | 52 +++++ .../Command/GetPluginMetadataTests.cs | 7 +- .../KeyVault/KeySpecExtensionTests.cs | 39 ++++ .../KeyVault/KeyVaultClientTests.cs | 179 ++++++++++++++++++ ...Notation.Plugin.AzureKeyVault.Tests.csproj | 3 + .../Protocol/CertificateExtensionTests.cs | 61 ++++++ .../Protocol/DescribeKeyTests.cs | 71 +++++++ .../Protocol/ErrorTests.cs | 81 ++++++++ .../Protocol/GenerateSignatureTests.cs | 44 +++++ .../Protocol/GetMetadataResponseTests.cs | 30 +++ .../Protocol/KeySpecTests.cs | 44 +++++ .../Protocol/PluginIOTests.cs | 62 ++++++ .../TestData/dsa_2048_cert.pem | 24 +++ .../TestData/ec_163_cert.pem | 8 + .../TestData/ec_256_cert.pem | 9 + .../TestData/ec_384_cert.pem | 10 + .../TestData/ec_521_cert.pem | 12 ++ .../TestData/rsa_1024_cert.pem | 12 ++ .../TestData/rsa_2048_cert.pem | 17 ++ .../TestData/rsa_3072_cert.pem | 22 +++ .../TestData/rsa_4096_cert.pem | 28 +++ .../Command/DescribeKey.cs | 28 ++- .../Command/GenerateSignature.cs | 38 +++- .../Command/GetPluginMetadata.cs | 2 +- .../Command/IPluginCommand.cs | 2 +- .../KeyVault/KeyVaultClient.cs | 35 +++- Notation.Plugin.AzureKeyVault/Program.cs | 12 +- .../Protocol/KeySpec.cs | 3 - 28 files changed, 899 insertions(+), 36 deletions(-) create mode 100644 Notation.Plugin.AzureKeyVault.Tests/Command/DescribeKeyTests.cs create mode 100644 Notation.Plugin.AzureKeyVault.Tests/KeyVault/KeySpecExtensionTests.cs create mode 100644 Notation.Plugin.AzureKeyVault.Tests/KeyVault/KeyVaultClientTests.cs create mode 100644 Notation.Plugin.AzureKeyVault.Tests/Protocol/CertificateExtensionTests.cs create mode 100644 Notation.Plugin.AzureKeyVault.Tests/Protocol/DescribeKeyTests.cs create mode 100644 Notation.Plugin.AzureKeyVault.Tests/Protocol/ErrorTests.cs create mode 100644 Notation.Plugin.AzureKeyVault.Tests/Protocol/GenerateSignatureTests.cs create mode 100644 Notation.Plugin.AzureKeyVault.Tests/Protocol/GetMetadataResponseTests.cs create mode 100644 Notation.Plugin.AzureKeyVault.Tests/Protocol/KeySpecTests.cs create mode 100644 Notation.Plugin.AzureKeyVault.Tests/Protocol/PluginIOTests.cs create mode 100644 Notation.Plugin.AzureKeyVault.Tests/TestData/dsa_2048_cert.pem create mode 100644 Notation.Plugin.AzureKeyVault.Tests/TestData/ec_163_cert.pem create mode 100644 Notation.Plugin.AzureKeyVault.Tests/TestData/ec_256_cert.pem create mode 100644 Notation.Plugin.AzureKeyVault.Tests/TestData/ec_384_cert.pem create mode 100644 Notation.Plugin.AzureKeyVault.Tests/TestData/ec_521_cert.pem create mode 100644 Notation.Plugin.AzureKeyVault.Tests/TestData/rsa_1024_cert.pem create mode 100644 Notation.Plugin.AzureKeyVault.Tests/TestData/rsa_2048_cert.pem create mode 100644 Notation.Plugin.AzureKeyVault.Tests/TestData/rsa_3072_cert.pem create mode 100644 Notation.Plugin.AzureKeyVault.Tests/TestData/rsa_4096_cert.pem diff --git a/Notation.Plugin.AzureKeyVault.Tests/Command/DescribeKeyTests.cs b/Notation.Plugin.AzureKeyVault.Tests/Command/DescribeKeyTests.cs new file mode 100644 index 00000000..00f7f5a6 --- /dev/null +++ b/Notation.Plugin.AzureKeyVault.Tests/Command/DescribeKeyTests.cs @@ -0,0 +1,52 @@ +using System.IO; +using System.Security.Cryptography.X509Certificates; +using System.Threading.Tasks; +using Moq; +using Notation.Plugin.AzureKeyVault.Client; +using Notation.Plugin.AzureKeyVault.Command; +using Notation.Plugin.Protocol; +using Xunit; + +namespace Notation.Plugin.AzureKeyVault.Command.Tests +{ + public class DescribeKeyTests + { + [Fact] + public async Task RunAsync_ReturnsValidDescribeKeyResponseAsync() + { + // Arrange + var keyId = "https://testvault.vault.azure.net/keys/testkey/123"; + var expectedKeySpec = "RSA-2048"; + var mockCert = new X509Certificate2(Path.Combine(Directory.GetCurrentDirectory(), "TestData", "rsa_2048_cert.pem")); + + var mockKeyVaultClient = new Mock(); + mockKeyVaultClient.Setup(client => client.GetCertificateAsync()).ReturnsAsync(mockCert); + + var request = new DescribeKeyRequest(contractVersion: "1.0", keyId); + var describeKeyCommand = new DescribeKey(request, mockKeyVaultClient.Object); + + // Act + var result = await describeKeyCommand.RunAsync(); + + // Assert + Assert.IsType(result); + var response = result as DescribeKeyResponse; + if (response == null) + { + throw new System.Exception("response is null"); + } + Assert.Equal(keyId, response.KeyId); + Assert.Equal(expectedKeySpec, response.KeySpec); + } + + [Fact] + public void Constructor_ThrowsValidationException_WhenInvalidInput() + { + // Arrange + string invalidInputJson = "null"; + + // Act & Assert + Assert.Throws(() => new DescribeKey(invalidInputJson)); + } + } +} diff --git a/Notation.Plugin.AzureKeyVault.Tests/Command/GetPluginMetadataTests.cs b/Notation.Plugin.AzureKeyVault.Tests/Command/GetPluginMetadataTests.cs index 43f4ac6f..0fb07a0c 100644 --- a/Notation.Plugin.AzureKeyVault.Tests/Command/GetPluginMetadataTests.cs +++ b/Notation.Plugin.AzureKeyVault.Tests/Command/GetPluginMetadataTests.cs @@ -1,9 +1,8 @@ -using Xunit; using System.Threading.Tasks; +using Xunit; using Notation.Plugin.Protocol; -using Notation.Plugin.AzureKeyVault.Command; -namespace Notation.Plugin.AzureKeyVault.Tests +namespace Notation.Plugin.AzureKeyVault.Command.Tests { public class GetPluginMetadataTests { @@ -14,7 +13,7 @@ public async Task RunAsync_ReturnsExpectedMetadata() var getPluginMetadata = new GetPluginMetadata(); // Act - var result = await getPluginMetadata.RunAsync(""); + var result = await getPluginMetadata.RunAsync(); // Assert Assert.IsType(result); diff --git a/Notation.Plugin.AzureKeyVault.Tests/KeyVault/KeySpecExtensionTests.cs b/Notation.Plugin.AzureKeyVault.Tests/KeyVault/KeySpecExtensionTests.cs new file mode 100644 index 00000000..db277692 --- /dev/null +++ b/Notation.Plugin.AzureKeyVault.Tests/KeyVault/KeySpecExtensionTests.cs @@ -0,0 +1,39 @@ +using System; +using Xunit; +using Notation.Plugin.Protocol; + +namespace Notation.Plugin.AzureKeyVault.Client.Tests +{ + public class KeySpecExtensionTests + { + [Theory] + [InlineData(KeyType.RSA, 2048, "PS256")] + [InlineData(KeyType.RSA, 3072, "PS384")] + [InlineData(KeyType.RSA, 4096, "PS512")] + [InlineData(KeyType.EC, 256, "ES256")] + [InlineData(KeyType.EC, 384, "ES384")] + [InlineData(KeyType.EC, 521, "ES512")] + public void ToSignatureAlgorithm_ValidKeySpecs_ReturnsCorrectSignatureAlgorithm(KeyType keyType, int keySize, string expectedAlgorithm) + { + // Arrange + var keySpec = new KeySpec(keyType, keySize); + // Act + var signatureAlgorithm = keySpec.ToSignatureAlgorithm(); + + // Assert + Assert.Equal(expectedAlgorithm, signatureAlgorithm); + } + + [Theory] + [InlineData(KeyType.RSA, 1024)] + [InlineData(KeyType.EC, 128)] + public void ToSignatureAlgorithm_InvalidKeySpecs_ThrowsArgumentException(KeyType keyType, int keySize) + { + // Arrange + var keySpec = new KeySpec(keyType, keySize); + + // Act & Assert + Assert.Throws(() => keySpec.ToSignatureAlgorithm()); + } + } +} \ No newline at end of file diff --git a/Notation.Plugin.AzureKeyVault.Tests/KeyVault/KeyVaultClientTests.cs b/Notation.Plugin.AzureKeyVault.Tests/KeyVault/KeyVaultClientTests.cs new file mode 100644 index 00000000..d3273b8b --- /dev/null +++ b/Notation.Plugin.AzureKeyVault.Tests/KeyVault/KeyVaultClientTests.cs @@ -0,0 +1,179 @@ +using System; +using System.IO; +using System.Security.Cryptography.X509Certificates; +using System.Threading; +using System.Threading.Tasks; +using Azure; +using Azure.Core; +using Azure.Security.KeyVault.Certificates; +using Azure.Security.KeyVault.Keys; +using Azure.Security.KeyVault.Keys.Cryptography; +using Moq; +using Xunit; +using Notation.Plugin.Protocol; + +namespace Notation.Plugin.AzureKeyVault.Client.Tests +{ + public class KeyVaultClientTests + { + [Fact] + public void TestConstructorWithKeyId() + { + string keyId = "https://myvault.vault.azure.net/keys/my-key/123"; + + KeyVaultClient keyVaultClient = new KeyVaultClient(keyId); + + Assert.Equal("my-key", keyVaultClient.Name); + Assert.Equal("123", keyVaultClient.Version); + Assert.Equal(keyId, keyVaultClient.KeyId); + } + + [Fact] + public void TestConstructorWithKeyVaultUrlNameVersion() + { + string keyVaultUrl = "https://myvault.vault.azure.net"; + string name = "my-key"; + string version = "123"; + + KeyVaultClient keyVaultClient = new KeyVaultClient(keyVaultUrl, name, version); + + Assert.Equal(name, keyVaultClient.Name); + Assert.Equal(version, keyVaultClient.Version); + Assert.Equal($"{keyVaultUrl}/keys/{name}/{version}", keyVaultClient.KeyId); + } + + [Theory] + [InlineData("https://myvault.vault.azure.net/invalid/my-key/123")] + [InlineData("https://myvault.vault.azure.net/keys/my-key")] + [InlineData("https://myvault.vault.azure.net/keys/my-key/")] + [InlineData("http://myvault.vault.azure.net/keys/my-key/123")] + public void TestConstructorWithInvalidKeyId(string invalidKeyId) + { + Assert.Throws(() => new KeyVaultClient(invalidKeyId)); + } + + [Theory] + [InlineData("")] + [InlineData(null)] + public void TestConstructorWithEmptyKeyId(string invalidKeyId) + { + Assert.Throws(() => new KeyVaultClient(invalidKeyId)); + } + + private class TestableKeyVaultClient : KeyVaultClient + { + public TestableKeyVaultClient(string keyVaultUrl, string name, string version, CryptographyClient cryptoClient) + : base(keyVaultUrl, name, version) + { + this._cryptoClient = new Lazy(() => cryptoClient); + } + + public TestableKeyVaultClient(string keyVaultUrl, string name, string version, CertificateClient certificateClient) + : base(keyVaultUrl, name, version) + { + this._certificateClient = new Lazy(() => certificateClient); + } + } + + private TestableKeyVaultClient CreateMockedKeyVaultClient(SignResult signResult) + { + var mockCryptoClient = new Mock(new Uri("https://fake.vault.azure.net/keys/fake-key/123"), new Mock().Object); + mockCryptoClient.Setup(c => c.SignDataAsync(It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(signResult); + + return new TestableKeyVaultClient("https://fake.vault.azure.net", "fake-key", "123", mockCryptoClient.Object); + } + + private TestableKeyVaultClient CreateMockedKeyVaultClient(KeyVaultCertificate certificate) + { + var mockCertificateClient = new Mock(new Uri("https://fake.vault.azure.net/certificates/fake-certificate/123"), new Mock().Object); + mockCertificateClient.Setup(c => c.GetCertificateVersionAsync(It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(Response.FromValue(certificate, new Mock().Object)); + + return new TestableKeyVaultClient("https://fake.vault.azure.net", "fake-certificate", "123", mockCertificateClient.Object); + } + + [Fact] + public async Task TestSignAsyncReturnsExpectedSignature() + { + var signResult = CryptographyModelFactory.SignResult( + keyId: "https://fake.vault.azure.net/keys/fake-key/123", + signature: new byte[] { 1, 2, 3 }, + algorithm: SignatureAlgorithm.RS256); + + TestableKeyVaultClient keyVaultClient = CreateMockedKeyVaultClient(signResult); + byte[] payload = new byte[] { 4, 5, 6 }; + + byte[] signature = await keyVaultClient.SignAsync(SignatureAlgorithm.RS256, payload); + + Assert.Equal(signResult.Signature, signature); + } + + [Fact] + public async Task TestSignAsyncThrowsExceptionOnInvalidKeyId() + { + var signResult = CryptographyModelFactory.SignResult( + keyId: "https://fake.vault.azure.net/keys/invalid-key/123", + signature: new byte[] { 1, 2, 3 }, + algorithm: SignatureAlgorithm.RS256); + + TestableKeyVaultClient keyVaultClient = CreateMockedKeyVaultClient(signResult); + byte[] payload = new byte[] { 4, 5, 6 }; + + await Assert.ThrowsAsync(async () => await keyVaultClient.SignAsync(SignatureAlgorithm.RS256, payload)); + } + + [Fact] + public async Task TestSignAsyncThrowsExceptionOnInvalidAlgorithm() + { + var signResult = CryptographyModelFactory.SignResult( + keyId: "https://fake.vault.azure.net/keys/fake-key/123", + signature: new byte[] { 1, 2, 3 }, + algorithm: SignatureAlgorithm.RS384); + + TestableKeyVaultClient keyVaultClient = CreateMockedKeyVaultClient(signResult); + byte[] payload = new byte[] { 4, 5, 6 }; + await Assert.ThrowsAsync(async () => await keyVaultClient.SignAsync(SignatureAlgorithm.RS256, payload)); + } + + [Fact] + public async Task GetCertificateAsync_ReturnsCertificate() + { + var testCertificate = new X509Certificate2(Path.Combine(Directory.GetCurrentDirectory(), "TestData", "rsa_2048_cert.pem")); + var signResult = CryptographyModelFactory.SignResult( + keyId: "https://fake.vault.azure.net/keys/fake-key/123", + signature: new byte[] { 1, 2, 3 }, + algorithm: SignatureAlgorithm.RS384); + + var keyVaultCertificate = CertificateModelFactory.KeyVaultCertificate( + properties: CertificateModelFactory.CertificateProperties(version: "123"), + cer: testCertificate.RawData); + + var keyVaultClient = CreateMockedKeyVaultClient(keyVaultCertificate); + var certificate = await keyVaultClient.GetCertificateAsync(); + + Assert.NotNull(certificate); + Assert.IsType(certificate); + Assert.Equal("123", keyVaultCertificate.Properties.Version); + Assert.Equal(testCertificate.RawData, certificate.RawData); + } + + [Fact] + public async Task GetCertificateAsyncThrowValidationException() + { + var testCertificate = new X509Certificate2(Path.Combine(Directory.GetCurrentDirectory(), "TestData", "rsa_2048_cert.pem")); + var signResult = CryptographyModelFactory.SignResult( + keyId: "https://fake.vault.azure.net/keys/fake-key/123", + signature: new byte[] { 1, 2, 3 }, + algorithm: SignatureAlgorithm.RS384); + + var keyVaultCertificate = CertificateModelFactory.KeyVaultCertificate( + properties: CertificateModelFactory.CertificateProperties(version: "1234"), + cer: testCertificate.RawData); + + var keyVaultClient = CreateMockedKeyVaultClient(keyVaultCertificate); + + await Assert.ThrowsAsync(async () => await keyVaultClient.GetCertificateAsync()); + } + } +} diff --git a/Notation.Plugin.AzureKeyVault.Tests/Notation.Plugin.AzureKeyVault.Tests.csproj b/Notation.Plugin.AzureKeyVault.Tests/Notation.Plugin.AzureKeyVault.Tests.csproj index 7b708c6d..d4bd997c 100644 --- a/Notation.Plugin.AzureKeyVault.Tests/Notation.Plugin.AzureKeyVault.Tests.csproj +++ b/Notation.Plugin.AzureKeyVault.Tests/Notation.Plugin.AzureKeyVault.Tests.csproj @@ -9,6 +9,8 @@ + + runtime; build; native; contentfiles; analyzers; buildtransitive @@ -18,6 +20,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive all + diff --git a/Notation.Plugin.AzureKeyVault.Tests/Protocol/CertificateExtensionTests.cs b/Notation.Plugin.AzureKeyVault.Tests/Protocol/CertificateExtensionTests.cs new file mode 100644 index 00000000..994f470a --- /dev/null +++ b/Notation.Plugin.AzureKeyVault.Tests/Protocol/CertificateExtensionTests.cs @@ -0,0 +1,61 @@ +using System.IO; +using System.Security.Cryptography.X509Certificates; +using Xunit; + +namespace Notation.Plugin.Protocol.Tests +{ + public class CertificateExtensionTests + { + [Theory] + [InlineData("RSA", 2048)] + [InlineData("RSA", 3072)] + [InlineData("RSA", 4096)] + [InlineData("EC", 256)] + [InlineData("EC", 384)] + [InlineData("EC", 521)] + public void KeySpec_ValidKeySize_ReturnsKeySpec(string keyType, int keySize) + { + // Arrange + X509Certificate2 certificate = LoadCertificate(keyType, keySize); + + // Act + KeySpec result = certificate.KeySpec(); + + // Assert + Assert.Equal(keyType, result.Type.ToString()); + Assert.Equal(keySize, result.Size); + } + + [Theory] + [InlineData("RSA", 1024)] + [InlineData("EC", 163)] + public void KeySpec_InvalidKeySize_ThrowsValidationException(string keyType, int keySize) + { + // Arrange + X509Certificate2 certificate = LoadCertificate(keyType, keySize); + + // Act & Assert + Assert.Throws(() => certificate.KeySpec()); + } + + [Fact] + public void KeySpec_UnsupportedPublicKeyType_ThrowsValidationException() + { + // Arrange + X509Certificate2 certificate = LoadCertificate("dsa", 2048); + + // Act & Assert + Assert.Throws(() => certificate.KeySpec()); + } + + // + // Load certificate from file. + // + private static X509Certificate2 LoadCertificate(string keyType, int keySize) + { + var certName = $"{keyType.ToLower()}_{keySize}_cert.pem"; + return new X509Certificate2(Path.Combine(Directory.GetCurrentDirectory(), "TestData", certName)); + } + + } +} diff --git a/Notation.Plugin.AzureKeyVault.Tests/Protocol/DescribeKeyTests.cs b/Notation.Plugin.AzureKeyVault.Tests/Protocol/DescribeKeyTests.cs new file mode 100644 index 00000000..e102b1ec --- /dev/null +++ b/Notation.Plugin.AzureKeyVault.Tests/Protocol/DescribeKeyTests.cs @@ -0,0 +1,71 @@ +using System; +using Xunit; + +namespace Notation.Plugin.Protocol.Tests +{ + public class DescribeKeyTests + { + [Fact] + public void DescribeKeyRequest_ValidParameters() + { + // Arrange + string contractVersion = "1.0"; + string keyId = "test-key-id"; + + // Act + DescribeKeyRequest request = new DescribeKeyRequest(contractVersion, keyId); + + // Assert + Assert.Equal(contractVersion, request.ContractVersion); + Assert.Equal(keyId, request.KeyId); + } + + [Theory] + [InlineData(null, "test-key-id")] + [InlineData("", "test-key-id")] + [InlineData("1.0", null)] + [InlineData("1.0", "")] + public void DescribeKeyRequest_InvalidParameters_ThrowsException(string contractVersion, string keyId) + { + // Act & Assert + Assert.Throws(() => new DescribeKeyRequest(contractVersion, keyId)); + } + + [Fact] + public void DescribeKeyRequest_UnsupportedContractVersion_ThrowsException() + { + // Arrange + string contractVersion = "2.0"; + string keyId = "test-key-id"; + + // Act & Assert + Assert.Throws(() => new DescribeKeyRequest(contractVersion, keyId)); + } + + [Fact] + public void DescribeKeyResponse_ValidParameters() + { + // Arrange + string keyId = "test-key-id"; + string keySpec = "RSA-2048"; + + // Act + DescribeKeyResponse response = new DescribeKeyResponse(keyId, keySpec); + + // Assert + Assert.Equal(keyId, response.KeyId); + Assert.Equal(keySpec, response.KeySpec); + } + + [Theory] + [InlineData(null, "RSA-2048")] + [InlineData("", "RSA-2048")] + [InlineData("test-key-id", null)] + [InlineData("test-key-id", "")] + public void DescribeKeyResponse_InvalidParameters_ThrowsException(string keyId, string keySpec) + { + // Act & Assert + Assert.Throws(() => new DescribeKeyResponse(keyId, keySpec)); + } + } +} diff --git a/Notation.Plugin.AzureKeyVault.Tests/Protocol/ErrorTests.cs b/Notation.Plugin.AzureKeyVault.Tests/Protocol/ErrorTests.cs new file mode 100644 index 00000000..95493e76 --- /dev/null +++ b/Notation.Plugin.AzureKeyVault.Tests/Protocol/ErrorTests.cs @@ -0,0 +1,81 @@ +using System; +using System.IO; +using Xunit; + +namespace Notation.Plugin.Protocol.Tests +{ + public class ErrorAndExceptionTests + { + [Fact] + public void PluginException_ValidParameters() + { + // Arrange + string message = "Test error message"; + string code = "TEST_ERROR_CODE"; + + // Act + PluginException exception = new PluginException(message, code); + + // Assert + Assert.Equal(message, exception.Message); + Assert.Equal(code, exception.Code); + } + + [Fact] + public void PluginException_DefaultErrorCode() + { + // Arrange + string message = "Test error message"; + + // Act + PluginException exception = new PluginException(message); + + // Assert + Assert.Equal(message, exception.Message); + Assert.Equal(Error.ERROR, exception.Code); + } + + [Fact] + public void ValidationException_ValidParameters() + { + // Arrange + string message = "Test validation error message"; + + // Act + ValidationException exception = new ValidationException(message); + + // Assert + Assert.Equal(message, exception.Message); + Assert.Equal(Error.VALIDATION_ERROR, exception.Code); + } + + [Fact] + public void PrintError_WritesToStandardError() + { + // Arrange + string errorCode = "TEST_ERROR_CODE"; + string errorMessage = "Test error message"; + string expectedOutput = "{\"errorCode\":\"TEST_ERROR_CODE\",\"errorMessage\":\"Test error message\"}"; + + // Redirect standard error output + StringWriter stringWriter = new StringWriter(); + TextWriter originalStdErr = Console.Error; + Console.SetError(stringWriter); + + try + { + // Act + Error.PrintError(errorCode, errorMessage); + + // Assert + string actualOutput = stringWriter.ToString().Trim(); + Assert.Equal(expectedOutput, actualOutput); + } + finally + { + // Restore original standard error output + Console.SetError(originalStdErr); + } + } + } +} \ No newline at end of file diff --git a/Notation.Plugin.AzureKeyVault.Tests/Protocol/GenerateSignatureTests.cs b/Notation.Plugin.AzureKeyVault.Tests/Protocol/GenerateSignatureTests.cs new file mode 100644 index 00000000..d81f74bb --- /dev/null +++ b/Notation.Plugin.AzureKeyVault.Tests/Protocol/GenerateSignatureTests.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using Xunit; + +namespace Notation.Plugin.Protocol.Tests +{ + public class GenerateSignatureTests + { + [Fact] + public void GenerateSignatureRequest_ThrowsArgumentNullException_WhenFieldsAreEmptyOrNull() + { + // Arrange + string contractVersion = "test-version"; + string keyId = "test-key-id"; + Dictionary pluginConfig = new Dictionary { { "key1", "value1" } }; + string keySpec = "test-key-spec"; + string hashAlgorithm = "SHA256"; + byte[] payload = new byte[] { 1, 2, 3, 4, 5 }; + + // Assert + Assert.Throws(() => new GenerateSignatureRequest(string.Empty, keyId, pluginConfig, keySpec, hashAlgorithm, payload)); + Assert.Throws(() => new GenerateSignatureRequest(contractVersion, string.Empty, pluginConfig, keySpec, hashAlgorithm, payload)); + Assert.Throws(() => new GenerateSignatureRequest(contractVersion, keyId, pluginConfig, string.Empty, hashAlgorithm, payload)); + Assert.Throws(() => new GenerateSignatureRequest(contractVersion, keyId, pluginConfig, keySpec, string.Empty, payload)); + Assert.Throws(() => new GenerateSignatureRequest(contractVersion, keyId, pluginConfig, keySpec, hashAlgorithm, new byte[0])); + } + + [Fact] + public void GenerateSignatureResponse_ThrowsArgumentNullException_WhenFieldsAreEmptyOrNull() + { + // Arrange + string keyId = "test-key-id"; + byte[] signature = new byte[] { 1, 2, 3, 4, 5 }; + string signingAlgorithm = "RSA-PSS"; + List certificateChain = new List { new byte[] { 6, 7, 8, 9, 10 } }; + + // Assert + Assert.Throws(() => new GenerateSignatureResponse(string.Empty, signature, signingAlgorithm, certificateChain)); + Assert.Throws(() => new GenerateSignatureResponse(keyId, new byte[0], signingAlgorithm, certificateChain)); + Assert.Throws(() => new GenerateSignatureResponse(keyId, signature, string.Empty, certificateChain)); + Assert.Throws(() => new GenerateSignatureResponse(keyId, signature, signingAlgorithm, new List())); + } + } +} diff --git a/Notation.Plugin.AzureKeyVault.Tests/Protocol/GetMetadataResponseTests.cs b/Notation.Plugin.AzureKeyVault.Tests/Protocol/GetMetadataResponseTests.cs new file mode 100644 index 00000000..0843e5b5 --- /dev/null +++ b/Notation.Plugin.AzureKeyVault.Tests/Protocol/GetMetadataResponseTests.cs @@ -0,0 +1,30 @@ +using Xunit; + +namespace Notation.Plugin.Protocol.Tests +{ + public class GetMetadataResponseTests + { + [Fact] + public void GetMetadataResponse_CreatesInstance_WithCorrectValues() + { + // Arrange + string name = "Test Plugin"; + string description = "A test plugin for Notation"; + string version = "1.0.0"; + string url = "https://github.com/example/test-plugin"; + string[] supportedContractVersions = new[] { "1.0" }; + string[] capabilities = new[] { "describe-key", "generate-signature" }; + + // Act + GetMetadataResponse response = new GetMetadataResponse(name, description, version, url, supportedContractVersions, capabilities); + + // Assert + Assert.Equal(name, response.Name); + Assert.Equal(description, response.Description); + Assert.Equal(version, response.Version); + Assert.Equal(url, response.Url); + Assert.Equal(supportedContractVersions, response.SupportedContractVersions); + Assert.Equal(capabilities, response.Capabilities); + } + } +} diff --git a/Notation.Plugin.AzureKeyVault.Tests/Protocol/KeySpecTests.cs b/Notation.Plugin.AzureKeyVault.Tests/Protocol/KeySpecTests.cs new file mode 100644 index 00000000..6af88699 --- /dev/null +++ b/Notation.Plugin.AzureKeyVault.Tests/Protocol/KeySpecTests.cs @@ -0,0 +1,44 @@ +using System; +using Xunit; + +namespace Notation.Plugin.Protocol.Tests +{ + public class KeySpecTests + { + [Theory] + [InlineData(KeyType.RSA, 2048, "RSA-2048", "RSASSA-PSS-SHA-256")] + [InlineData(KeyType.RSA, 3072, "RSA-3072", "RSASSA-PSS-SHA-384")] + [InlineData(KeyType.RSA, 4096, "RSA-4096", "RSASSA-PSS-SHA-512")] + [InlineData(KeyType.EC, 256, "EC-256", "ECDSA-SHA-256")] + [InlineData(KeyType.EC, 384, "EC-384", "ECDSA-SHA-384")] + [InlineData(KeyType.EC, 521, "EC-521", "ECDSA-SHA-512")] + public void KeySpec_EncodeKeySpecAndToSigningAlgorithm_ReturnsCorrectValues(KeyType keyType, int size, string expectedKeySpec, string expectedSigningAlgorithm) + { + // Arrange + KeySpec keySpec = new KeySpec(keyType, size); + + // Act + string encodedKeySpec = keySpec.EncodeKeySpec(); + string signingAlgorithm = keySpec.ToSigningAlgorithm(); + + // Assert + Assert.Equal(expectedKeySpec, encodedKeySpec); + Assert.Equal(expectedSigningAlgorithm, signingAlgorithm); + } + + [Theory] + [InlineData(KeyType.RSA, 1024)] + [InlineData(KeyType.RSA, 3070)] + [InlineData(KeyType.EC, 128)] + [InlineData(KeyType.EC, 500)] + public void KeySpec_EncodeKeySpecAndToSigningAlgorithm_ThrowsArgumentExceptionForInvalidSizes(KeyType keyType, int size) + { + // Arrange + KeySpec keySpec = new KeySpec(keyType, size); + + // Act & Assert + Assert.Throws(() => keySpec.EncodeKeySpec()); + Assert.Throws(() => keySpec.ToSigningAlgorithm()); + } + } +} diff --git a/Notation.Plugin.AzureKeyVault.Tests/Protocol/PluginIOTests.cs b/Notation.Plugin.AzureKeyVault.Tests/Protocol/PluginIOTests.cs new file mode 100644 index 00000000..49a25c96 --- /dev/null +++ b/Notation.Plugin.AzureKeyVault.Tests/Protocol/PluginIOTests.cs @@ -0,0 +1,62 @@ +using System; +using System.IO; +using Xunit; + +namespace Notation.Plugin.Protocol.Tests +{ + public class PluginIOTests + { + [Fact] + public void ReadInput_ReturnsCorrectString() + { + // Arrange + const string expectedInput = "{\"test\":\"value\"}"; + using var stringReader = new StringReader(expectedInput); + Console.SetIn(stringReader); + + // Act + string actualInput = PluginIO.ReadInput(); + + // Assert + Assert.Equal(expectedInput, actualInput); + } + + [Fact] + public void ReadInput_ThrowsValidationExceptionOnEmptyInput() + { + // Arrange + using var stringReader = new StringReader(string.Empty); + Console.SetIn(stringReader); + + // Act & Assert + Assert.Throws(() => PluginIO.ReadInput()); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void WriteOutput_WritesCorrectOutput(bool stderr) + { + // Arrange + var obj = new { test = "value" }; + const string expectedOutput = "{\"test\":\"value\"}\n"; + + using var stringWriter = new StringWriter(); + if (stderr) + { + Console.SetError(stringWriter); + } + else + { + Console.SetOut(stringWriter); + } + + // Act + PluginIO.WriteOutput(obj, stderr); + + // Assert + string actualOutput = stringWriter.ToString(); + Assert.Equal(expectedOutput, actualOutput); + } + } +} diff --git a/Notation.Plugin.AzureKeyVault.Tests/TestData/dsa_2048_cert.pem b/Notation.Plugin.AzureKeyVault.Tests/TestData/dsa_2048_cert.pem new file mode 100644 index 00000000..35863733 --- /dev/null +++ b/Notation.Plugin.AzureKeyVault.Tests/TestData/dsa_2048_cert.pem @@ -0,0 +1,24 @@ +-----BEGIN CERTIFICATE----- +MIIECzCCA7kCFD8dwf2XjrViDyRLsuBR9mb8sUCuMAsGCWCGSAFlAwQDAjAWMRQw +EgYDVQQDDAtleGFtcGxlLmNvbTAeFw0yMzA0MjUwNjEyMjVaFw0yNDA0MjQwNjEy +MjVaMBYxFDASBgNVBAMMC2V4YW1wbGUuY29tMIIDQjCCAjUGByqGSM44BAEwggIo +AoIBAQD88KOo4DvKMrg1qFBsjNNEevAfhoksyv9Co53WTMeptS9IPNbfKJWrlwUZ +m6HoN+lBkvYZpLfDiBIvaCl4DX+oSQzxJyY2i6TkUTsSqpUd6JLSIJpXxXvcRACC +w6Bceh38YyrV78BN+q9zDd+lFk5NEtpU35lNfU2PxV4ZHjwTNfULzrjX4ExHj8eR +aStqbiOPf5l4QgU38rzq/CpQWs9VaTWo1+9J+f2z6OfxjlhMjDzmCPl5QEnyrkkN +C7AZXhMPTAX9zexoVGgVWTqmbd9Zi9VFSxfkQuGvx5vSYjUNJeIHtW4DXRF49Zfa +GcyhvFdBDSJRL/vY1crKzpctIhBZAh0AnPvrnNsR3YPLukax+OJcZHIy9zRfvA6B +BB/UgQKCAQB4FpPx1p7eHiqqxi05Omatb6RJJgIMa7RPqB85JWuB4izWm85w1NL7 +NT/nrtiudCaVZc2PuHzlWiGJ3e5EAJ7nYXU8I2XSWUtirJL1f9ln9nZQRksk6/mP +/ioHmbQOsnQfZMs4ruAI13Fo0OcVqmDKoPLV9K6AE1kA/GzS4asFvpf4CX+Uo0bV +aw8RpkKwqbf2ImlbsZDiIo2xWGt9Q4E5AiYTgSvmIRKayTkUuqAr+0JZ81ViAL+L +txRmRatoYM2Q2BkV9cCVsW7TM2JK9gUxbBF7aqnxV5zWkAtqDCWbABQiaolrEij6 +Dx9pqCxTxLwxU6bCaqw5tXru7/K4ybABA4IBBQACggEADXe/UAEudwDpXnphv7L9 +l75+5ute+kfjGfgaP85pni//Cr6jA02uO4I5U+V4dTKk1vs5g/u2Gvv8cAx9WUVC +HsQwBGrysFhs1FnX5mRWDDdC9Bi8ikmvYgsc+MAIifDhwe/lAupVe6gJ2Ir+G9xN ++gr+jH8uIxZS+LrCePGCWKIoI+Jqa14fZvEQnQ4zJv4m+l+DshXXQF7NMZ8VWlk2 +Z7YWq6sUogSwmm5T5I/d08iJyx9Ty7KGZPh6L3s7cFJ2WKJcTWB6Keb+ABWHrF0Q +q4b9Tb82SaDyAeQO8pGdSMwd02zh6kTNSjT0JHYzJU/babx/o9bW00XbLhPoryqc +1TALBglghkgBZQMEAwIDPwAwPAIcT5Mex3EvQcL6iv0ZV5eJkPg+8/jkc7bGfDyV +IwIcESUbQUzyekWeEaIy4xQW9Lev/+11Ykq98f3cEw== +-----END CERTIFICATE----- diff --git a/Notation.Plugin.AzureKeyVault.Tests/TestData/ec_163_cert.pem b/Notation.Plugin.AzureKeyVault.Tests/TestData/ec_163_cert.pem new file mode 100644 index 00000000..b4e4c984 --- /dev/null +++ b/Notation.Plugin.AzureKeyVault.Tests/TestData/ec_163_cert.pem @@ -0,0 +1,8 @@ +-----BEGIN CERTIFICATE----- +MIH4MIG3AhREvOeQUB2hn+XAPyz70ITqejS9jDAKBggqhkjOPQQDAjAWMRQwEgYD +VQQDDAtleGFtcGxlLmNvbTAeFw0yMzA0MjUwNjIyNDhaFw0yNDA0MjQwNjIyNDha +MBYxFDASBgNVBAMMC2V4YW1wbGUuY29tMEMwEwYHKoZIzj0CAQYIKoZIzj0DAAED +LAAEAIcvA4WsGAkUYfM5cJCd62r+u1QDBMOesjFsR/RDuDV7RXMdUTqDskNxMAoG +CCqGSM49BAMCAzAAMC0CFQJUK5bQJRDsqCYfbIEN9CLkVHcQ0QIUPXRPwctr5Yt9 +J2LczAgawx60nm8= +-----END CERTIFICATE----- diff --git a/Notation.Plugin.AzureKeyVault.Tests/TestData/ec_256_cert.pem b/Notation.Plugin.AzureKeyVault.Tests/TestData/ec_256_cert.pem new file mode 100644 index 00000000..d3f0484e --- /dev/null +++ b/Notation.Plugin.AzureKeyVault.Tests/TestData/ec_256_cert.pem @@ -0,0 +1,9 @@ +-----BEGIN CERTIFICATE----- +MIIBJzCBzQIUXQystHHpbJJoMkKypH2wuWtsDXcwCgYIKoZIzj0EAwIwFjEUMBIG +A1UEAwwLZXhhbXBsZS5jb20wHhcNMjMwNDI1MDYxOTAxWhcNMjQwNDI0MDYxOTAx +WjAWMRQwEgYDVQQDDAtleGFtcGxlLmNvbTBZMBMGByqGSM49AgEGCCqGSM49AwEH +A0IABCW3pAhuzuj2pgao0g33EO+ir8Z1P7GL4KeC8cx6vIX2Y3Me6RDdfE52miwq +9uBa75nNDCDmYRCkhEbQjBfNcU4wCgYIKoZIzj0EAwIDSQAwRgIhAOliErXEpBnQ +q/pGNRqQAH2+EAS5EtPiSk/Vz71L/9ovAiEA6i/BqB5/FSXxEaYYxcPHuLLjPkaL +s7Uun/3IOVDyWSY= +-----END CERTIFICATE----- diff --git a/Notation.Plugin.AzureKeyVault.Tests/TestData/ec_384_cert.pem b/Notation.Plugin.AzureKeyVault.Tests/TestData/ec_384_cert.pem new file mode 100644 index 00000000..3b89e2b5 --- /dev/null +++ b/Notation.Plugin.AzureKeyVault.Tests/TestData/ec_384_cert.pem @@ -0,0 +1,10 @@ +-----BEGIN CERTIFICATE----- +MIIBYjCB6gIUFef9WNePjZnIF+gtD2rmkrL7tgMwCgYIKoZIzj0EAwIwFjEUMBIG +A1UEAwwLZXhhbXBsZS5jb20wHhcNMjMwNDI1MDYxOTAxWhcNMjQwNDI0MDYxOTAx +WjAWMRQwEgYDVQQDDAtleGFtcGxlLmNvbTB2MBAGByqGSM49AgEGBSuBBAAiA2IA +BD1SVgaMSQkGOGGDErKvoVfMkP/xEM0euOHMukTvlvMkdOfUvw1M+cHm3ZIYTXKt +xpuMFYinm1Dw9I2FeVGzH9SrMhegfVMNNy12iY69O2ipunP+7uumS3al2IQZwX4e +7zAKBggqhkjOPQQDAgNnADBkAjB4z5M6MBJNqfJKXNJtJXhr/zsA/lBiIh13kRCQ +BApEvwk7ChqwUWbEKZOt3Gb+0gUCMDALweIIDX7YoYzr2DJov4NX3Ll18rtwhyUY +4G9IAoPLWXUCSkFD/RgF+ZgTlTK/7A== +-----END CERTIFICATE----- diff --git a/Notation.Plugin.AzureKeyVault.Tests/TestData/ec_521_cert.pem b/Notation.Plugin.AzureKeyVault.Tests/TestData/ec_521_cert.pem new file mode 100644 index 00000000..c069064c --- /dev/null +++ b/Notation.Plugin.AzureKeyVault.Tests/TestData/ec_521_cert.pem @@ -0,0 +1,12 @@ +-----BEGIN CERTIFICATE----- +MIIBrzCCARACFHv1Q0ZFDbSslWU4F9Klkl9+WdRaMAoGCCqGSM49BAMCMBYxFDAS +BgNVBAMMC2V4YW1wbGUuY29tMB4XDTIzMDQyNTA2MTkwMVoXDTI0MDQyNDA2MTkw +MVowFjEUMBIGA1UEAwwLZXhhbXBsZS5jb20wgZswEAYHKoZIzj0CAQYFK4EEACMD +gYYABACeUNiz/qge8mwsjYJvnB2OR+4cMi8th1rUwiEEzuD30GtIJiM/6bcssj1c +N70jix+MMqcBLJiYtEl8lOI4i8GsnADzlzp4Ogygl2j5/xBQqQggPNve36e28U2M +KSGjU4WrFFWQmb08Shxg3OIHzihat/lunQOC6frbNvXrZiV79VxMuDAKBggqhkjO +PQQDAgOBjAAwgYgCQgFV3YH3OiEDakgoruBwfl4D9pzjpvWsh43qwY4OOcLBRQJG +8r54DcRrc+Q5TKEpcMjre54fHS1DwaWtXYw+BN7aSwJCASI5LhddFSNfkiX2ExtY +5hdXm9rSYfOdGHNrZilbrxPLZdxIoRUI2nDxoBFpPOzEO6XahyNu4fLB2MyoeBax +IbZd +-----END CERTIFICATE----- diff --git a/Notation.Plugin.AzureKeyVault.Tests/TestData/rsa_1024_cert.pem b/Notation.Plugin.AzureKeyVault.Tests/TestData/rsa_1024_cert.pem new file mode 100644 index 00000000..37d5c5c7 --- /dev/null +++ b/Notation.Plugin.AzureKeyVault.Tests/TestData/rsa_1024_cert.pem @@ -0,0 +1,12 @@ +-----BEGIN CERTIFICATE----- +MIIBrjCCARcCFE9Q5YDM9LNazCxDmXG/kVjMiQHmMA0GCSqGSIb3DQEBCwUAMBYx +FDASBgNVBAMMC2V4YW1wbGUuY29tMB4XDTIzMDQyNTA2MjQ0M1oXDTI0MDQyNDA2 +MjQ0M1owFjEUMBIGA1UEAwwLZXhhbXBsZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQAD +gY0AMIGJAoGBALZIlBgO6uGf8kfNeRAP00l3NADcBRA7u7/YXVFHaY/4dK58Z4BK +qQsIKR/sUjB7nZku7YsomLtUgr76voRfXcnOpC6RRYFBKGPZ3RIWeN+bTyQwC8ll +nO4BgRoueKpIdzVTuGVff0LrRyGaGxjStvzq8Y6bypu5Is9+XdKejUJNAgMBAAEw +DQYJKoZIhvcNAQELBQADgYEAksA+W9aq4/8dDJgQjbilprXc5FFbw9YE4xveMIpm +cRvZWCoXaB8oYxzOylH3jsXoWCaWlvbmk57lcnSQm6VGy3W12VJ7EeA7TZiKB954 +DvZoGq1hKWzy9Mg1LkyjBbk444dcIA7zAprWmT3t+2Mz7hulSzHC3rpF2XE6oNf0 +KHM= +-----END CERTIFICATE----- diff --git a/Notation.Plugin.AzureKeyVault.Tests/TestData/rsa_2048_cert.pem b/Notation.Plugin.AzureKeyVault.Tests/TestData/rsa_2048_cert.pem new file mode 100644 index 00000000..2d92843e --- /dev/null +++ b/Notation.Plugin.AzureKeyVault.Tests/TestData/rsa_2048_cert.pem @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE----- +MIICszCCAZsCFHx5UpKORmNIshFZw4advD9qlwGcMA0GCSqGSIb3DQEBCwUAMBYx +FDASBgNVBAMMC2V4YW1wbGUuY29tMB4XDTIzMDQyNTA2MTgyNFoXDTI0MDQyNDA2 +MTgyNFowFjEUMBIGA1UEAwwLZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUA +A4IBDwAwggEKAoIBAQCs+wzGIKn0YbfB71X7h8k+3j41b8pNb9xWxTaSGAgUz6eO +MI1+W8KQEWcdIyeCjN5U7nZUHK87nPHtRewTc3fdzoGL1JntDULHajiPxC3rebKC +g7AzrxNgm70emujotcpQMKpMsVhIirvhCwN/LV8l30CXs2Yzn7Opygr0DNtDloyH +qCFpfTw1asdkewmEWFuzthPv6K1RgoE8FiVGp0YA4t30QNKXTFUd1LsA2SMSqbIo +rvbWXprULknlK3u9RXdxRBqm019Mz1SyrlJ+8iUwzgpq3lH3QDxp6D3at6qkkXVH +/LnD417+iNoVsXchx+GWQ+mpjHGFJpNNGaIGOUqzAgMBAAEwDQYJKoZIhvcNAQEL +BQADggEBAG9zXKdPztJb5CPak741L8Pzfz11U+hueXguJ+xL+IiIh6mY6XlZnIcR +8XwPDBJB7v8Zda7w6+oQjl5jpdtu0FrG1h+KUPytfq2tEzDqgN4ifZeoDrW8V1nM +uyRYDyxV71J5CySdVh7B9gjdX+m/2dxEFlOGvZM8q8t/UgRwOkiJ4HRk9bgGHCB/ +0qF2WWLjSdXLg6Jz4iG0Qoa2N7q8P2TJcwWBqDHN2QfBB9f0UWmsax+cscTDf/DJ +szaLZTe1viZgvnFY9nfheqnxHaKIEisoD+tM/fSC+IeyAc6Zm+vUKQ514srK1tY1 +IZfmSlsiGVWmzBwcWwo5GMuO7Ht35TQ= +-----END CERTIFICATE----- diff --git a/Notation.Plugin.AzureKeyVault.Tests/TestData/rsa_3072_cert.pem b/Notation.Plugin.AzureKeyVault.Tests/TestData/rsa_3072_cert.pem new file mode 100644 index 00000000..fb80ad21 --- /dev/null +++ b/Notation.Plugin.AzureKeyVault.Tests/TestData/rsa_3072_cert.pem @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDszCCAhsCFCxTt00Zi5S+8FN0+0NGeTwqLgvBMA0GCSqGSIb3DQEBCwUAMBYx +FDASBgNVBAMMC2V4YW1wbGUuY29tMB4XDTIzMDQyNTA2MTgyNFoXDTI0MDQyNDA2 +MTgyNFowFjEUMBIGA1UEAwwLZXhhbXBsZS5jb20wggGiMA0GCSqGSIb3DQEBAQUA +A4IBjwAwggGKAoIBgQCkQy6JoYzdKMgLKCKTYwY2b9TKYuO7B8wJr39WBQvzCdE3 +HC7jNyjgnJ4pHrIdF16w1JeD0c5w5XjyjMzuEM4LcP8sJ6OIIpmTi4EX8B/jtjA4 +D997adho3JcjhoSHrsG0ZaqxrgT1NJp2h3eurUeMig0aXEKPeRUpaej1F+GcW42p +j/raHM4vsbM8/G/Q1NXBcaLeWVbZOx3Bqb/hKXT0rUuPKwViCtcH8MhgSAIBkfBK +Sl+9OOeoPv5+CWZbtf+RsrfjFQhmhVlyeL05a7Fpdx7qrSwgICTmXJwLrOW9z/vU +VjrHgSYqWARolHembWl309EI52CnR7b4H/HIbfx8vq9SY3+JeNCgeQ12nQzJWi28 +JmWrgK0etQh5UpZJOF2bs48hOaE5Mz/5uOl29toUCsBBprroLTUhqwoBnvQMb6Qh +FfJlHPB9n6ekAWYcFON2T1tvSgDHHC8Z/qURgr3E86zXie9UIaXVQM+1yNHfmFYc +cbrdl+B3T6szTr8dvcUCAwEAATANBgkqhkiG9w0BAQsFAAOCAYEAideV2psiFniN ++lHoxma89AFkPZBPzWRs7p7l9NVRSjoDRs450btzxHMAC+mn05Z8684Yteq7t5xA +hNHxF7ceOQL1NVLx47SnrpqjMm3w3XlDcmgHQLYkk/BXQCl7D3nswbpDVZlFNOLt +2il36riFwtdE01H7j/6unuKsVGO8O1RtuAdMBDVfpNbdE3skcX8jmhd9+gbHGcPK +uzDfG6D09yHY86ZgkIxQTYAFt3Mz6QowfeWUtUJNbL+JI9mJReO7+0fXe1kk3b9K +BI2h6hX3buQgsMjgCGDoHw/blaEM5u8Doai05iZWbV4mpAljj88gOpAVb206f2EY +VBYJ9xbWOUvwiHdNODQoGTT7s6nPtlBWntUZ4FO4d0w59i9uda+jW3QXqdOWAHMd +VZQeAH3UXU5dwlDEPELwoMx8dCgY1OiZ691h+m+26Umro2TVPpO2oizKfh65QbMY +9/O3jWAg17mBNcr8eKr6zibhOFGmFbBT9AsqQ1fiqovZZoJiqza4 +-----END CERTIFICATE----- diff --git a/Notation.Plugin.AzureKeyVault.Tests/TestData/rsa_4096_cert.pem b/Notation.Plugin.AzureKeyVault.Tests/TestData/rsa_4096_cert.pem new file mode 100644 index 00000000..1b722a3d --- /dev/null +++ b/Notation.Plugin.AzureKeyVault.Tests/TestData/rsa_4096_cert.pem @@ -0,0 +1,28 @@ +-----BEGIN CERTIFICATE----- +MIIEszCCApsCFEvG2as+oG0NgWqerILcioDvXqiwMA0GCSqGSIb3DQEBCwUAMBYx +FDASBgNVBAMMC2V4YW1wbGUuY29tMB4XDTIzMDQyNTA2MTgyNVoXDTI0MDQyNDA2 +MTgyNVowFjEUMBIGA1UEAwwLZXhhbXBsZS5jb20wggIiMA0GCSqGSIb3DQEBAQUA +A4ICDwAwggIKAoICAQCosWrbv8hdFeGmEc6xmNDDiL2WThqYGxLAg7VaLSYdbvJo +OdKa6mRJmobjgKziTh6J7PICAOcywxdHxJxTo9yFkYDbYgtwLpCAeQ6Y/JRtBcPM +QTC22hbhX5FdFq/Dkosp+FqYyz/+kJpRHw/yjePo1d5piTscXcf5Jt7AFDZ9DRMa +oODv9nHVghNak+DnLjZhvv8Fi02o7FYvd4CI5DbT+m95IcQdvCE+BW/4o04waCbY +bT7g7ZqpWahrESnZVJQg5YPYA3UJ5tSkgFP2OlYIRu7e5A2feCI/vwEUzmUp79zy +E93+tFZxTOMP3oPIcPLv6KhdVvpkC6TUvwDhMsyqCQlCouu8sekNmnFY8sKxqGSj +qPbNuQ+z8fvns7RTp2/KsbCLQfP0lp4ut27UbiSdYngZRAzIRrQQtSK72o+lFk08 +QhfLJRZ/M8ONUvuDhqUAH7lghPKMUb6M+p2FS6hItI/dZKB/q5BIRqPIPPlbKn26 +k8W5nxjPuSTnkx67GI1+D5FSxghEInfFLDmYXKMLZTl8M1l2JPhooxgnwAPxZHiV +YQQuu5y7DZlh+6IAC8HUk8/gcekNbw/UjoPg5BOdeKqbpLYcBmAbOtoH2ebJ1rO2 +jlNqeCeMj828HtNQqn36jWE/qF+HRrfJyufg7WNo2tbumeA0hc6Lgw2oCrhKvQID +AQABMA0GCSqGSIb3DQEBCwUAA4ICAQCa3uxTr1SW5lIZ9kC5eRe3jlCF+PwwEasx +fWT3WM7lOTZE23q87vEmeXVplQu/yGhVKL259FCGCO2OXrIX6D5msfzVD0K9nXtv +VA5FeMOWsrk1aLtKRGm98Dytw9+ifRmP/0evH77AwyDUriqU5lm4aKj6JEEb3RaW +XR1rKPPLMMdbAg/2qXzQH5aZQJqQlReM9Exiw1xySFBWYJRm1X6u9bAVazIDR42k +TmbQ3iX1DmjUs59aISGpVzbRf6dhCGb0T3DZpSk/bPrMMPNKdnNIHt1qVXqQEPxi +7qm890tH0GkwjZNwHwNOe5r9oKVxoHHlUIWMhX0eEj/xmz2jIlfn7YPi9l0CcT0d +q/Z3q+B2Eu3H+daxL9xI/soanPwjpDiJvx8vPqc8Tny5mWnXX3bQKdCVAvtt0UPV +Y+QLVPVlVxEV855g6+2KlY8JMXtIi4QJTB1yXm/vW4VfJomjaeKPE4FIkre/BGSY +lAzOlpGGvd42vInzT0AqgljDkDyVTqPagT1JabLVc/W5UGRjpLYBwH8YqOi/t9wH +CiYxVzxJRrO5aK8Pd2RR6DGndY+qrMJ3g25XKuefS6d9dGqN1hwNbyLpCHkVEnnj +ABFU6q3D89M03w8FvoBRLhm/ltUUvnFQ6VOjTKn4cCVL27zsrY/PNBqxNoSWu6H2 +4hpTz+swnQ== +-----END CERTIFICATE----- diff --git a/Notation.Plugin.AzureKeyVault/Command/DescribeKey.cs b/Notation.Plugin.AzureKeyVault/Command/DescribeKey.cs index 0b7d4437..b60a0237 100644 --- a/Notation.Plugin.AzureKeyVault/Command/DescribeKey.cs +++ b/Notation.Plugin.AzureKeyVault/Command/DescribeKey.cs @@ -9,23 +9,41 @@ namespace Notation.Plugin.AzureKeyVault.Command /// public class DescribeKey : IPluginCommand { + private DescribeKeyRequest _request; + private IKeyVaultClient _keyVaultClient; private const string invalidInputError = "Invalid input. The valid input format is '{\"contractVersion\":\"1.0\",\"keyId\":\"https://.vault.azure.net///\"}'"; - public async Task RunAsync(string inputJson) + /// + /// Constructor to create DescribeKey object from JSON string. + /// + public DescribeKey(string inputJson) { // Deserialize JSON string to DescribeKeyRequest object - DescribeKeyRequest? request = JsonSerializer.Deserialize(inputJson); + var request = JsonSerializer.Deserialize(inputJson); if (request == null) { throw new ValidationException(invalidInputError); } + this._request = request; + this._keyVaultClient = new KeyVaultClient(request.KeyId); + } + /// + /// Constructor for unit test. + /// + public DescribeKey(DescribeKeyRequest request, IKeyVaultClient keyVaultClient) + { + this._request = request; + this._keyVaultClient = keyVaultClient; + } + + public async Task RunAsync() + { // Get certificate from Azure Key Vault - var akvClient = new KeyVaultClient(request.KeyId); - var cert = await akvClient.GetCertificateAsync(); + var cert = await _keyVaultClient.GetCertificateAsync(); return new DescribeKeyResponse( - keyId: request.KeyId, + keyId: _request.KeyId, keySpec: cert.KeySpec().EncodeKeySpec()); } } diff --git a/Notation.Plugin.AzureKeyVault/Command/GenerateSignature.cs b/Notation.Plugin.AzureKeyVault/Command/GenerateSignature.cs index 83896441..43cd4fcd 100644 --- a/Notation.Plugin.AzureKeyVault/Command/GenerateSignature.cs +++ b/Notation.Plugin.AzureKeyVault/Command/GenerateSignature.cs @@ -11,35 +11,53 @@ namespace Notation.Plugin.AzureKeyVault.Command /// public class GenerateSignature : IPluginCommand { - public async Task RunAsync(string inputJson) + private GenerateSignatureRequest _request; + private IKeyVaultClient _keyVaultClient; + + /// + /// Constructor to create GenerateSignature object from JSON string. + /// + public GenerateSignature(string inputJson) { // Parse the input - GenerateSignatureRequest? input = JsonSerializer.Deserialize(inputJson); - if (input == null) + var request = JsonSerializer.Deserialize(inputJson); + if (request == null) { throw new ValidationException("Invalid input"); } + this._request = request; + this._keyVaultClient = new KeyVaultClient(request.KeyId); + } - var akvClient = new KeyVaultClient(input.KeyId); + /// + /// Constructor for unit test. + /// + public GenerateSignature(GenerateSignatureRequest request, IKeyVaultClient keyVaultClient) + { + this._request = request; + this._keyVaultClient = keyVaultClient; + } + public async Task RunAsync() + { // Extract signature algorithm from the certificate - var leafCert = await akvClient.GetCertificateAsync(); + var leafCert = await _keyVaultClient.GetCertificateAsync(); var keySpec = leafCert.KeySpec(); var signatureAlgorithm = keySpec.ToSignatureAlgorithm(); // Sign - var signature = await akvClient.SignAsync(signatureAlgorithm, input.Payload); + var signature = await _keyVaultClient.SignAsync(signatureAlgorithm, _request.Payload); // Build the certificate chain List certificateChain = new List(); - if (input.PluginConfig?.ContainsKey("ca_certs") == true) + if (_request.PluginConfig?.ContainsKey("ca_certs") == true) { // Build the entire certificate chain from the certificate // bundle (including the intermediate and root certificates). - var caCertsPath = input.PluginConfig["ca_certs"]; + var caCertsPath = _request.PluginConfig["ca_certs"]; certificateChain = CertificateChain.Build(leafCert, CertificateBundle.Create(caCertsPath)); } - else if (input.PluginConfig?.ContainsKey("as_secret") == true) + else if (_request.PluginConfig?.ContainsKey("as_secret") == true) { // Read the entire certificate chain from the Azure Key Vault with GetSecret permission. throw new NotImplementedException("as_secret is not implemented yet"); @@ -51,7 +69,7 @@ public async Task RunAsync(string inputJson) } return new GenerateSignatureResponse( - keyId: input.KeyId, + keyId: _request.KeyId, signature: signature, signingAlgorithm: keySpec.ToSigningAlgorithm(), certificateChain: certificateChain); diff --git a/Notation.Plugin.AzureKeyVault/Command/GetPluginMetadata.cs b/Notation.Plugin.AzureKeyVault/Command/GetPluginMetadata.cs index 10433fab..6f76c1ec 100644 --- a/Notation.Plugin.AzureKeyVault/Command/GetPluginMetadata.cs +++ b/Notation.Plugin.AzureKeyVault/Command/GetPluginMetadata.cs @@ -8,7 +8,7 @@ namespace Notation.Plugin.AzureKeyVault.Command public class GetPluginMetadata : IPluginCommand { public const string Version = "1.0.0"; - public async Task RunAsync(string _) + public async Task RunAsync() { return await Task.FromResult(new GetMetadataResponse( name: "azure-kv", diff --git a/Notation.Plugin.AzureKeyVault/Command/IPluginCommand.cs b/Notation.Plugin.AzureKeyVault/Command/IPluginCommand.cs index 15c2baf4..93dc0c4e 100644 --- a/Notation.Plugin.AzureKeyVault/Command/IPluginCommand.cs +++ b/Notation.Plugin.AzureKeyVault/Command/IPluginCommand.cs @@ -4,6 +4,6 @@ namespace Notation.Plugin.AzureKeyVault.Command /// Interface for plugin commands. /// public interface IPluginCommand{ - Task RunAsync(string inputJson); + Task RunAsync(); } } \ No newline at end of file diff --git a/Notation.Plugin.AzureKeyVault/KeyVault/KeyVaultClient.cs b/Notation.Plugin.AzureKeyVault/KeyVault/KeyVaultClient.cs index b721466f..90b08eab 100644 --- a/Notation.Plugin.AzureKeyVault/KeyVault/KeyVaultClient.cs +++ b/Notation.Plugin.AzureKeyVault/KeyVault/KeyVaultClient.cs @@ -1,12 +1,27 @@ +using System.Runtime.CompilerServices; using System.Security.Cryptography.X509Certificates; using Azure.Identity; using Azure.Security.KeyVault.Certificates; using Azure.Security.KeyVault.Keys.Cryptography; using Notation.Plugin.Protocol; +[assembly: InternalsVisibleTo("Notation.Plugin.AzureKeyVault.Tests")] namespace Notation.Plugin.AzureKeyVault.Client { - class KeyVaultClient + public interface IKeyVaultClient + { + /// + /// Sign the payload with the specified algorithm. + /// + public Task SignAsync(SignatureAlgorithm algorithm, byte[] payload); + + /// + /// Get the certificate from KeyVault. + /// + public Task GetCertificateAsync(); + } + + public class KeyVaultClient: IKeyVaultClient { /// /// A helper record to store KeyVault metadata. @@ -14,18 +29,26 @@ class KeyVaultClient private record KeyVaultMetadata(string KeyVaultUrl, string Name, string Version); // Certificate client (lazy initialization) - private Lazy _certificateClient; + // Protected for unit test + protected Lazy _certificateClient; // Cryptography client (lazy initialization) - private Lazy _cryptoClient; + // Protected for unit test + protected Lazy _cryptoClient; + // Error message for invalid input private const string INVALID_INPUT_ERROR_MSG = "Invalid input. The valid input format is '{\"contractVersion\":\"1.0\",\"keyId\":\"https://.vault.azure.net///\"}'"; // Key name or certificate name - public string _name; + private string _name; // Key version or certificate version - public string _version; + private string _version; // Key identifier (e.g. https://.vault.azure.net/keys//) - public string _keyId; + private string _keyId; + + // Internal getters for unit test + internal string Name => _name; + internal string Version => _version; + internal string KeyId => _keyId; /// /// Constructor to create AzureKeyVault object from keyVaultUrl, name diff --git a/Notation.Plugin.AzureKeyVault/Program.cs b/Notation.Plugin.AzureKeyVault/Program.cs index e2a231e0..46ee93a2 100644 --- a/Notation.Plugin.AzureKeyVault/Program.cs +++ b/Notation.Plugin.AzureKeyVault/Program.cs @@ -30,6 +30,9 @@ static async Task ExecuteAsync(string[] args) throw new ValidationException("Missing command"); } + // read the input + var inputJson = PluginIO.ReadInput(); + IPluginCommand? cmd = null; switch (args[0]) { @@ -37,20 +40,17 @@ static async Task ExecuteAsync(string[] args) cmd = new GetPluginMetadata(); break; case "describe-key": - cmd = new DescribeKey(); + cmd = new DescribeKey(inputJson); break; case "generate-signature": - cmd = new GenerateSignature(); + cmd = new GenerateSignature(inputJson); break; default: throw new ValidationException($"Invalid command: {args[0]}"); } - // read the input - var inputJson = PluginIO.ReadInput(); - // execute the command - var resp = await cmd.RunAsync(inputJson); + var resp = await cmd.RunAsync(); // print the output PluginIO.WriteOutput(resp); diff --git a/Notation.Plugin.AzureKeyVault/Protocol/KeySpec.cs b/Notation.Plugin.AzureKeyVault/Protocol/KeySpec.cs index b993960f..a1a5f79c 100644 --- a/Notation.Plugin.AzureKeyVault/Protocol/KeySpec.cs +++ b/Notation.Plugin.AzureKeyVault/Protocol/KeySpec.cs @@ -1,6 +1,3 @@ -using System.Security.Cryptography; -using System.Security.Cryptography.X509Certificates; - namespace Notation.Plugin.Protocol { /// From 47d74db539d88c7c3d60c5fbcbf20774cb88237c Mon Sep 17 00:00:00 2001 From: Junjie Gao Date: Fri, 28 Apr 2023 13:33:53 +0800 Subject: [PATCH 07/17] fix: test Signed-off-by: Junjie Gao --- .../KeyVault/KeySpecExtensionTests.cs | 4 ++-- Notation.Plugin.AzureKeyVault/KeyVault/KeyVaultClient.cs | 4 ++-- .../Notation.Plugin.AzureKeyVault.csproj | 1 - 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/Notation.Plugin.AzureKeyVault.Tests/KeyVault/KeySpecExtensionTests.cs b/Notation.Plugin.AzureKeyVault.Tests/KeyVault/KeySpecExtensionTests.cs index db277692..9359e30a 100644 --- a/Notation.Plugin.AzureKeyVault.Tests/KeyVault/KeySpecExtensionTests.cs +++ b/Notation.Plugin.AzureKeyVault.Tests/KeyVault/KeySpecExtensionTests.cs @@ -18,7 +18,7 @@ public void ToSignatureAlgorithm_ValidKeySpecs_ReturnsCorrectSignatureAlgorithm( // Arrange var keySpec = new KeySpec(keyType, keySize); // Act - var signatureAlgorithm = keySpec.ToSignatureAlgorithm(); + var signatureAlgorithm = keySpec.ToKeyVaultSignatureAlgorithm(); // Assert Assert.Equal(expectedAlgorithm, signatureAlgorithm); @@ -33,7 +33,7 @@ public void ToSignatureAlgorithm_InvalidKeySpecs_ThrowsArgumentException(KeyType var keySpec = new KeySpec(keyType, keySize); // Act & Assert - Assert.Throws(() => keySpec.ToSignatureAlgorithm()); + Assert.Throws(() => keySpec.ToKeyVaultSignatureAlgorithm()); } } } \ No newline at end of file diff --git a/Notation.Plugin.AzureKeyVault/KeyVault/KeyVaultClient.cs b/Notation.Plugin.AzureKeyVault/KeyVault/KeyVaultClient.cs index 48970073..38dd2cf9 100644 --- a/Notation.Plugin.AzureKeyVault/KeyVault/KeyVaultClient.cs +++ b/Notation.Plugin.AzureKeyVault/KeyVault/KeyVaultClient.cs @@ -38,9 +38,9 @@ private record KeyVaultMetadata(string KeyVaultUrl, string Name, string Version) // Protected for unit test protected Lazy _certificateClient; // Cryptography client (lazy initialization) - private Lazy _cryptoClient; + protected Lazy _cryptoClient; // Secret client (lazy initialization) - private Lazy _secretClient; + protected Lazy _secretClient; // Error message for invalid input private const string INVALID_INPUT_ERROR_MSG = "Invalid input. The valid input format is '{\"contractVersion\":\"1.0\",\"keyId\":\"https://.vault.azure.net///\"}'"; diff --git a/Notation.Plugin.AzureKeyVault/Notation.Plugin.AzureKeyVault.csproj b/Notation.Plugin.AzureKeyVault/Notation.Plugin.AzureKeyVault.csproj index b0cad21e..64e3724d 100644 --- a/Notation.Plugin.AzureKeyVault/Notation.Plugin.AzureKeyVault.csproj +++ b/Notation.Plugin.AzureKeyVault/Notation.Plugin.AzureKeyVault.csproj @@ -4,7 +4,6 @@ Exe net6.0 Notation.Plugin.AzureKeyVault - linux-x64 enable enable notation-azure-kv From f2ade79c15989c5f32348b57aeebbdd6f1f2eaee Mon Sep 17 00:00:00 2001 From: Junjie Gao Date: Fri, 28 Apr 2023 16:45:31 +0800 Subject: [PATCH 08/17] fix: update test Signed-off-by: Junjie Gao --- .../Command/DescribeKeyTests.cs | 11 +++- .../KeyVault/KeyVaultClientTests.cs | 62 ++++++++++++++++++ ...Notation.Plugin.AzureKeyVault.Tests.csproj | 1 + .../Protocol/ErrorTests.cs | 1 + .../Protocol/PluginIOTests.cs | 5 ++ .../TestData/cert_chain.pem | 39 +++++++++++ .../TestData/cert_chain.pfx | Bin 0 -> 2146 bytes 7 files changed, 118 insertions(+), 1 deletion(-) create mode 100644 Notation.Plugin.AzureKeyVault.Tests/TestData/cert_chain.pem create mode 100644 Notation.Plugin.AzureKeyVault.Tests/TestData/cert_chain.pfx diff --git a/Notation.Plugin.AzureKeyVault.Tests/Command/DescribeKeyTests.cs b/Notation.Plugin.AzureKeyVault.Tests/Command/DescribeKeyTests.cs index 00f7f5a6..9c07e37d 100644 --- a/Notation.Plugin.AzureKeyVault.Tests/Command/DescribeKeyTests.cs +++ b/Notation.Plugin.AzureKeyVault.Tests/Command/DescribeKeyTests.cs @@ -3,7 +3,6 @@ using System.Threading.Tasks; using Moq; using Notation.Plugin.AzureKeyVault.Client; -using Notation.Plugin.AzureKeyVault.Command; using Notation.Plugin.Protocol; using Xunit; @@ -48,5 +47,15 @@ public void Constructor_ThrowsValidationException_WhenInvalidInput() // Act & Assert Assert.Throws(() => new DescribeKey(invalidInputJson)); } + + [Fact] + public void Constructor_Valid() + { + // Arrange + string invalidInputJson = "{\"contractVersion\":\"1.0\",\"keyId\":\"https://notationakvtest.vault.azure.net/keys/dotnetPluginCertPKCS12/3f06c6eeac0640ea9f93cd0bf69d2f17\"}"; + + // Act & Assert + Assert.Null(Record.Exception(() => new DescribeKey(invalidInputJson))); + } } } diff --git a/Notation.Plugin.AzureKeyVault.Tests/KeyVault/KeyVaultClientTests.cs b/Notation.Plugin.AzureKeyVault.Tests/KeyVault/KeyVaultClientTests.cs index d3273b8b..4b42a9e9 100644 --- a/Notation.Plugin.AzureKeyVault.Tests/KeyVault/KeyVaultClientTests.cs +++ b/Notation.Plugin.AzureKeyVault.Tests/KeyVault/KeyVaultClientTests.cs @@ -8,9 +8,11 @@ using Azure.Security.KeyVault.Certificates; using Azure.Security.KeyVault.Keys; using Azure.Security.KeyVault.Keys.Cryptography; +using Azure.Security.KeyVault.Secrets; using Moq; using Xunit; using Notation.Plugin.Protocol; +using System.Text; namespace Notation.Plugin.AzureKeyVault.Client.Tests { @@ -73,6 +75,12 @@ public TestableKeyVaultClient(string keyVaultUrl, string name, string version, C { this._certificateClient = new Lazy(() => certificateClient); } + + public TestableKeyVaultClient(string keyVaultUrl, string name, string version, SecretClient secretClient) + : base(keyVaultUrl, name, version) + { + this._secretClient = new Lazy(() => secretClient); + } } private TestableKeyVaultClient CreateMockedKeyVaultClient(SignResult signResult) @@ -93,6 +101,14 @@ private TestableKeyVaultClient CreateMockedKeyVaultClient(KeyVaultCertificate ce return new TestableKeyVaultClient("https://fake.vault.azure.net", "fake-certificate", "123", mockCertificateClient.Object); } + private TestableKeyVaultClient CreateMockedKeyVaultClient(KeyVaultSecret secret) + { + var mockSecretClient = new Mock(new Uri("https://fake.vault.azure.net/secrets/fake-secret/123"), new Mock().Object); + mockSecretClient.Setup(c => c.GetSecretAsync(It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(Response.FromValue(secret, new Mock().Object)); + return new TestableKeyVaultClient("https://fake.vault.azure.net", "fake-certificate", "123", mockSecretClient.Object); + } + [Fact] public async Task TestSignAsyncReturnsExpectedSignature() { @@ -175,5 +191,51 @@ public async Task GetCertificateAsyncThrowValidationException() await Assert.ThrowsAsync(async () => await keyVaultClient.GetCertificateAsync()); } + + [Fact] + public async Task GetCertificateChainAsync_PKCS12() + { + var certChainBytes = File.ReadAllBytes(Path.Combine(Directory.GetCurrentDirectory(), "TestData", "cert_chain.pfx")); + var testCertificateChain = new X509Certificate2Collection(); + testCertificateChain.Import(certChainBytes, "", X509KeyStorageFlags.Exportable); + var properties = SecretModelFactory.SecretProperties(); + properties.ContentType = "application/x-pkcs12"; + var secret = SecretModelFactory.KeyVaultSecret( + value: Convert.ToBase64String(certChainBytes), + properties: properties); + + var keyVaultClient = CreateMockedKeyVaultClient(secret); + + var certificateChain = await keyVaultClient.GetCertificateChainAsync(); + + Assert.NotNull(certificateChain); + Assert.IsType(certificateChain); + Assert.Equal(2, certificateChain.Count); + Assert.Equal(testCertificateChain[0].RawData, certificateChain[0].RawData); + Assert.Equal(testCertificateChain[1].RawData, certificateChain[1].RawData); + } + + [Fact] + public async Task GetCertificateChainAsync_PEM() + { + var certChainBytes = File.ReadAllBytes(Path.Combine(Directory.GetCurrentDirectory(), "TestData", "cert_chain.pem")); + var testCertificateChain = new X509Certificate2Collection(); + testCertificateChain.ImportFromPemFile(Path.Combine(Directory.GetCurrentDirectory(), "TestData", "cert_chain.pem")); + var properties = SecretModelFactory.SecretProperties(); + properties.ContentType = "application/x-pem-file"; + var secret = SecretModelFactory.KeyVaultSecret( + value: Encoding.UTF8.GetString(certChainBytes), + properties: properties); + + var keyVaultClient = CreateMockedKeyVaultClient(secret); + + var certificateChain = await keyVaultClient.GetCertificateChainAsync(); + + Assert.NotNull(certificateChain); + Assert.IsType(certificateChain); + Assert.Equal(2, certificateChain.Count); + Assert.Equal(testCertificateChain[0].RawData, certificateChain[0].RawData); + Assert.Equal(testCertificateChain[1].RawData, certificateChain[1].RawData); + } } } diff --git a/Notation.Plugin.AzureKeyVault.Tests/Notation.Plugin.AzureKeyVault.Tests.csproj b/Notation.Plugin.AzureKeyVault.Tests/Notation.Plugin.AzureKeyVault.Tests.csproj index d4bd997c..8ca4dbf0 100644 --- a/Notation.Plugin.AzureKeyVault.Tests/Notation.Plugin.AzureKeyVault.Tests.csproj +++ b/Notation.Plugin.AzureKeyVault.Tests/Notation.Plugin.AzureKeyVault.Tests.csproj @@ -21,6 +21,7 @@ all + diff --git a/Notation.Plugin.AzureKeyVault.Tests/Protocol/ErrorTests.cs b/Notation.Plugin.AzureKeyVault.Tests/Protocol/ErrorTests.cs index 95493e76..aa10dc8f 100644 --- a/Notation.Plugin.AzureKeyVault.Tests/Protocol/ErrorTests.cs +++ b/Notation.Plugin.AzureKeyVault.Tests/Protocol/ErrorTests.cs @@ -4,6 +4,7 @@ namespace Notation.Plugin.Protocol.Tests { + [Collection(nameof(OutputTestCollectionDefinition))] public class ErrorAndExceptionTests { [Fact] diff --git a/Notation.Plugin.AzureKeyVault.Tests/Protocol/PluginIOTests.cs b/Notation.Plugin.AzureKeyVault.Tests/Protocol/PluginIOTests.cs index 49a25c96..09ec1187 100644 --- a/Notation.Plugin.AzureKeyVault.Tests/Protocol/PluginIOTests.cs +++ b/Notation.Plugin.AzureKeyVault.Tests/Protocol/PluginIOTests.cs @@ -4,8 +4,13 @@ namespace Notation.Plugin.Protocol.Tests { + [CollectionDefinition(nameof(OutputTestCollectionDefinition), DisableParallelization = true)] + public class OutputTestCollectionDefinition { } + + [Collection(nameof(OutputTestCollectionDefinition))] public class PluginIOTests { + [Fact] public void ReadInput_ReturnsCorrectString() { diff --git a/Notation.Plugin.AzureKeyVault.Tests/TestData/cert_chain.pem b/Notation.Plugin.AzureKeyVault.Tests/TestData/cert_chain.pem new file mode 100644 index 00000000..835c311a --- /dev/null +++ b/Notation.Plugin.AzureKeyVault.Tests/TestData/cert_chain.pem @@ -0,0 +1,39 @@ +-----BEGIN CERTIFICATE----- +MIIDNzCCAh+gAwIBAgIBAjANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdUZXN0 +IENBMB4XDTIzMDQyMDA4MzMxMloXDTI0MDQxOTA4MzMxMlowQzERMA8GA1UEChMI +bm90YXRpb24xCzAJBgNVBAgTAldBMQswCQYDVQQGEwJVUzEUMBIGA1UEAxMLVGVz +dC1TaWduZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC/mNdlcO+7 +rJkbuGJJsfbyqLgqZMMuYZCeJOQ3BuaehFMUG9W9Orkp4nFGSeaeT7cUlgrNF1Xy +TrOw/s9YAzxCoaKyfMPlxZJHeCL4NTbatbyV+oY+dGFLTWivdgMLK3uUjk4B2Q2Q +n8nFs/PtSwOf6wksZKzcJQBh1u0bKlFVJXGhZBc6N+/wpFzoiVGfs/I7l2rURFC7 +k1Lp0J2RYn3p2ieqT0dUdQlokdBt8jEbxt/q4hA3UbJsq1KUPa/iirh0t+HztExx +GhDM+uOdf4RoFAF3g8ByFiakT3wdsfFs7dmXnNAqcxGT6VrKLnd2U8RiMJRTJruS +2SWQq4KNoB49AgMBAAGjZzBlMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggr +BgEFBQcDAzAdBgNVHQ4EFgQUvJnagTED9382FSntElG0NC+kWSIwHwYDVR0jBBgw +FoAU3IfJ0NXrAT/vsRf3exnjQNwrhFIwDQYJKoZIhvcNAQELBQADggEBAE4Ec+kN +JqbE7qbW4oKS6z/OUSaxDJYvP/1scSS7nOwGuaAfLdN9/e2PBwO3UlqlrR/L9gNr +GjXE3vWkN7Bxg40G9/oMovQmPePMDnrkGBSTAW/ibrYdWBsvL1FABtfbAqIyeOrM +nOXwdUjOLzC9WWYW+F02lFp6jMvgPxY9qdCXtn7bW3wQZc/d17dUxQbaL0WUgPiR +4O7LPomVyV3wLfkFzgmnaX5aDeqB4tEtd04W09cGT0x5jda3JsW2geJAO+b4ChKH +E1fT4YoaDquqoM6eBrj5hapUuASUUGOLZm2vxSf0IOYqN+gIBwUiJ7FHe7NkFwY9 +Z9M1kj4R4ufg0yI= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDFDCCAfygAwIBAgITXhoD9dTxCqlG0zTvTuTFwEJBkTANBgkqhkiG9w0BAQsF +ADASMRAwDgYDVQQDDAdUZXN0IENBMB4XDTIzMDQyMDA4MzMwNVoXDTI0MDQxOTA4 +MzMwNVowEjEQMA4GA1UEAwwHVGVzdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEP +ADCCAQoCggEBAJefbW2Ga3GDo4nvT+8tLxUZlPcXmlOfiP/K7XzNiRnBinEwjPvV +GcpuYPThgVO9jr8lB58bddYdDKgweso/5liAlNYUpzOvW1qPipeKTsYIZg7+P0Z6 +a+F5Srxywv8yQ5grMKPOVgeuBwgXyS+G2kBU4jR0ic7FJHWqEayGzbj8KLJFVfW8 +nZEx1SsQFH6sc6mtQZadJxAeSqyT7zeGFAvtn97yV/+m6deU7fqyEIhKh5dhyxp6 +7WD/kaNXdtQdUuPLJOzztnzrSW9F3GW2Gg7DFupmU8Sk2K0LQt28PvHIH58rXkIL +xaaa79zIGYJuwGy1cUZE55Yb3XsNaGEXELcCAwEAAaNjMGEwHQYDVR0OBBYEFNyH +ydDV6wE/77EX93sZ40DcK4RSMB8GA1UdIwQYMBaAFNyHydDV6wE/77EX93sZ40Dc +K4RSMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgIEMA0GCSqGSIb3DQEB +CwUAA4IBAQB5g5hJBj+KvyOYfvG/CViEiNrIZ4pY+r+Qhi4nnkZ6tc3j0ZZu7rKq +VIBBgJ/7BN3UTK1vwWpf74JnIiwUDypt0xIj4bx3muSDI47pMyKC0rLSyKqwle72 +AaF9mrX0t/WVHstOUbq+wgRork7KAo50Jvl264QVUxsajydXyKtt52SEqiL7IXsn +fIctPdwf55VqCAwnSyRtT7rjO1PIuyJ3t8FRm6+Mkze83zBsvyHZ6eBzqlUksliI +6XBpq1uzhotopuQtnqQ9d77SEEP8ZUueieV7E9j4nHzFtXe/zEkX7ZgrSPXgK/fI +sNnvnj4+FBiMN4N1ziCigrvHlyr6i4Y6 +-----END CERTIFICATE----- diff --git a/Notation.Plugin.AzureKeyVault.Tests/TestData/cert_chain.pfx b/Notation.Plugin.AzureKeyVault.Tests/TestData/cert_chain.pfx new file mode 100644 index 0000000000000000000000000000000000000000..dde27091e36dee3d92088c6ad5ad2b33f910dbaa GIT binary patch literal 2146 zcmV-o2%YyZf(Tv$0Ru3C2pR?nDuzgg_YDCD0ic2i3j~4)2QY#N12BRI{{{&vhDe6@ z4FLxRpn?bRFoFl{0s#Opf(PXW2`Yw2hW8Bt2LUh~1_~;MNQUz3~=!Nlb|cDu_j+a*~+j^eV! zewmcvQ=ui~ZuXhkpYsSQAG@ycL6DPwc*PV|RTz~vjw&6@f3gDK8?yTxP zgnu$nEKT)cPF!WGlma)dVcX@Jjn7Q}3yk)N*cjqE3f78(vB-1vKFkd*KCsZteiPw8 z{|xs$od?o8vmHAUscQaW;7P|BGp8DOAo6ZFzl(E~EE1N4^`>!>PsB3YhCtY>TR584 zLd4Myg366|Ck=7DeNw5T;-n~lWCUga<1#CDRypj>0TV=P=Acw<)hwcV=*x-MdhpdZ zy>rPnZ##wK&U0&#d_#h79s{aqzMys_2>OAg^g_}(=_7xI*zWnELOe2;`^u_X5V zN1Wo-sO_XFsgmcmIIGLavZfYWO{a%jlc{4YBIVvL_aRh zm!fX&pK|&e1l4QX1cvls{#~U2Luefb9)EQ#BUgtO>uhjDcx_il#W9d+`Ql4n0{#Mf zlbIP7>gJakIo)mSk(TY-hyg|JgVo#p*GeP6O6J|bbet1%=V{xDS z-`8^~h9vx@6z_QnuHj(H8y%6)RjUS)lCPa=yvtm{?-1d_tK!OK1*-|>soaUWD4nqh z0Lp)M))?n!7T={)e@T}TQ>rF%qF@HOK1S1@LB#wt$B~E*O!;33ar1=kG87P#T@SF? zqN$<1w^6{@d1=x4rZHO)?8V_*N?CW%V?7OZb}SD}KNb)uZRnYL#M^Z~*9HB51+s6zV4kvL zE4@9Cx>9c{`AGVj1mUL1QEZybCRbvYq+W{Yzczfg*dsz5S5P2g38=UvH;=9ZpyHQ45E(JTkHS5bGIKI zja{&m8*&!lj%}y+9=K2PveH%Qlh1+uByh>y?{&qe~}{0 z|D~Hsof-eGaP74h4Zb<0%d(wf!#IpVr3HFPZRB)$VOl&6GrtWaY7W0Vqf4m{;SiK;NN~_ie*U z`MeX9R9e=V!KN83uXsE-SJ#SRGs<5a*ZXgYB-B*>REa;`AaTZ6PXJgW9dk9z7GYMm z3&;3#u{{a*tp=%%@GYBYG>b%rB$5wP=i=b^S5kyH(4raE;Y-e3K6p>cpgwryII>-b ziTRfWxfsetGyLjZF@Df*FHiqQ>3`PoRY~ZZ>nok@8X`~l`J3ndq{Lx2jOo-Pe;-|l zz4Y8+T}aWUQO4=wQMg=#w}Bmtj$pQ!@a;BwCT{Q`5i-i;8;{Y+`)WFAr(zcLEPH(J zeQB-pIcCJJ{N-HOgd;x2;f8;7{Nu)H6TDKV=f+dl9^)c)91!q=AL!^FhOR~gwl4k# zOs)&bp+;b9rH+@YGbOd(EXs}dr;G_gJivXKDDC_{Ip(qVblqcVQ(Gvhhx(9+;onrg z>#lc5iw%zTYR+|v_G9T0Qn%=a=DmJ^)BbQG#m`t9l#v(0q{96sVSZLGuR!^i0@j6> zUYriNzuBNOG}Orku?axRX*$)^SwzeK-xPD5QaJB#eA&|X`QVWXl)x6-MaD|eAm;yD zhz@t3Iq{4>U21|mFgq|GFb4(&D-Ht!8Uz%@RQ*Agtr7-A5XJ&_&n>CpZ55-{h6w~}%} Date: Thu, 4 May 2023 10:53:11 +0800 Subject: [PATCH 09/17] fix: update code Signed-off-by: Junjie Gao --- .github/workflows/test.yml | 3 ++- Notation.Plugin.AzureKeyVault/Command/IPluginCommand.cs | 3 ++- Notation.Plugin.AzureKeyVault/KeyVault/KeyVaultClient.cs | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index deea3828..d8a9f122 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -30,8 +30,9 @@ jobs: env: VALIDATE_ALL_CODEBASE: false DEFAULT_BRANCH: main - DEFAULT_WORKSPACE: ./Notation.Plugin.AzureKeyVault + DEFAULT_WORKSPACE: Notation.Plugin.AzureKeyVault GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + FILTER_REGEX_EXCLUDE: Notation.Plugin.AzureKeyVault.Tests/.* build: name: "Build" runs-on: ubuntu-latest diff --git a/Notation.Plugin.AzureKeyVault/Command/IPluginCommand.cs b/Notation.Plugin.AzureKeyVault/Command/IPluginCommand.cs index 93dc0c4e..665b5e1e 100644 --- a/Notation.Plugin.AzureKeyVault/Command/IPluginCommand.cs +++ b/Notation.Plugin.AzureKeyVault/Command/IPluginCommand.cs @@ -3,7 +3,8 @@ namespace Notation.Plugin.AzureKeyVault.Command /// /// Interface for plugin commands. /// - public interface IPluginCommand{ + public interface IPluginCommand + { Task RunAsync(); } } \ No newline at end of file diff --git a/Notation.Plugin.AzureKeyVault/KeyVault/KeyVaultClient.cs b/Notation.Plugin.AzureKeyVault/KeyVault/KeyVaultClient.cs index 38dd2cf9..11821476 100644 --- a/Notation.Plugin.AzureKeyVault/KeyVault/KeyVaultClient.cs +++ b/Notation.Plugin.AzureKeyVault/KeyVault/KeyVaultClient.cs @@ -27,7 +27,7 @@ public interface IKeyVaultClient public Task GetCertificateChainAsync(); } - public class KeyVaultClient: IKeyVaultClient + public class KeyVaultClient : IKeyVaultClient { /// /// A helper record to store KeyVault metadata. From 66f82d1415fbef692823c0aaa1decac2bce1fd2f Mon Sep 17 00:00:00 2001 From: Junjie Gao Date: Thu, 4 May 2023 11:03:09 +0800 Subject: [PATCH 10/17] fix: update code Signed-off-by: Junjie Gao --- .github/workflows/test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d8a9f122..64ad5040 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -30,9 +30,9 @@ jobs: env: VALIDATE_ALL_CODEBASE: false DEFAULT_BRANCH: main - DEFAULT_WORKSPACE: Notation.Plugin.AzureKeyVault + DEFAULT_WORKSPACE: ./Notation.Plugin.AzureKeyVault GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - FILTER_REGEX_EXCLUDE: Notation.Plugin.AzureKeyVault.Tests/.* + FILTER_REGEX_EXCLUDE: .*Tests/.* build: name: "Build" runs-on: ubuntu-latest From f0699e32372ff70de58c2041fb9e5ee477e034d9 Mon Sep 17 00:00:00 2001 From: Junjie Gao Date: Thu, 4 May 2023 12:10:14 +0800 Subject: [PATCH 11/17] test: add unit test for Certificate Signed-off-by: Junjie Gao --- Makefile | 1 + .../Certificate/CertificateBundleTests.cs | 23 +++++++++ .../Certificate/CertificateChainTests.cs | 48 +++++++++++++++++++ .../KeyVault/KeyVaultClientTests.cs | 12 +++++ .../TestData/expired_leaf_cert.pem | 21 ++++++++ .../TestData/leaf_cert.pem | 20 ++++++++ .../TestData/root_cert.pem | 19 ++++++++ 7 files changed, 144 insertions(+) create mode 100644 Notation.Plugin.AzureKeyVault.Tests/Certificate/CertificateBundleTests.cs create mode 100644 Notation.Plugin.AzureKeyVault.Tests/Certificate/CertificateChainTests.cs create mode 100644 Notation.Plugin.AzureKeyVault.Tests/TestData/expired_leaf_cert.pem create mode 100644 Notation.Plugin.AzureKeyVault.Tests/TestData/leaf_cert.pem create mode 100644 Notation.Plugin.AzureKeyVault.Tests/TestData/root_cert.pem diff --git a/Makefile b/Makefile index 45117191..ad30167e 100644 --- a/Makefile +++ b/Makefile @@ -19,6 +19,7 @@ build: ## builds binaries .PHONY: test test: ## run unit test + rm -rf $(BUILD_DIR)/TestResults dotnet test $(TEST_PROJECT_DIR) --collect:"XPlat Code Coverage" --logger trx --results-directory $(BUILD_DIR)/TestResults .PHONY: install diff --git a/Notation.Plugin.AzureKeyVault.Tests/Certificate/CertificateBundleTests.cs b/Notation.Plugin.AzureKeyVault.Tests/Certificate/CertificateBundleTests.cs new file mode 100644 index 00000000..698efca8 --- /dev/null +++ b/Notation.Plugin.AzureKeyVault.Tests/Certificate/CertificateBundleTests.cs @@ -0,0 +1,23 @@ +using System.IO; +using System.Security.Cryptography.X509Certificates; +using Xunit; + +namespace Notation.Plugin.AzureKeyVault.Certificate.Tests +{ + public class CertificateBundleTests + { + [Fact] + public void Create_WithValidPemFile_ReturnsCertificates() + { + // Arrange + string pemFilePath = Path.Combine(Directory.GetCurrentDirectory(), "TestData", "rsa_2048_cert.pem"); + + // Act + X509Certificate2Collection certificates = CertificateBundle.Create(pemFilePath); + + // Assert + Assert.NotNull(certificates); + Assert.True(certificates.Count > 0); + } + } +} diff --git a/Notation.Plugin.AzureKeyVault.Tests/Certificate/CertificateChainTests.cs b/Notation.Plugin.AzureKeyVault.Tests/Certificate/CertificateChainTests.cs new file mode 100644 index 00000000..0c339c77 --- /dev/null +++ b/Notation.Plugin.AzureKeyVault.Tests/Certificate/CertificateChainTests.cs @@ -0,0 +1,48 @@ +using System.Collections.Generic; +using System.Security.Cryptography.X509Certificates; +using Xunit; +using Notation.Plugin.Protocol; +using System.IO; + +namespace Notation.Plugin.AzureKeyVault.Certificate.Tests +{ + public class CertificateChainTests + { + [Fact] + public void Build_WithValidLeafAndCertificateBundle_BuildsCertificateChain() + { + // Arrange + X509Certificate2 leafCert = new X509Certificate2(Path.Combine(Directory.GetCurrentDirectory(), "TestData", "leaf_cert.pem")); + X509Certificate2Collection certificateBundle = CertificateBundle.Create(Path.Combine(Directory.GetCurrentDirectory(), "TestData", "root_cert.pem")); + + // Act + List certificateChain = CertificateChain.Build(leafCert, certificateBundle); + + // Assert + Assert.NotNull(certificateChain); + Assert.True(certificateChain.Count > 0); + } + + [Fact] + public void Build_WithInvalidLeafCertificate_ThrowsValidationException() + { + // Arrange + X509Certificate2 expiredLeafCert = new X509Certificate2(Path.Combine(Directory.GetCurrentDirectory(), "TestData", "expired_leaf_cert.pem")); + X509Certificate2Collection certificateBundle = new X509Certificate2Collection(); + + // Act and Assert + Assert.Throws(() => CertificateChain.Build(expiredLeafCert, certificateBundle)); + } + + [Fact] + public void Build_WithIncompleteCertificateBundle_ThrowsValidationException() + { + // Arrange + X509Certificate2 invalidLeafCert = new X509Certificate2(Path.Combine(Directory.GetCurrentDirectory(), "TestData", "leaf_cert.pem")); + X509Certificate2Collection certificateBundle = new X509Certificate2Collection(); + + // Act and Assert + Assert.Throws(() => CertificateChain.Build(invalidLeafCert, certificateBundle)); + } + } +} diff --git a/Notation.Plugin.AzureKeyVault.Tests/KeyVault/KeyVaultClientTests.cs b/Notation.Plugin.AzureKeyVault.Tests/KeyVault/KeyVaultClientTests.cs index 4b42a9e9..a48a8de8 100644 --- a/Notation.Plugin.AzureKeyVault.Tests/KeyVault/KeyVaultClientTests.cs +++ b/Notation.Plugin.AzureKeyVault.Tests/KeyVault/KeyVaultClientTests.cs @@ -237,5 +237,17 @@ public async Task GetCertificateChainAsync_PEM() Assert.Equal(testCertificateChain[0].RawData, certificateChain[0].RawData); Assert.Equal(testCertificateChain[1].RawData, certificateChain[1].RawData); } + + [Fact] + public async Task GetCertificateChainAsync_UnknownFormat() + { + var properties = SecretModelFactory.SecretProperties(); + properties.ContentType = "application/x-unknown"; + var secret = SecretModelFactory.KeyVaultSecret( + properties: properties); + + var keyVaultClient = CreateMockedKeyVaultClient(secret); + await Assert.ThrowsAsync(async () => await keyVaultClient.GetCertificateChainAsync()); + } } } diff --git a/Notation.Plugin.AzureKeyVault.Tests/TestData/expired_leaf_cert.pem b/Notation.Plugin.AzureKeyVault.Tests/TestData/expired_leaf_cert.pem new file mode 100644 index 00000000..8549521a --- /dev/null +++ b/Notation.Plugin.AzureKeyVault.Tests/TestData/expired_leaf_cert.pem @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDbTCCAlWgAwIBAgIUXGf4izZdJlBDh7DAvRivUChVe88wDQYJKoZIhvcNAQEL +BQAwRjELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkRFMRUwEwYDVQQKDAxNeUNlcnQs +IEluYy4xEzARBgNVBAMMCm15Y2VydC5jb20wHhcNMjMwNDI3MDQwMjMxWhcNMjMw +NDI4MDQwMjMxWjBGMQswCQYDVQQGEwJVUzELMAkGA1UECAwCREUxFTATBgNVBAoM +DE15Q2VydCwgSW5jLjETMBEGA1UEAwwKbXljZXJ0LmNvbTCCASIwDQYJKoZIhvcN +AQEBBQADggEPADCCAQoCggEBANVzV/+tCDpcUhFFS06icTo7wUVzrc7BxA/jvE8V +F7aMSPEktROKzbSCLx0vHhu3Ol6e4gm1PshMXuxflU2x3z1UNGoQWmumg7PQR5zY +LIVG7j001wOsRW82hvL8p0FrEyT0OaXrUoj0u0EJB6UrrzBMrdhFWVeGMwdNeuYL +RXm6DdlOKLrKswW6tnfBmYi5wUGy4+D7B2MJbMz/2MZWoAj5NbxrfzAFbr6Wgz+m +zmZ8cpVNmRJsoJ5kUCYpXuzTjFrvKjlUtowTv+X1WD6DgdXqniqYdtxT5xIjQxeH +ggiMBS6GqAe17cUS7rQYxx+lK5IgOZrxGlCamY1/cWAH8h8CAwEAAaNTMFEwHQYD +VR0OBBYEFH62QasR15Wks0QYolynMFBeLeY1MB8GA1UdIwQYMBaAFH62QasR15Wk +s0QYolynMFBeLeY1MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEB +AA9Hyab+Zt1RAtym2fMWxjW7LRMNTD1IyakZPaEx3BUB8yyx2bGZ/xp63GJI4h+Z +5sarLVcq7pQNvVmzro0zzh7t2SZkuaVfAmt7v5PloByXalMtzQBieSCkjZgNLdjI +QOuG9VkzvTUfsChjgmDVG0gsUx8w2MgUyl5BmAqXB/cFRhXWUesDiXhmPHrm0Ctm +byANUWVQowuryPE7DJwASm6bwAIUQErXeQ6pLp+xkbIzX7Yw1/bth0MSSgNzB9Ka +czpsIeRP2geFwaOSdNZYjR+YqB+AdV9vXaR3qaehnguNZ7wWxtYDaK2OcxIOUUz8 +2n1N8dQBU+gGfGS1boPG0rE= +-----END CERTIFICATE----- diff --git a/Notation.Plugin.AzureKeyVault.Tests/TestData/leaf_cert.pem b/Notation.Plugin.AzureKeyVault.Tests/TestData/leaf_cert.pem new file mode 100644 index 00000000..f356176b --- /dev/null +++ b/Notation.Plugin.AzureKeyVault.Tests/TestData/leaf_cert.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDNzCCAh+gAwIBAgIBAjANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdUZXN0 +IENBMB4XDTIzMDQyMDA4MzMxMloXDTI0MDQxOTA4MzMxMlowQzERMA8GA1UEChMI +bm90YXRpb24xCzAJBgNVBAgTAldBMQswCQYDVQQGEwJVUzEUMBIGA1UEAxMLVGVz +dC1TaWduZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC/mNdlcO+7 +rJkbuGJJsfbyqLgqZMMuYZCeJOQ3BuaehFMUG9W9Orkp4nFGSeaeT7cUlgrNF1Xy +TrOw/s9YAzxCoaKyfMPlxZJHeCL4NTbatbyV+oY+dGFLTWivdgMLK3uUjk4B2Q2Q +n8nFs/PtSwOf6wksZKzcJQBh1u0bKlFVJXGhZBc6N+/wpFzoiVGfs/I7l2rURFC7 +k1Lp0J2RYn3p2ieqT0dUdQlokdBt8jEbxt/q4hA3UbJsq1KUPa/iirh0t+HztExx +GhDM+uOdf4RoFAF3g8ByFiakT3wdsfFs7dmXnNAqcxGT6VrKLnd2U8RiMJRTJruS +2SWQq4KNoB49AgMBAAGjZzBlMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggr +BgEFBQcDAzAdBgNVHQ4EFgQUvJnagTED9382FSntElG0NC+kWSIwHwYDVR0jBBgw +FoAU3IfJ0NXrAT/vsRf3exnjQNwrhFIwDQYJKoZIhvcNAQELBQADggEBAE4Ec+kN +JqbE7qbW4oKS6z/OUSaxDJYvP/1scSS7nOwGuaAfLdN9/e2PBwO3UlqlrR/L9gNr +GjXE3vWkN7Bxg40G9/oMovQmPePMDnrkGBSTAW/ibrYdWBsvL1FABtfbAqIyeOrM +nOXwdUjOLzC9WWYW+F02lFp6jMvgPxY9qdCXtn7bW3wQZc/d17dUxQbaL0WUgPiR +4O7LPomVyV3wLfkFzgmnaX5aDeqB4tEtd04W09cGT0x5jda3JsW2geJAO+b4ChKH +E1fT4YoaDquqoM6eBrj5hapUuASUUGOLZm2vxSf0IOYqN+gIBwUiJ7FHe7NkFwY9 +Z9M1kj4R4ufg0yI= +-----END CERTIFICATE----- diff --git a/Notation.Plugin.AzureKeyVault.Tests/TestData/root_cert.pem b/Notation.Plugin.AzureKeyVault.Tests/TestData/root_cert.pem new file mode 100644 index 00000000..195528a9 --- /dev/null +++ b/Notation.Plugin.AzureKeyVault.Tests/TestData/root_cert.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDFDCCAfygAwIBAgITXhoD9dTxCqlG0zTvTuTFwEJBkTANBgkqhkiG9w0BAQsF +ADASMRAwDgYDVQQDDAdUZXN0IENBMB4XDTIzMDQyMDA4MzMwNVoXDTI0MDQxOTA4 +MzMwNVowEjEQMA4GA1UEAwwHVGVzdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEP +ADCCAQoCggEBAJefbW2Ga3GDo4nvT+8tLxUZlPcXmlOfiP/K7XzNiRnBinEwjPvV +GcpuYPThgVO9jr8lB58bddYdDKgweso/5liAlNYUpzOvW1qPipeKTsYIZg7+P0Z6 +a+F5Srxywv8yQ5grMKPOVgeuBwgXyS+G2kBU4jR0ic7FJHWqEayGzbj8KLJFVfW8 +nZEx1SsQFH6sc6mtQZadJxAeSqyT7zeGFAvtn97yV/+m6deU7fqyEIhKh5dhyxp6 +7WD/kaNXdtQdUuPLJOzztnzrSW9F3GW2Gg7DFupmU8Sk2K0LQt28PvHIH58rXkIL +xaaa79zIGYJuwGy1cUZE55Yb3XsNaGEXELcCAwEAAaNjMGEwHQYDVR0OBBYEFNyH +ydDV6wE/77EX93sZ40DcK4RSMB8GA1UdIwQYMBaAFNyHydDV6wE/77EX93sZ40Dc +K4RSMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgIEMA0GCSqGSIb3DQEB +CwUAA4IBAQB5g5hJBj+KvyOYfvG/CViEiNrIZ4pY+r+Qhi4nnkZ6tc3j0ZZu7rKq +VIBBgJ/7BN3UTK1vwWpf74JnIiwUDypt0xIj4bx3muSDI47pMyKC0rLSyKqwle72 +AaF9mrX0t/WVHstOUbq+wgRork7KAo50Jvl264QVUxsajydXyKtt52SEqiL7IXsn +fIctPdwf55VqCAwnSyRtT7rjO1PIuyJ3t8FRm6+Mkze83zBsvyHZ6eBzqlUksliI +6XBpq1uzhotopuQtnqQ9d77SEEP8ZUueieV7E9j4nHzFtXe/zEkX7ZgrSPXgK/fI +sNnvnj4+FBiMN4N1ziCigrvHlyr6i4Y6 +-----END CERTIFICATE----- From 3c54c9094b34b005aabcbaf1ca7a5e95c91a96ba Mon Sep 17 00:00:00 2001 From: Junjie Gao Date: Thu, 4 May 2023 14:10:44 +0800 Subject: [PATCH 12/17] test: added unit test Signed-off-by: Junjie Gao --- .../Command/DescribeKeyTests.cs | 4 +- .../Command/GenerateSignatureTests.cs | 171 ++++++++++++++++++ 2 files changed, 173 insertions(+), 2 deletions(-) create mode 100644 Notation.Plugin.AzureKeyVault.Tests/Command/GenerateSignatureTests.cs diff --git a/Notation.Plugin.AzureKeyVault.Tests/Command/DescribeKeyTests.cs b/Notation.Plugin.AzureKeyVault.Tests/Command/DescribeKeyTests.cs index 9c07e37d..b634e064 100644 --- a/Notation.Plugin.AzureKeyVault.Tests/Command/DescribeKeyTests.cs +++ b/Notation.Plugin.AzureKeyVault.Tests/Command/DescribeKeyTests.cs @@ -52,10 +52,10 @@ public void Constructor_ThrowsValidationException_WhenInvalidInput() public void Constructor_Valid() { // Arrange - string invalidInputJson = "{\"contractVersion\":\"1.0\",\"keyId\":\"https://notationakvtest.vault.azure.net/keys/dotnetPluginCertPKCS12/3f06c6eeac0640ea9f93cd0bf69d2f17\"}"; + string validInputJson = "{\"contractVersion\":\"1.0\",\"keyId\":\"https://notationakvtest.vault.azure.net/keys/dotnetPluginCertPKCS12/3f06c6eeac0640ea9f93cd0bf69d2f17\"}"; // Act & Assert - Assert.Null(Record.Exception(() => new DescribeKey(invalidInputJson))); + Assert.Null(Record.Exception(() => new DescribeKey(validInputJson))); } } } diff --git a/Notation.Plugin.AzureKeyVault.Tests/Command/GenerateSignatureTests.cs b/Notation.Plugin.AzureKeyVault.Tests/Command/GenerateSignatureTests.cs new file mode 100644 index 00000000..e9d76bd8 --- /dev/null +++ b/Notation.Plugin.AzureKeyVault.Tests/Command/GenerateSignatureTests.cs @@ -0,0 +1,171 @@ +using System.Collections.Generic; +using System.IO; +using System.Security.Cryptography.X509Certificates; +using System.Text; +using System.Threading.Tasks; +using Azure.Security.KeyVault.Keys.Cryptography; +using Moq; +using Notation.Plugin.AzureKeyVault.Certificate; +using Notation.Plugin.AzureKeyVault.Client; +using Notation.Plugin.Protocol; +using Xunit; + +namespace Notation.Plugin.AzureKeyVault.Command.Tests +{ + public class GenerateSignatureTests + { + [Fact] + public async Task RunAsync_SelfSigned_ReturnsValidGenerateSignatureResponseAsync() + { + // Arrange + var keyId = "https://testvault.vault.azure.net/keys/testkey/123"; + var expectedKeySpec = "RSA-2048"; + var mockCert = new X509Certificate2(Path.Combine(Directory.GetCurrentDirectory(), "TestData", "rsa_2048_cert.pem")); + var mockSignature = new byte[] { 0x01, 0x02, 0x03, 0x04 }; + + var mockKeyVaultClient = new Mock(); + // mock GetCertificateAsync + mockKeyVaultClient.Setup(client => client.GetCertificateAsync()) + .ReturnsAsync(mockCert); + + // mock SignAsync + mockKeyVaultClient.Setup(client => client.SignAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(mockSignature); + + var request = new GenerateSignatureRequest( + contractVersion: "1.0", + keyId: keyId, + pluginConfig: null, + keySpec: expectedKeySpec, + hashAlgorithm: "SHA-256", + payload: Encoding.UTF8.GetBytes("Cg==")); + + var generateSignatureCommand = new GenerateSignature(request, mockKeyVaultClient.Object); + + var result = await generateSignatureCommand.RunAsync(); + + Assert.IsType(result); + var response = result as GenerateSignatureResponse; + if (response == null) + { + throw new System.Exception("response is null"); + } + Assert.Equal(keyId, response.KeyId); + Assert.Equal("RSASSA-PSS-SHA-256", response.SigningAlgorithm); + Assert.Equal(mockSignature, response.Signature); + Assert.Equal(1, response.CertificateChain.Count); + Assert.Equal(mockCert.RawData, response.CertificateChain[0]); + } + + [Fact] + public async Task RunAsync_ca_certs_ReturnsValidGenerateSignatureResponseAsync() + { + // Arrange + var keyId = "https://testvault.vault.azure.net/keys/testkey/123"; + var expectedKeySpec = "RSA-2048"; + var testRootCert = new X509Certificate2(Path.Combine(Directory.GetCurrentDirectory(), "TestData", "root_cert.pem")); + var mockCert = new X509Certificate2(Path.Combine(Directory.GetCurrentDirectory(), "TestData", "leaf_cert.pem")); + var mockSignature = new byte[] { 0x01, 0x02, 0x03, 0x04 }; + + var mockKeyVaultClient = new Mock(); + // mock GetCertificateAsync + mockKeyVaultClient.Setup(client => client.GetCertificateAsync()) + .ReturnsAsync(mockCert); + + // mock SignAsync + mockKeyVaultClient.Setup(client => client.SignAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(mockSignature); + + var request = new GenerateSignatureRequest( + contractVersion: "1.0", + keyId: keyId, + pluginConfig: new Dictionary() + { + ["ca_certs"] = Path.Combine(Directory.GetCurrentDirectory(), "TestData", "root_cert.pem") + }, + keySpec: expectedKeySpec, + hashAlgorithm: "SHA-256", + payload: Encoding.UTF8.GetBytes("Cg==")); + + var generateSignatureCommand = new GenerateSignature(request, mockKeyVaultClient.Object); + + var result = await generateSignatureCommand.RunAsync(); + + Assert.IsType(result); + var response = result as GenerateSignatureResponse; + if (response == null) + { + throw new System.Exception("response is null"); + } + Assert.Equal(keyId, response.KeyId); + Assert.Equal("RSASSA-PSS-SHA-256", response.SigningAlgorithm); + Assert.Equal(mockSignature, response.Signature); + Assert.Equal(2, response.CertificateChain.Count); + Assert.Equal(mockCert.RawData, response.CertificateChain[0]); + Assert.Equal(testRootCert.RawData, response.CertificateChain[1]); + } + + [Fact] + public async Task RunAsync_as_secret_ReturnsValidGenerateSignatureResponseAsync() + { + // Arrange + var keyId = "https://testvault.vault.azure.net/keys/testkey/123"; + var expectedKeySpec = "RSA-2048"; + var mockSignature = new byte[] { 0x01, 0x02, 0x03, 0x04 }; + var mockCertChain = CertificateBundle.Create(Path.Combine(Directory.GetCurrentDirectory(), "TestData", "cert_chain.pem")); + + var mockKeyVaultClient = new Mock(); + // mock GetCertificateAsync + mockKeyVaultClient.Setup(client => client.GetCertificateChainAsync()) + .ReturnsAsync(mockCertChain); + + // mock SignAsync + mockKeyVaultClient.Setup(client => client.SignAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(mockSignature); + + var request = new GenerateSignatureRequest( + contractVersion: "1.0", + keyId: keyId, + pluginConfig: new Dictionary() + { + ["as_secret"] = "true" + }, + keySpec: expectedKeySpec, + hashAlgorithm: "SHA-256", + payload: Encoding.UTF8.GetBytes("Cg==")); + + var generateSignatureCommand = new GenerateSignature(request, mockKeyVaultClient.Object); + + var result = await generateSignatureCommand.RunAsync(); + + Assert.IsType(result); + var response = result as GenerateSignatureResponse; + if (response == null) + { + throw new System.Exception("response is null"); + } + Assert.Equal(keyId, response.KeyId); + Assert.Equal("RSASSA-PSS-SHA-256", response.SigningAlgorithm); + Assert.Equal(mockSignature, response.Signature); + Assert.Equal(2, response.CertificateChain.Count); + Assert.Equal(mockCertChain[0].RawData, response.CertificateChain[0]); + Assert.Equal(mockCertChain[1].RawData, response.CertificateChain[1]); + } + + [Fact] + public void Constructor_Valid() + { + string validInputJson = "{\"contractVersion\":\"1.0\",\"keyId\":\"https://notationakvtest.vault.azure.net/keys/dotnetPluginCert/b6046b30d069458886de94b0ac9ed121\",\"keySpec\":\"RSA-2048\",\"hashAlgorithm\":\"SHA-256\",\"payload\":\"Cg==\"}"; + + Assert.Null(Record.Exception(() => new GenerateSignature(validInputJson))); + } + + [Fact] + public void Constructor_Invalid() + { + string InvalidInputJson = "null"; + + Assert.Throws(() => new GenerateSignature(InvalidInputJson)); + } + } +} \ No newline at end of file From 9b54227997d69adecd66a084c911777828cdb6f2 Mon Sep 17 00:00:00 2001 From: Junjie Gao Date: Thu, 4 May 2023 15:14:01 +0800 Subject: [PATCH 13/17] fix: update certificates Signed-off-by: Junjie Gao --- .github/.codecov.yml | 1 - .../Certificate/CertificateBundleTests.cs | 2 +- .../Certificate/CertificateChainTests.cs | 8 +- .../Command/DescribeKeyTests.cs | 2 +- .../Command/GenerateSignatureTests.cs | 10 +-- .../KeyVault/KeyVaultClientTests.cs | 4 +- ...Notation.Plugin.AzureKeyVault.Tests.csproj | 1 + .../Protocol/CertificateExtensionTests.cs | 2 +- .../TestData/cert_chain.pem | 70 +++++++++--------- .../TestData/cert_chain.pfx | Bin 2146 -> 2154 bytes .../TestData/dsa_2048.crt | 28 +++++++ .../TestData/dsa_2048_cert.pem | 24 ------ .../TestData/ec_163.crt | 12 +++ .../TestData/ec_163_cert.pem | 8 -- .../TestData/ec_256.crt | 13 ++++ .../TestData/ec_256_cert.pem | 9 --- .../TestData/ec_384.crt | 14 ++++ .../TestData/ec_384_cert.pem | 10 --- .../TestData/ec_521.crt | 15 ++++ .../TestData/ec_521_cert.pem | 12 --- ...expired_leaf_cert.pem => expired_leaf.crt} | 0 .../TestData/leaf.crt | 20 +++++ .../TestData/leaf_cert.pem | 20 ----- .../TestData/root.crt | 19 +++++ .../TestData/root_cert.pem | 19 ----- .../TestData/rsa_1024.crt | 15 ++++ .../TestData/rsa_1024_cert.pem | 12 --- .../TestData/rsa_2048.crt | 21 ++++++ .../TestData/rsa_2048_cert.pem | 17 ----- .../TestData/rsa_3072.crt | 26 +++++++ .../TestData/rsa_3072_cert.pem | 22 ------ .../TestData/rsa_4096.crt | 32 ++++++++ .../TestData/rsa_4096_cert.pem | 28 ------- 33 files changed, 265 insertions(+), 231 deletions(-) create mode 100644 Notation.Plugin.AzureKeyVault.Tests/TestData/dsa_2048.crt delete mode 100644 Notation.Plugin.AzureKeyVault.Tests/TestData/dsa_2048_cert.pem create mode 100644 Notation.Plugin.AzureKeyVault.Tests/TestData/ec_163.crt delete mode 100644 Notation.Plugin.AzureKeyVault.Tests/TestData/ec_163_cert.pem create mode 100644 Notation.Plugin.AzureKeyVault.Tests/TestData/ec_256.crt delete mode 100644 Notation.Plugin.AzureKeyVault.Tests/TestData/ec_256_cert.pem create mode 100644 Notation.Plugin.AzureKeyVault.Tests/TestData/ec_384.crt delete mode 100644 Notation.Plugin.AzureKeyVault.Tests/TestData/ec_384_cert.pem create mode 100644 Notation.Plugin.AzureKeyVault.Tests/TestData/ec_521.crt delete mode 100644 Notation.Plugin.AzureKeyVault.Tests/TestData/ec_521_cert.pem rename Notation.Plugin.AzureKeyVault.Tests/TestData/{expired_leaf_cert.pem => expired_leaf.crt} (100%) create mode 100644 Notation.Plugin.AzureKeyVault.Tests/TestData/leaf.crt delete mode 100644 Notation.Plugin.AzureKeyVault.Tests/TestData/leaf_cert.pem create mode 100644 Notation.Plugin.AzureKeyVault.Tests/TestData/root.crt delete mode 100644 Notation.Plugin.AzureKeyVault.Tests/TestData/root_cert.pem create mode 100644 Notation.Plugin.AzureKeyVault.Tests/TestData/rsa_1024.crt delete mode 100644 Notation.Plugin.AzureKeyVault.Tests/TestData/rsa_1024_cert.pem create mode 100644 Notation.Plugin.AzureKeyVault.Tests/TestData/rsa_2048.crt delete mode 100644 Notation.Plugin.AzureKeyVault.Tests/TestData/rsa_2048_cert.pem create mode 100644 Notation.Plugin.AzureKeyVault.Tests/TestData/rsa_3072.crt delete mode 100644 Notation.Plugin.AzureKeyVault.Tests/TestData/rsa_3072_cert.pem create mode 100644 Notation.Plugin.AzureKeyVault.Tests/TestData/rsa_4096.crt delete mode 100644 Notation.Plugin.AzureKeyVault.Tests/TestData/rsa_4096_cert.pem diff --git a/.github/.codecov.yml b/.github/.codecov.yml index a7fbfa76..e5761182 100644 --- a/.github/.codecov.yml +++ b/.github/.codecov.yml @@ -1,6 +1,5 @@ --- coverage: - require_ci_to_pass: false status: project: default: diff --git a/Notation.Plugin.AzureKeyVault.Tests/Certificate/CertificateBundleTests.cs b/Notation.Plugin.AzureKeyVault.Tests/Certificate/CertificateBundleTests.cs index 698efca8..d7605e90 100644 --- a/Notation.Plugin.AzureKeyVault.Tests/Certificate/CertificateBundleTests.cs +++ b/Notation.Plugin.AzureKeyVault.Tests/Certificate/CertificateBundleTests.cs @@ -10,7 +10,7 @@ public class CertificateBundleTests public void Create_WithValidPemFile_ReturnsCertificates() { // Arrange - string pemFilePath = Path.Combine(Directory.GetCurrentDirectory(), "TestData", "rsa_2048_cert.pem"); + string pemFilePath = Path.Combine(Directory.GetCurrentDirectory(), "TestData", "rsa_2048.crt"); // Act X509Certificate2Collection certificates = CertificateBundle.Create(pemFilePath); diff --git a/Notation.Plugin.AzureKeyVault.Tests/Certificate/CertificateChainTests.cs b/Notation.Plugin.AzureKeyVault.Tests/Certificate/CertificateChainTests.cs index 0c339c77..6dae71c9 100644 --- a/Notation.Plugin.AzureKeyVault.Tests/Certificate/CertificateChainTests.cs +++ b/Notation.Plugin.AzureKeyVault.Tests/Certificate/CertificateChainTests.cs @@ -12,8 +12,8 @@ public class CertificateChainTests public void Build_WithValidLeafAndCertificateBundle_BuildsCertificateChain() { // Arrange - X509Certificate2 leafCert = new X509Certificate2(Path.Combine(Directory.GetCurrentDirectory(), "TestData", "leaf_cert.pem")); - X509Certificate2Collection certificateBundle = CertificateBundle.Create(Path.Combine(Directory.GetCurrentDirectory(), "TestData", "root_cert.pem")); + X509Certificate2 leafCert = new X509Certificate2(Path.Combine(Directory.GetCurrentDirectory(), "TestData", "leaf.crt")); + X509Certificate2Collection certificateBundle = CertificateBundle.Create(Path.Combine(Directory.GetCurrentDirectory(), "TestData", "root.crt")); // Act List certificateChain = CertificateChain.Build(leafCert, certificateBundle); @@ -27,7 +27,7 @@ public void Build_WithValidLeafAndCertificateBundle_BuildsCertificateChain() public void Build_WithInvalidLeafCertificate_ThrowsValidationException() { // Arrange - X509Certificate2 expiredLeafCert = new X509Certificate2(Path.Combine(Directory.GetCurrentDirectory(), "TestData", "expired_leaf_cert.pem")); + X509Certificate2 expiredLeafCert = new X509Certificate2(Path.Combine(Directory.GetCurrentDirectory(), "TestData", "expired_leaf.crt")); X509Certificate2Collection certificateBundle = new X509Certificate2Collection(); // Act and Assert @@ -38,7 +38,7 @@ public void Build_WithInvalidLeafCertificate_ThrowsValidationException() public void Build_WithIncompleteCertificateBundle_ThrowsValidationException() { // Arrange - X509Certificate2 invalidLeafCert = new X509Certificate2(Path.Combine(Directory.GetCurrentDirectory(), "TestData", "leaf_cert.pem")); + X509Certificate2 invalidLeafCert = new X509Certificate2(Path.Combine(Directory.GetCurrentDirectory(), "TestData", "leaf.crt")); X509Certificate2Collection certificateBundle = new X509Certificate2Collection(); // Act and Assert diff --git a/Notation.Plugin.AzureKeyVault.Tests/Command/DescribeKeyTests.cs b/Notation.Plugin.AzureKeyVault.Tests/Command/DescribeKeyTests.cs index b634e064..ebc45ea4 100644 --- a/Notation.Plugin.AzureKeyVault.Tests/Command/DescribeKeyTests.cs +++ b/Notation.Plugin.AzureKeyVault.Tests/Command/DescribeKeyTests.cs @@ -16,7 +16,7 @@ public async Task RunAsync_ReturnsValidDescribeKeyResponseAsync() // Arrange var keyId = "https://testvault.vault.azure.net/keys/testkey/123"; var expectedKeySpec = "RSA-2048"; - var mockCert = new X509Certificate2(Path.Combine(Directory.GetCurrentDirectory(), "TestData", "rsa_2048_cert.pem")); + var mockCert = new X509Certificate2(Path.Combine(Directory.GetCurrentDirectory(), "TestData", "rsa_2048.crt")); var mockKeyVaultClient = new Mock(); mockKeyVaultClient.Setup(client => client.GetCertificateAsync()).ReturnsAsync(mockCert); diff --git a/Notation.Plugin.AzureKeyVault.Tests/Command/GenerateSignatureTests.cs b/Notation.Plugin.AzureKeyVault.Tests/Command/GenerateSignatureTests.cs index e9d76bd8..7619a414 100644 --- a/Notation.Plugin.AzureKeyVault.Tests/Command/GenerateSignatureTests.cs +++ b/Notation.Plugin.AzureKeyVault.Tests/Command/GenerateSignatureTests.cs @@ -20,7 +20,7 @@ public async Task RunAsync_SelfSigned_ReturnsValidGenerateSignatureResponseAsync // Arrange var keyId = "https://testvault.vault.azure.net/keys/testkey/123"; var expectedKeySpec = "RSA-2048"; - var mockCert = new X509Certificate2(Path.Combine(Directory.GetCurrentDirectory(), "TestData", "rsa_2048_cert.pem")); + var mockCert = new X509Certificate2(Path.Combine(Directory.GetCurrentDirectory(), "TestData", "rsa_2048.crt")); var mockSignature = new byte[] { 0x01, 0x02, 0x03, 0x04 }; var mockKeyVaultClient = new Mock(); @@ -53,7 +53,7 @@ public async Task RunAsync_SelfSigned_ReturnsValidGenerateSignatureResponseAsync Assert.Equal(keyId, response.KeyId); Assert.Equal("RSASSA-PSS-SHA-256", response.SigningAlgorithm); Assert.Equal(mockSignature, response.Signature); - Assert.Equal(1, response.CertificateChain.Count); + Assert.Single(response.CertificateChain); Assert.Equal(mockCert.RawData, response.CertificateChain[0]); } @@ -63,8 +63,8 @@ public async Task RunAsync_ca_certs_ReturnsValidGenerateSignatureResponseAsync() // Arrange var keyId = "https://testvault.vault.azure.net/keys/testkey/123"; var expectedKeySpec = "RSA-2048"; - var testRootCert = new X509Certificate2(Path.Combine(Directory.GetCurrentDirectory(), "TestData", "root_cert.pem")); - var mockCert = new X509Certificate2(Path.Combine(Directory.GetCurrentDirectory(), "TestData", "leaf_cert.pem")); + var testRootCert = new X509Certificate2(Path.Combine(Directory.GetCurrentDirectory(), "TestData", "root.crt")); + var mockCert = new X509Certificate2(Path.Combine(Directory.GetCurrentDirectory(), "TestData", "leaf.crt")); var mockSignature = new byte[] { 0x01, 0x02, 0x03, 0x04 }; var mockKeyVaultClient = new Mock(); @@ -81,7 +81,7 @@ public async Task RunAsync_ca_certs_ReturnsValidGenerateSignatureResponseAsync() keyId: keyId, pluginConfig: new Dictionary() { - ["ca_certs"] = Path.Combine(Directory.GetCurrentDirectory(), "TestData", "root_cert.pem") + ["ca_certs"] = Path.Combine(Directory.GetCurrentDirectory(), "TestData", "root.crt") }, keySpec: expectedKeySpec, hashAlgorithm: "SHA-256", diff --git a/Notation.Plugin.AzureKeyVault.Tests/KeyVault/KeyVaultClientTests.cs b/Notation.Plugin.AzureKeyVault.Tests/KeyVault/KeyVaultClientTests.cs index a48a8de8..a7a27c94 100644 --- a/Notation.Plugin.AzureKeyVault.Tests/KeyVault/KeyVaultClientTests.cs +++ b/Notation.Plugin.AzureKeyVault.Tests/KeyVault/KeyVaultClientTests.cs @@ -155,7 +155,7 @@ public async Task TestSignAsyncThrowsExceptionOnInvalidAlgorithm() [Fact] public async Task GetCertificateAsync_ReturnsCertificate() { - var testCertificate = new X509Certificate2(Path.Combine(Directory.GetCurrentDirectory(), "TestData", "rsa_2048_cert.pem")); + var testCertificate = new X509Certificate2(Path.Combine(Directory.GetCurrentDirectory(), "TestData", "rsa_2048.crt")); var signResult = CryptographyModelFactory.SignResult( keyId: "https://fake.vault.azure.net/keys/fake-key/123", signature: new byte[] { 1, 2, 3 }, @@ -177,7 +177,7 @@ public async Task GetCertificateAsync_ReturnsCertificate() [Fact] public async Task GetCertificateAsyncThrowValidationException() { - var testCertificate = new X509Certificate2(Path.Combine(Directory.GetCurrentDirectory(), "TestData", "rsa_2048_cert.pem")); + var testCertificate = new X509Certificate2(Path.Combine(Directory.GetCurrentDirectory(), "TestData", "rsa_2048.crt")); var signResult = CryptographyModelFactory.SignResult( keyId: "https://fake.vault.azure.net/keys/fake-key/123", signature: new byte[] { 1, 2, 3 }, diff --git a/Notation.Plugin.AzureKeyVault.Tests/Notation.Plugin.AzureKeyVault.Tests.csproj b/Notation.Plugin.AzureKeyVault.Tests/Notation.Plugin.AzureKeyVault.Tests.csproj index 8ca4dbf0..b7e396f2 100644 --- a/Notation.Plugin.AzureKeyVault.Tests/Notation.Plugin.AzureKeyVault.Tests.csproj +++ b/Notation.Plugin.AzureKeyVault.Tests/Notation.Plugin.AzureKeyVault.Tests.csproj @@ -20,6 +20,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive all + diff --git a/Notation.Plugin.AzureKeyVault.Tests/Protocol/CertificateExtensionTests.cs b/Notation.Plugin.AzureKeyVault.Tests/Protocol/CertificateExtensionTests.cs index 994f470a..218cf045 100644 --- a/Notation.Plugin.AzureKeyVault.Tests/Protocol/CertificateExtensionTests.cs +++ b/Notation.Plugin.AzureKeyVault.Tests/Protocol/CertificateExtensionTests.cs @@ -53,7 +53,7 @@ public void KeySpec_UnsupportedPublicKeyType_ThrowsValidationException() // private static X509Certificate2 LoadCertificate(string keyType, int keySize) { - var certName = $"{keyType.ToLower()}_{keySize}_cert.pem"; + var certName = $"{keyType.ToLower()}_{keySize}.crt"; return new X509Certificate2(Path.Combine(Directory.GetCurrentDirectory(), "TestData", certName)); } diff --git a/Notation.Plugin.AzureKeyVault.Tests/TestData/cert_chain.pem b/Notation.Plugin.AzureKeyVault.Tests/TestData/cert_chain.pem index 835c311a..54eabb97 100644 --- a/Notation.Plugin.AzureKeyVault.Tests/TestData/cert_chain.pem +++ b/Notation.Plugin.AzureKeyVault.Tests/TestData/cert_chain.pem @@ -1,39 +1,39 @@ -----BEGIN CERTIFICATE----- -MIIDNzCCAh+gAwIBAgIBAjANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdUZXN0 -IENBMB4XDTIzMDQyMDA4MzMxMloXDTI0MDQxOTA4MzMxMlowQzERMA8GA1UEChMI -bm90YXRpb24xCzAJBgNVBAgTAldBMQswCQYDVQQGEwJVUzEUMBIGA1UEAxMLVGVz -dC1TaWduZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC/mNdlcO+7 -rJkbuGJJsfbyqLgqZMMuYZCeJOQ3BuaehFMUG9W9Orkp4nFGSeaeT7cUlgrNF1Xy -TrOw/s9YAzxCoaKyfMPlxZJHeCL4NTbatbyV+oY+dGFLTWivdgMLK3uUjk4B2Q2Q -n8nFs/PtSwOf6wksZKzcJQBh1u0bKlFVJXGhZBc6N+/wpFzoiVGfs/I7l2rURFC7 -k1Lp0J2RYn3p2ieqT0dUdQlokdBt8jEbxt/q4hA3UbJsq1KUPa/iirh0t+HztExx -GhDM+uOdf4RoFAF3g8ByFiakT3wdsfFs7dmXnNAqcxGT6VrKLnd2U8RiMJRTJruS -2SWQq4KNoB49AgMBAAGjZzBlMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggr -BgEFBQcDAzAdBgNVHQ4EFgQUvJnagTED9382FSntElG0NC+kWSIwHwYDVR0jBBgw -FoAU3IfJ0NXrAT/vsRf3exnjQNwrhFIwDQYJKoZIhvcNAQELBQADggEBAE4Ec+kN -JqbE7qbW4oKS6z/OUSaxDJYvP/1scSS7nOwGuaAfLdN9/e2PBwO3UlqlrR/L9gNr -GjXE3vWkN7Bxg40G9/oMovQmPePMDnrkGBSTAW/ibrYdWBsvL1FABtfbAqIyeOrM -nOXwdUjOLzC9WWYW+F02lFp6jMvgPxY9qdCXtn7bW3wQZc/d17dUxQbaL0WUgPiR -4O7LPomVyV3wLfkFzgmnaX5aDeqB4tEtd04W09cGT0x5jda3JsW2geJAO+b4ChKH -E1fT4YoaDquqoM6eBrj5hapUuASUUGOLZm2vxSf0IOYqN+gIBwUiJ7FHe7NkFwY9 -Z9M1kj4R4ufg0yI= +MIIDOTCCAiGgAwIBAgIBAjANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdUZXN0 +IENBMCAXDTIzMDUwNDA3MDIwNloYDzIxMjMwNDEwMDcwMjA2WjBDMREwDwYDVQQK +Ewhub3RhdGlvbjELMAkGA1UECBMCV0ExCzAJBgNVBAYTAlVTMRQwEgYDVQQDEwtU +ZXN0LVNpZ25lcjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOZenQqj +cimpyADt540GvlXsAwYqAyFpXmJDVsWImNMvHt+kaWabK1C5HJjTlXqGE9wIg1oY +YG20pqbFKpPwULTBA7AkZvQipvMbkrqp9fJQUDi32HBLM4yIp1rYWciHIlKnv/Rk +tSbtNxg6ES50xLJl/V0YFwgxT9JayVYk6D5BEEgXffpZsMUtuGbRe0QsHED09jaX +onCvSV+sIpn0zAF4ahcWRseoTtfLAUfk5cJpFqwYvf3eIfvjWx7fvltpcEjfBDTj +8TxPXNlst9lgn3X5z2dr2dbltXOgNzX/NfLh7MbAe3uM0AE5fkrQ4CoIm/9tFuhT +nEv28Mk3WJQquX0CAwEAAaNnMGUwDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoG +CCsGAQUFBwMDMB0GA1UdDgQWBBTyceghcpaJ6QYyDNhHjIq2UkWmYzAfBgNVHSME +GDAWgBRRmku1TQfBHTqI6paqiKR40uhYIDANBgkqhkiG9w0BAQsFAAOCAQEAlzNY +LsYcV3hk22ZDFzGj8mZg6TjhHaejMgfOEm88UAUSjeHLaathV38sYHfqTwPwHjtf +lGGoSqzhiWasQ4Le4wRPraD/kFhRMRiUJ3YmW9EwwpXRDc7YexypbqVIKr/48gb3 +oMtUMYEarYlAOfWfMaMPHvXBlAIOq1oGLtuZ12doNJ3NqyqPoyLXNyjwYETnq+I/ +6qZQ4R1prdOwCdBuay6vn4lSrD45WxdjdWTDpAbYoT6voje+nHtH21EIxo1iWMpD +jzwp93CVbVxQnVf+dvSthHrUDPah+WPQ8yu4qhITxOrrAggCzBee3sK6WknERShf +aIZK0TY5G9C8rxUIhg== -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- -MIIDFDCCAfygAwIBAgITXhoD9dTxCqlG0zTvTuTFwEJBkTANBgkqhkiG9w0BAQsF -ADASMRAwDgYDVQQDDAdUZXN0IENBMB4XDTIzMDQyMDA4MzMwNVoXDTI0MDQxOTA4 -MzMwNVowEjEQMA4GA1UEAwwHVGVzdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEP -ADCCAQoCggEBAJefbW2Ga3GDo4nvT+8tLxUZlPcXmlOfiP/K7XzNiRnBinEwjPvV -GcpuYPThgVO9jr8lB58bddYdDKgweso/5liAlNYUpzOvW1qPipeKTsYIZg7+P0Z6 -a+F5Srxywv8yQ5grMKPOVgeuBwgXyS+G2kBU4jR0ic7FJHWqEayGzbj8KLJFVfW8 -nZEx1SsQFH6sc6mtQZadJxAeSqyT7zeGFAvtn97yV/+m6deU7fqyEIhKh5dhyxp6 -7WD/kaNXdtQdUuPLJOzztnzrSW9F3GW2Gg7DFupmU8Sk2K0LQt28PvHIH58rXkIL -xaaa79zIGYJuwGy1cUZE55Yb3XsNaGEXELcCAwEAAaNjMGEwHQYDVR0OBBYEFNyH -ydDV6wE/77EX93sZ40DcK4RSMB8GA1UdIwQYMBaAFNyHydDV6wE/77EX93sZ40Dc -K4RSMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgIEMA0GCSqGSIb3DQEB -CwUAA4IBAQB5g5hJBj+KvyOYfvG/CViEiNrIZ4pY+r+Qhi4nnkZ6tc3j0ZZu7rKq -VIBBgJ/7BN3UTK1vwWpf74JnIiwUDypt0xIj4bx3muSDI47pMyKC0rLSyKqwle72 -AaF9mrX0t/WVHstOUbq+wgRork7KAo50Jvl264QVUxsajydXyKtt52SEqiL7IXsn -fIctPdwf55VqCAwnSyRtT7rjO1PIuyJ3t8FRm6+Mkze83zBsvyHZ6eBzqlUksliI -6XBpq1uzhotopuQtnqQ9d77SEEP8ZUueieV7E9j4nHzFtXe/zEkX7ZgrSPXgK/fI -sNnvnj4+FBiMN4N1ziCigrvHlyr6i4Y6 +MIIDFzCCAf+gAwIBAgIUcXSzPd52oq3cTzlLBJOoO7fiprkwDQYJKoZIhvcNAQEL +BQAwEjEQMA4GA1UEAwwHVGVzdCBDQTAgFw0yMzA1MDQwNzAwNTVaGA8yMTIzMDQx +MDA3MDA1NVowEjEQMA4GA1UEAwwHVGVzdCBDQTCCASIwDQYJKoZIhvcNAQEBBQAD +ggEPADCCAQoCggEBAJoVdivOR5V/Qot+h8rwuHxYF4T0uWFF3XrIqJ3BIw7Ts8Ev +6EyIzc2oHlGOv5aM3IcW4Adn5bZgT0kPSi4ZAyxUVHG9o2TeeGIN6q8skrzP7316 +hnB92shXYCgqXwo3KP5uKhbNPRTQ7OVwhGQqLaVrJXLw9rddAnLLG9AtkiuCCi+b +U7ytPeq+vsP04BX1EKMuuErOt+TJLjJ7caYN6sobjmWEIrLcB31rPNuHBGJXXLYP +LXIJ/IcnDRIeq8M28QEOiEKpIb+OR6nojnkJG2zk14RCzrM+TrudPsE9PGUojYOL +qD5iKvexVLkum2zbYEOorl6q9BK+IL88m3Yr6j0CAwEAAaNjMGEwHQYDVR0OBBYE +FFGaS7VNB8EdOojqlqqIpHjS6FggMB8GA1UdIwQYMBaAFFGaS7VNB8EdOojqlqqI +pHjS6FggMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgIEMA0GCSqGSIb3 +DQEBCwUAA4IBAQCKJsj78P6r6+NFDHfrKC730JDUeThokcYKVqSLOKm1Sc0gEAx2 +wM88fZay2LRTisynt0qFxwUFcrPqlXJeob7bI+aJRGBEqyY/zDkycpEaOyHJFpiN +elrfpQ1bVjTgOpUqmG21URMenxCApbbrBiCeNmeHcIsL/MGLAU2UL1rhjG6NrZLE +IK/qRDNmDdntvn8SOHBg9nsc4u7OkNryKCTfqGwkB2TCNWB40DM6akPSRb5dVh/P +viqpIeyPYMHw+yvs74s6/xTim9/Jy9MROScurGxFb9Vrby7gjd+41WuGf1hUBQ7S +F/83FanddfSmtN/tyCz65FhyVyQJPT9oZATK -----END CERTIFICATE----- diff --git a/Notation.Plugin.AzureKeyVault.Tests/TestData/cert_chain.pfx b/Notation.Plugin.AzureKeyVault.Tests/TestData/cert_chain.pfx index dde27091e36dee3d92088c6ad5ad2b33f910dbaa..1a2bff01d05c9ea4a8fb0de19155507aa9f04be3 100644 GIT binary patch delta 2150 zcmV-s2$}cd5b6*>FoFnX0s#Xsf(Rl82`Yw2hW8Bt2LYgh2onT?2oErV2n#TR2nPlU zDuzgg_YDCD2B3lm_%MP8^a23@FoFl|kw6`PzCyd3IaT~x0s;rnfPx3W|52FwCM0n| z^}$b|yDW!}tWp!JWo_|p1B$1_-an~G3~VvP=eZ{wn2U&W(GK9M(Y*l+g7;?sRT&bD zQ!esxOI}|#!CyyUPU+ml9RGP~ZYdy6X9=1wstMu-vPwJINY=kYlyFs;341-#;(!~u0a!~Z&7UbX*K z!ERg~;qkJfi7!C5iX!z+b?%&`)9gKf3r!b-G|8$uM7Q8oByAv3! z*W;sH^CAguO+>rkw6#Fl0dP;Y8$UdMJOl6GCxn>K@|QW-3oOZ_X>%S(per8_KQi=AMIW=q;CZN3C=2U&Z zE@LEQtu73_)s0U^T+A<7cRCAyLzmMA5&M(Ot+ivbdh)sAE(sKD&aSZbZ1ZM}5Y@+* zVnNkr$<-g=Pp+hO04-F2U`VJ8U7)O%n|U9olI6x!D)z03(RB_%!jfzz(br4M#%*NP zBWWTvqhuur(fsJbr0i9`v3ZT3*&Xo(D85b`Pjg7k_gQ|{yoxnnb!u%;} ziZ99H__(c=y)y)^A*&M))g^F1#pmL!=9r|pu*uftX$snB`{Yf8NV z$tgV_(Ac{Ed*qyetXRl@#fJhY`r@gMfuq9CeHH1+lAu)8zA>7RwL~%hU`fw}(lY)4 zGTBN&)u=JbhY2s)*co=2wWGl+EMZb(N4>0aQq8cQajEWGC$SD31 zQj5nYi?2*&kNdf7;06>2djE^Y&Y?->#04)MI79xVqgZUcehNwbI-iMT;Z6sgAh8UqoI?%Px@ zr2i$$NHJR0u5MH{OIyjW%$QKYSZi1*|G#On6Si|{SmV#afmmQ_l3k}&HE5eRErf%T z{`jM6ImJJifk^v*IWO2+t$5Pbg7$;z`PYRRy(fF$f|HqJSo*(8NOPp|Y5a+cX1lh+ zTg}j-whNRF_RX$B^7fo;evX~XChYQYB(y4xai!g07r$&2uu^vsNnqLvC)1_Wj9eZ< zQtehV#)WUt)ro=3GktJ>smuL$b~9(H3ySbm*lxpS z*^_p)J0_R@*f$(RMjCaHa4I~%F>vrz)o-i;cbJPkrBde8)mnV%njuY*nOO`UyU*IYF$oyNqkBcY6pWgKQWpGM)CZC zL%fXb7#_`keET`otS~S~Dk?OB+%&_<)mvWdM;4ck%tum(b&xE^thbV1UXElE+H z+QnNypZpXraBne$kmBe3omgfU%28R{ z)p$*}K$LeH#+najeQp|9s~-01Jf{;-CvD1vO%52`jwcTUN^N;36F-SGOZV^4n3YP< z>sBv+Nk9OZ9f5=I5#LGwLay7lv)*TFV@?ce#ze!w#dr|W+iXw)dvC<`2Y*vCIrAq` z$YSY#2r1cLdf_Z@v*?6is6NU9Zdy(jx-P^J6=h(c`tUo@!s8+;tlT?t(w-Ch(eKgC zwG-CAPXy@UixeA4T!0a0z~z@r((P0%ek>ti{-wk7;MHtYK?w+d_E)&La?+Bp?m%6< zsWloD1GzjE2W>OXWrER+bu*SulCB3UH?q)wvLnQdyyi8-u;+czzHP~N5NZH8up_zF zDi+|~Ybrt)D7zG;S|FB8LmR|YWim_x#6n6PSdypr%cH1|V&7~)L$*9LpeF9TwIA)K z0MS(t_^=FvckBqkbf`K&S9}k4y2%f-EZJhLp0j~Ls$nk80MY=pJjZ7QtbXM97?yH> zs(>b-Fw-D_0h47r#dTpJy5L+lIpgnrp;$YSYS-JiY+_i{NdF+h6y)YXcC+A*SqX&1z2m`2~@Y^>#s zdb?3#>g&^|1RS|TE(Pn+6IF!{i`m-a$K%l-%&VWO8WkaeNtv}&^Gfl&qNEMc3&}bj z6EMJE%lE)BI8W2Bs+B@8J1`$G2L=Tz4g&%j1QgDfwYrf^+5259G7}2)`OBLffUDvJ c6rL+j;M$Gz;?mPGydOwuw$%zAhXMiz&{rA_1ONa4 delta 2142 zcmV-k2%-1t5aJL(FoFnP0s#Xsf(RN02`Yw2hW8Bt2LYgh2nz&)2nR5N2m>&J2mb~M zDuzgg_YDCD2B3lm@Gyc0>;eG*FoFl=kw6`PAWh+uR5ZFw0s;rnfPx3O!yL9A+(7nR z8f-u?_uz_z3~=!Nlb|cDu_j+a*~+j^eV!ewmcvQ=ui~ZuXhkpYsSQAG@yc zL6DPwc*PV|RTz~vjw&6@f3gDK8?yTxPgnu$nEKT)cPF!WGlma)dVcX?@ znvKs){tJxuh}an7Ittc`g0aYR^*+oEEk3Z&%zhK$KmQE(Je>#9JF^`-5~*tbV&F-~ z88fFEcp&m_IKPW?l`ImLg!QIzl261k+lD~ct6Mml)k4J44uZ;!cP9;TynRxsqvE6} ze`Ew^0OK+%c2+s;&H)odYv!O-ZPhG)qI&4diPw7Y)i=F!$xd{m&Z#akOC;Gq0%Z>V zzM=H<2_sFJl%&IhDxq71So8}!C(_nEy%dR`=wLcOqtT=JqFM|Y&`HK7bG-s_cw@zw zVj%rpB|yBf)&@%s;Y9e}H9{Bil%8c>Q>=d}N%T-fk@62iZo|>R`#!aERe2?UVSNM< zC1SUN?{_?Xd|pQ)3BXy<}h=alTz)@$Z`^iD`EccvAAgIb*P)MB=-DAoZ{7}?W8HGlIONKtINr< zrXyw-k&83}XZxslW%_5xEY+-kWO{a%jlc{4YBIVvL_aRhm!fX&pK|&e1l4QX1cvls z{#~U2Luefb9)EQ#BUgtO>uhjDcx_il#W9d+`Ql4n0{#MflbIP7>gJakIo)mSk(TY- zhyg|JgVo#p*GeP6O6J|bbet1%=V{xDSoFtb-iB|2>mkU!tob18-Jj&p>Giz<_(irKG^!mtgpB1nB%lVbV)(+<$w(@Z0~B z>ClVgVKFQnVQAfXGvNw_Fw{;gChWTsp6cl+h(kVsYAJjA30Hc5tS>5?I+5iR?|BKX z;b6)e9g)vfs|J#iubpbV%Ur?l5aGhB;>u+Os|n_*+=;p$n zF+s5d>R_I-V=KKqkh)TDD)~tI zngrpd$>X&FW0x)#=A?k6P211le&)_W4ui8@e)A`Cnd4R0fUwX{TGdj(!0J#M=5vy# zHfy-5Eo=#b+l4r}#SoQ_&4S9@NwNUp$!^N9g$$yO)m!WTy>qu89gSVElpAsu;Ervl z_a3-U^0Lx@Rq2z@f&CX}dXItmqwW9d0RhA7OM~rDd9v(xM*sh%n@XJ-|E_TD zwHFP(Ij`=s?x7WO$c3LE)0tSMSi{lp0dO%V~54J%r`Qt54m6T1pLSV zu(pfST^LfWEL@2vXh+Qx(s^t&i=ki2UE< zDGn%qwf!#IpVr3HFPZRB)$VOl&6GrtWaY7W0Vqf4m{;SiK;NN~_ie*U`MeX9R9e=V z!KN83uXsE-SJ#SRGs<5a*ZXgYB-B*>REa;`AaTZ6PXJgW9dk9z7GYMm3&;3#u{{a* ztp=%%@GYBYG>b%rB$5wP=i=b^S5kyH(4rZC*5OOeTt0YD%Ah`YBT`_*pZ!b^(M(Kan@l{Fao9io`?HVFa`1zaX{-nfVH;n1jBYz)Vh`sdOVO>bk zrcuV};!(ImRKDx3cSws3 zj`eEJb&B?5=@C-5=!WLKet^^da3aNj&sZCjkr%?G!u=*;epWB9K>3#f)`gc|oDR6Z z*`PBt)X4_12|&teI@Q!!M9cr*6my+YIPY(K+0ys<;E@WHz!uv@#!As3=KouW4tJk9 z@r*uQYJxj3J1`$G2L=Tz4g&%j1Qf+o{Xv$k5(Y#N#sYTFEvfflSSAD%bcb6Np>pZ5 U5-{h6w~}%} Date: Thu, 4 May 2023 15:17:17 +0800 Subject: [PATCH 14/17] fix: update code Signed-off-by: Junjie Gao --- Notation.Plugin.AzureKeyVault/Command/IPluginCommand.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Notation.Plugin.AzureKeyVault/Command/IPluginCommand.cs b/Notation.Plugin.AzureKeyVault/Command/IPluginCommand.cs index 665b5e1e..8a29df45 100644 --- a/Notation.Plugin.AzureKeyVault/Command/IPluginCommand.cs +++ b/Notation.Plugin.AzureKeyVault/Command/IPluginCommand.cs @@ -7,4 +7,4 @@ public interface IPluginCommand { Task RunAsync(); } -} \ No newline at end of file +} From 5370682c80af42b59c4fe5c7e516f903af203b3e Mon Sep 17 00:00:00 2001 From: Junjie Gao Date: Fri, 5 May 2023 14:32:33 +0800 Subject: [PATCH 15/17] fix: update using sort Signed-off-by: Junjie Gao --- .../KeyVault/KeyVaultClientTests.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Notation.Plugin.AzureKeyVault.Tests/KeyVault/KeyVaultClientTests.cs b/Notation.Plugin.AzureKeyVault.Tests/KeyVault/KeyVaultClientTests.cs index a7a27c94..b70ecb9a 100644 --- a/Notation.Plugin.AzureKeyVault.Tests/KeyVault/KeyVaultClientTests.cs +++ b/Notation.Plugin.AzureKeyVault.Tests/KeyVault/KeyVaultClientTests.cs @@ -1,18 +1,22 @@ using System; using System.IO; using System.Security.Cryptography.X509Certificates; +using System.Text; using System.Threading; using System.Threading.Tasks; + using Azure; using Azure.Core; using Azure.Security.KeyVault.Certificates; using Azure.Security.KeyVault.Keys; using Azure.Security.KeyVault.Keys.Cryptography; using Azure.Security.KeyVault.Secrets; + using Moq; -using Xunit; + using Notation.Plugin.Protocol; -using System.Text; + +using Xunit; namespace Notation.Plugin.AzureKeyVault.Client.Tests { From 446e477467a49317949ca29791466fbd9bfa6087 Mon Sep 17 00:00:00 2001 From: Junjie Gao Date: Fri, 5 May 2023 15:03:07 +0800 Subject: [PATCH 16/17] fix: update using sorting Signed-off-by: Junjie Gao --- .../KeyVault/KeyVaultClientTests.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Notation.Plugin.AzureKeyVault.Tests/KeyVault/KeyVaultClientTests.cs b/Notation.Plugin.AzureKeyVault.Tests/KeyVault/KeyVaultClientTests.cs index b70ecb9a..1875575f 100644 --- a/Notation.Plugin.AzureKeyVault.Tests/KeyVault/KeyVaultClientTests.cs +++ b/Notation.Plugin.AzureKeyVault.Tests/KeyVault/KeyVaultClientTests.cs @@ -4,18 +4,14 @@ using System.Text; using System.Threading; using System.Threading.Tasks; - using Azure; using Azure.Core; using Azure.Security.KeyVault.Certificates; using Azure.Security.KeyVault.Keys; using Azure.Security.KeyVault.Keys.Cryptography; using Azure.Security.KeyVault.Secrets; - using Moq; - using Notation.Plugin.Protocol; - using Xunit; namespace Notation.Plugin.AzureKeyVault.Client.Tests From 08f69451a63a2f48694c971fa2e169346fdcd9a9 Mon Sep 17 00:00:00 2001 From: Junjie Gao Date: Fri, 5 May 2023 15:07:33 +0800 Subject: [PATCH 17/17] fix: update using order Signed-off-by: Junjie Gao --- .../Certificate/CertificateChainTests.cs | 4 ++-- .../Command/GetPluginMetadataTests.cs | 2 +- .../KeyVault/KeySpecExtensionTests.cs | 2 +- Notation.Plugin.AzureKeyVault/Command/DescribeKey.cs | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Notation.Plugin.AzureKeyVault.Tests/Certificate/CertificateChainTests.cs b/Notation.Plugin.AzureKeyVault.Tests/Certificate/CertificateChainTests.cs index 6dae71c9..7f337c8f 100644 --- a/Notation.Plugin.AzureKeyVault.Tests/Certificate/CertificateChainTests.cs +++ b/Notation.Plugin.AzureKeyVault.Tests/Certificate/CertificateChainTests.cs @@ -1,8 +1,8 @@ using System.Collections.Generic; +using System.IO; using System.Security.Cryptography.X509Certificates; -using Xunit; using Notation.Plugin.Protocol; -using System.IO; +using Xunit; namespace Notation.Plugin.AzureKeyVault.Certificate.Tests { diff --git a/Notation.Plugin.AzureKeyVault.Tests/Command/GetPluginMetadataTests.cs b/Notation.Plugin.AzureKeyVault.Tests/Command/GetPluginMetadataTests.cs index 0fb07a0c..8b24ee6c 100644 --- a/Notation.Plugin.AzureKeyVault.Tests/Command/GetPluginMetadataTests.cs +++ b/Notation.Plugin.AzureKeyVault.Tests/Command/GetPluginMetadataTests.cs @@ -1,6 +1,6 @@ using System.Threading.Tasks; -using Xunit; using Notation.Plugin.Protocol; +using Xunit; namespace Notation.Plugin.AzureKeyVault.Command.Tests { diff --git a/Notation.Plugin.AzureKeyVault.Tests/KeyVault/KeySpecExtensionTests.cs b/Notation.Plugin.AzureKeyVault.Tests/KeyVault/KeySpecExtensionTests.cs index 9359e30a..9912ef4b 100644 --- a/Notation.Plugin.AzureKeyVault.Tests/KeyVault/KeySpecExtensionTests.cs +++ b/Notation.Plugin.AzureKeyVault.Tests/KeyVault/KeySpecExtensionTests.cs @@ -1,6 +1,6 @@ using System; -using Xunit; using Notation.Plugin.Protocol; +using Xunit; namespace Notation.Plugin.AzureKeyVault.Client.Tests { diff --git a/Notation.Plugin.AzureKeyVault/Command/DescribeKey.cs b/Notation.Plugin.AzureKeyVault/Command/DescribeKey.cs index b60a0237..7930d2d8 100644 --- a/Notation.Plugin.AzureKeyVault/Command/DescribeKey.cs +++ b/Notation.Plugin.AzureKeyVault/Command/DescribeKey.cs @@ -1,6 +1,6 @@ using System.Text.Json; -using Notation.Plugin.Protocol; using Notation.Plugin.AzureKeyVault.Client; +using Notation.Plugin.Protocol; namespace Notation.Plugin.AzureKeyVault.Command {