Skip to content

Commit 08cf2a8

Browse files
Set SendCertificateChain option in KeyVaultReader to enable SN+I authentication (#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
1 parent 5cc2ee8 commit 08cf2a8

3 files changed

Lines changed: 78 additions & 2 deletions

File tree

src/NuGet.Services.KeyVault/KeyVaultReader.cs

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,23 @@ namespace NuGet.Services.KeyVault
1313
{
1414
/// <summary>
1515
/// Reads secretes from KeyVault.
16-
/// Authentication with KeyVault is done using either a managed identity or a certificate in location:LocalMachine store name:My
16+
/// Authentication with KeyVault is done using either a managed identity or a certificate in location:LocalMachine store name:My
1717
/// </summary>
1818
public class KeyVaultReader : ISecretReader
1919
{
2020
private readonly KeyVaultConfiguration _configuration;
2121
private readonly Lazy<SecretClient> _keyVaultClient;
22-
2322
protected SecretClient KeyVaultClient => _keyVaultClient.Value;
23+
internal bool _testMode;
24+
internal bool _isUsingSendx5c;
25+
26+
internal KeyVaultReader(SecretClient secretClient, KeyVaultConfiguration configuration, bool testMode = false)
27+
{
28+
_configuration = configuration;
29+
_keyVaultClient = new Lazy<SecretClient>(() => secretClient);
30+
_testMode = testMode;
31+
InitializeClient();
32+
}
2433

2534
public KeyVaultReader(KeyVaultConfiguration configuration)
2635
{
@@ -97,10 +106,27 @@ private SecretClient InitializeClient()
97106
credential = new ManagedIdentityCredential(_configuration.ClientId);
98107
}
99108
}
109+
else if (_configuration.SendX5c)
110+
{
111+
var clientCredentialOptions = new ClientCertificateCredentialOptions
112+
{
113+
SendCertificateChain = true
114+
};
115+
116+
credential = new ClientCertificateCredential(_configuration.TenantId, _configuration.ClientId, _configuration.Certificate, clientCredentialOptions);
117+
118+
// If we are in unit testing mode, we dont actually create a SecretClient
119+
if (_testMode)
120+
{
121+
_isUsingSendx5c = true;
122+
return _keyVaultClient.Value;
123+
}
124+
}
100125
else
101126
{
102127
credential = new ClientCertificateCredential(_configuration.TenantId, _configuration.ClientId, _configuration.Certificate);
103128
}
129+
104130
return new SecretClient(GetKeyVaultUri(_configuration), credential);
105131
}
106132

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System.Runtime.CompilerServices;
5+
6+
#if SIGNED_BUILD
7+
[assembly: InternalsVisibleTo("NuGet.Services.KeyVault.Tests,PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")]
8+
#else
9+
[assembly: InternalsVisibleTo("NuGet.Services.KeyVault.Tests")]
10+
#endif
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
using System.Security.Cryptography.X509Certificates;
6+
using System.Threading.Tasks;
7+
using Azure.Security.KeyVault.Secrets;
8+
using Moq;
9+
using Xunit;
10+
11+
namespace NuGet.Services.KeyVault.Tests
12+
{
13+
public class KeyVaultReaderFacts
14+
{
15+
[Fact]
16+
public void VerifyKeyvaultReaderSendX5c()
17+
{
18+
// Arrange
19+
const string vaultName = "vaultName";
20+
const string tenantId = "tenantId";
21+
const string clientId = "clientId";
22+
23+
X509Certificate2 certificate = new X509Certificate2();
24+
KeyVaultConfiguration keyVaultConfiguration = new KeyVaultConfiguration(vaultName, tenantId, clientId, certificate, sendX5c:true);
25+
26+
var mockSecretClient = new Mock<SecretClient>();
27+
28+
// Act
29+
var keyvaultReader = new KeyVaultReader(mockSecretClient.Object, keyVaultConfiguration, testMode: true);
30+
31+
// Assert
32+
33+
// The KeyVaultReader constructor is internal which accepts a SecretClient object, KeyVaultConfiguration object and a boolean testMode parameter
34+
// The KeyVaultConfiguration object has the sendX5c property which is set to true
35+
// The KeyVaultReader object has an internal boolean _isUsingSendx5c which is set to true if the sendX5c property is set to true
36+
// The KeyVaultReader shot-circuits when the testMode is set to true instead of calling Azure KeyVault
37+
Assert.True(keyvaultReader._isUsingSendx5c);
38+
}
39+
}
40+
}

0 commit comments

Comments
 (0)