Skip to content

Commit

Permalink
Set SendCertificateChain option in KeyVaultReader to enable SN+I au…
Browse files Browse the repository at this point in the history
…thentication (NuGet#10179)

* Set `SendCertificateChain` option in KeyVaultReader to enable SN+I authentication
* Add unit test for Sendx5c
* Add tests
* Fix test
* Add a mock SecretClient and internal constructor
  • Loading branch information
adityapatwardhan authored Sep 12, 2024
1 parent 5cc2ee8 commit 08cf2a8
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 2 deletions.
30 changes: 28 additions & 2 deletions src/NuGet.Services.KeyVault/KeyVaultReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,23 @@ namespace NuGet.Services.KeyVault
{
/// <summary>
/// Reads secretes from KeyVault.
/// Authentication with KeyVault is done using either a managed identity or a certificate in location:LocalMachine store name:My
/// Authentication with KeyVault is done using either a managed identity or a certificate in location:LocalMachine store name:My
/// </summary>
public class KeyVaultReader : ISecretReader
{
private readonly KeyVaultConfiguration _configuration;
private readonly Lazy<SecretClient> _keyVaultClient;

protected SecretClient KeyVaultClient => _keyVaultClient.Value;
internal bool _testMode;
internal bool _isUsingSendx5c;

internal KeyVaultReader(SecretClient secretClient, KeyVaultConfiguration configuration, bool testMode = false)
{
_configuration = configuration;
_keyVaultClient = new Lazy<SecretClient>(() => secretClient);
_testMode = testMode;
InitializeClient();
}

public KeyVaultReader(KeyVaultConfiguration configuration)
{
Expand Down Expand Up @@ -97,10 +106,27 @@ private SecretClient InitializeClient()
credential = new ManagedIdentityCredential(_configuration.ClientId);
}
}
else if (_configuration.SendX5c)
{
var clientCredentialOptions = new ClientCertificateCredentialOptions
{
SendCertificateChain = true
};

credential = new ClientCertificateCredential(_configuration.TenantId, _configuration.ClientId, _configuration.Certificate, clientCredentialOptions);

// If we are in unit testing mode, we dont actually create a SecretClient
if (_testMode)
{
_isUsingSendx5c = true;
return _keyVaultClient.Value;
}
}
else
{
credential = new ClientCertificateCredential(_configuration.TenantId, _configuration.ClientId, _configuration.Certificate);
}

return new SecretClient(GetKeyVaultUri(_configuration), credential);
}

Expand Down
10 changes: 10 additions & 0 deletions src/NuGet.Services.KeyVault/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Runtime.CompilerServices;

#if SIGNED_BUILD
[assembly: InternalsVisibleTo("NuGet.Services.KeyVault.Tests,PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")]
#else
[assembly: InternalsVisibleTo("NuGet.Services.KeyVault.Tests")]
#endif
40 changes: 40 additions & 0 deletions tests/NuGet.Services.KeyVault.Tests/KeyVaultReaderFacts.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
using Azure.Security.KeyVault.Secrets;
using Moq;
using Xunit;

namespace NuGet.Services.KeyVault.Tests
{
public class KeyVaultReaderFacts
{
[Fact]
public void VerifyKeyvaultReaderSendX5c()
{
// Arrange
const string vaultName = "vaultName";
const string tenantId = "tenantId";
const string clientId = "clientId";

X509Certificate2 certificate = new X509Certificate2();
KeyVaultConfiguration keyVaultConfiguration = new KeyVaultConfiguration(vaultName, tenantId, clientId, certificate, sendX5c:true);

var mockSecretClient = new Mock<SecretClient>();

// Act
var keyvaultReader = new KeyVaultReader(mockSecretClient.Object, keyVaultConfiguration, testMode: true);

// Assert

// The KeyVaultReader constructor is internal which accepts a SecretClient object, KeyVaultConfiguration object and a boolean testMode parameter
// The KeyVaultConfiguration object has the sendX5c property which is set to true
// The KeyVaultReader object has an internal boolean _isUsingSendx5c which is set to true if the sendX5c property is set to true
// The KeyVaultReader shot-circuits when the testMode is set to true instead of calling Azure KeyVault
Assert.True(keyvaultReader._isUsingSendx5c);
}
}
}

0 comments on commit 08cf2a8

Please sign in to comment.