Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add versionless key identifier support #181

Merged
merged 16 commits into from
May 29, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ public void TestConstructorWithInvalidKeyId(string invalidKeyId)
[Theory]
[InlineData("", "", "")]
[InlineData("https://myvault.vault.azure.net", "", "")]
[InlineData("https://myvault.vault.azure.net", "my-key", "")]
public void TestConstructorWithInvalidArguments(string keyVaultUrl, string name, string? version)
{
Assert.Throws<ValidationException>(() => new KeyVaultClient(keyVaultUrl, name, version, Credentials.GetCredentials(defaultCredentialType)));
Expand Down Expand Up @@ -162,6 +163,20 @@ public async Task TestSignAsyncReturnsExpectedSignature()
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<PluginException>(async () => await keyVaultClient.SignAsync(SignatureAlgorithm.RS256, payload));
}

[Fact]
public async Task TestSignAsyncThrowsExceptionOnInvalidAlgorithm()
{
Expand Down
2 changes: 1 addition & 1 deletion Notation.Plugin.AzureKeyVault/Command/DescribeKey.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public DescribeKey(string inputJson)
var request = JsonSerializer.Deserialize(inputJson, DescribeKeyRequestContext.Default.DescribeKeyRequest);
if (request == null)
{
throw new ValidationException("Failed to parse the request for the plugin. Please contact the Notation developer to resolve the issue.");
throw new ValidationException("Failed to parse the request in JSON format. Please contact the Notation developer to resolve the issue.");
JeyJeyGao marked this conversation as resolved.
Show resolved Hide resolved
}
this._request = request;
this._keyVaultClient = new KeyVaultClient(
Expand Down
2 changes: 1 addition & 1 deletion Notation.Plugin.AzureKeyVault/Command/GenerateSignature.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public GenerateSignature(string inputJson)
var request = JsonSerializer.Deserialize(inputJson, GenerateSignatureRequestContext.Default.GenerateSignatureRequest);
if (request == null)
{
throw new ValidationException("Failed to parse the request for the plugin. Please contact the Notation developer to resolve the issue.");
throw new ValidationException("Failed to parse the request in JSON format. Please contact the Notation developer to resolve the issue.");
JeyJeyGao marked this conversation as resolved.
Show resolved Hide resolved
}
this._request = request;
this._keyVaultClient = new KeyVaultClient(
Expand Down
16 changes: 13 additions & 3 deletions Notation.Plugin.AzureKeyVault/KeyVault/KeyVaultClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,15 @@ public KeyVaultClient(string keyVaultUrl, string name, string? version, TokenCre
throw new ValidationException("Key name must not be null or empty");
}

if (version != null && version == string.Empty)
Two-Hearts marked this conversation as resolved.
Show resolved Hide resolved
{
throw new ValidationException("Key version must not be empty");
}

_name = name;
_version = version;
_keyId = $"{keyVaultUrl}/keys/{name}";
if (!string.IsNullOrEmpty(version))
if (version != null)
{
_keyId = $"{_keyId}/{version}";
}
Expand Down Expand Up @@ -119,7 +124,7 @@ private static KeyVaultMetadata ParseId(string id)
// Validate uri
if (uri.Segments.Length < 3 || uri.Segments.Length > 4)
{
throw new ValidationException("Invalid input passed to \"--id\". Please follow this format to input the ID \"https://{vault-name}.vault.azure.net/certificates/{certificate-name}\"");
throw new ValidationException("Invalid input passed to \"--id\". Please follow this format to input the ID \"https://{vault-name}.vault.azure.net/certificates/{certificate-name}\" or \"https://{vault-name}.vault.azure.net/certificates/{certificate-name}/{certificate-version}\"");
JeyJeyGao marked this conversation as resolved.
Show resolved Hide resolved
}

var type = uri.Segments[1].TrimEnd('/');
Expand Down Expand Up @@ -152,6 +157,11 @@ public async Task<byte[]> SignAsync(SignatureAlgorithm algorithm, byte[] payload
{
var signResult = await _cryptoClient.Value.SignDataAsync(algorithm, payload);

if (!string.IsNullOrEmpty(_version) && signResult.KeyId != _keyId)
{
throw new PluginException($"Invalid keys identifier. The user provides {_keyId} but the response contains {signResult.KeyId} as the keys. Please ensure the keys identifier is correct.");
JeyJeyGao marked this conversation as resolved.
Show resolved Hide resolved
}

if (signResult.Algorithm != algorithm)
{
throw new PluginException($"Invalid signature algorithm. The user provides {algorithm} but the response contains {signResult.Algorithm} as the algorithm");
Expand Down Expand Up @@ -180,7 +190,7 @@ public async Task<X509Certificate2> GetCertificateAsync()
// requested version, it means the version is invalid.
if (cert.Properties.Version != _version)
{
throw new PluginException($"The version of the certificate retrieved from Azure Key Vault is different from the version specified in the request. The version specified in the request is {_version} but the version retrieved from Azure Key Vault is {cert.Properties.Version}");
throw new PluginException($"The version specified in the request is {_version} but the version retrieved from Azure Key Vault is {cert.Properties.Version}. Please ensure the version is correct.");
}
}
return new X509Certificate2(cert.Cer);
Expand Down
2 changes: 2 additions & 0 deletions docs/ca-signed-workflow.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,8 @@
```sh
keyID=$(az keyvault certificate show -n $certName --vault-name $keyVault --query 'kid' -o tsv)
```
>[!NOTE]
Versionless KeyID is also supported. For example: https://{vault-name}.vault.azure.net/certificates/{certificate-name}
JeyJeyGao marked this conversation as resolved.
Show resolved Hide resolved
7. [Create an Azure Container Registry](https://learn.microsoft.com/azure/container-registry/container-registry-get-started-portal?tabs=azure-cli). The remaining steps use the example login server `<registry-name>.azurecr.io`, but you must substitute your own login server value.
8. Log in to container registry and push an image for signing:
```sh
Expand Down
2 changes: 2 additions & 0 deletions docs/self-signed-workflow.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@
# get the key identifier
keyID=$(az keyvault certificate show -n $certName --vault-name $keyVault --query 'kid' -o tsv)
```
>[!NOTE]
Versionless KeyID is also supported. For example: https://{vault-name}.vault.azure.net/certificates/{certificate-name}
JeyJeyGao marked this conversation as resolved.
Show resolved Hide resolved
5. [Create an Azure Container Registry](https://learn.microsoft.com/azure/container-registry/container-registry-get-started-portal?tabs=azure-cli). The remaining steps use the example login server `<registry-name>.azurecr.io`, but you must substitute your own login server value.
6. Log in to container registry and push an image for signing:
```sh
Expand Down
Loading