forked from skoruba/IdentityServer4.Admin
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add implementation for loading signing keys from Key Vault skoruba#533
- Loading branch information
1 parent
64047b0
commit 0358a47
Showing
10 changed files
with
210 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
7 changes: 7 additions & 0 deletions
7
src/Skoruba.IdentityServer4.STS.Identity/Configuration/TokenSigningConfiguration.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
namespace Skoruba.IdentityServer4.STS.Identity.Configuration | ||
{ | ||
public class TokenSigningConfiguration | ||
{ | ||
public static bool UseAzureKeyVault = true; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
9 changes: 9 additions & 0 deletions
9
src/Skoruba.IdentityServer4.STS.KeyVault/Configuration/AzureKeyVaultConfiguration.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
namespace Skoruba.IdentityServer4.STS.KeyVault.Configuration | ||
{ | ||
public class AzureKeyVaultConfiguration | ||
{ | ||
public string KeyVaultUri { get; set; } | ||
public string KeyName { get; set; } | ||
public string KeyIdentifier => $"{KeyVaultUri}/keys/{KeyName}"; | ||
} | ||
} |
34 changes: 34 additions & 0 deletions
34
src/Skoruba.IdentityServer4.STS.KeyVault/ServiceCollectionExtensions.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
using IdentityServer4.Services; | ||
using IdentityServer4.Stores; | ||
using Microsoft.Azure.KeyVault; | ||
using Microsoft.Azure.Services.AppAuthentication; | ||
using Microsoft.Extensions.Configuration; | ||
using Microsoft.Extensions.DependencyInjection; | ||
using Skoruba.IdentityServer4.STS.KeyVault.Configuration; | ||
using Skoruba.IdentityServer4.STS.KeyVault.Stores; | ||
using Skoruba.IdentityServer4.STS.KeyVault.Tokens; | ||
|
||
namespace Skoruba.IdentityServer4.STS.KeyVault | ||
{ | ||
public static class ServiceCollectionExtensions | ||
{ | ||
public static IServiceCollection AddKeyVaultSigningKeyFeature( | ||
this IServiceCollection services, | ||
IConfiguration configuration) | ||
{ | ||
var azureServiceTokenProvider = new AzureServiceTokenProvider(); | ||
var authenticationCallback = new KeyVaultClient.AuthenticationCallback(azureServiceTokenProvider.KeyVaultTokenCallback); | ||
var keyVaultClient = new KeyVaultClient(authenticationCallback); | ||
var azureKeyVaultConfiguration = new AzureKeyVaultConfiguration(); | ||
configuration.GetSection("AzureKeyVaultConfiguration").Bind(azureKeyVaultConfiguration); | ||
|
||
services.AddSingleton<IKeyVaultClient>(keyVaultClient) | ||
.AddSingleton(azureKeyVaultConfiguration) | ||
.AddTransient<ITokenCreationService, KeyVaultTokenCreationService>() | ||
.AddTransient<ISigningCredentialStore, AzureKeyVaultKeyStore>() | ||
.AddTransient<IValidationKeysStore, AzureKeyVaultKeyStore>(); | ||
|
||
return services; | ||
} | ||
} | ||
} |
15 changes: 15 additions & 0 deletions
15
src/Skoruba.IdentityServer4.STS.KeyVault/Skoruba.IdentityServer4.STS.KeyVault.csproj
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<TargetFramework>netcoreapp3.1</TargetFramework> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="Azure.Security.KeyVault.Keys" Version="4.0.3" /> | ||
<PackageReference Include="IdentityServer4" Version="3.1.2" /> | ||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="3.1.3" /> | ||
<PackageReference Include="Microsoft.Extensions.Configuration.AzureKeyVault" Version="3.1.3" /> | ||
<PackageReference Include="Microsoft.Azure.KeyVault" Version="3.0.5" /> | ||
<PackageReference Include="Microsoft.Azure.Services.AppAuthentication" Version="1.4.0" /> | ||
</ItemGroup> | ||
</Project> |
76 changes: 76 additions & 0 deletions
76
src/Skoruba.IdentityServer4.STS.KeyVault/Stores/AzureKeyVaultKeyStore.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Threading.Tasks; | ||
using IdentityServer4.Models; | ||
using IdentityServer4.Stores; | ||
using Microsoft.Azure.KeyVault; | ||
using Microsoft.IdentityModel.Tokens; | ||
using Skoruba.IdentityServer4.STS.KeyVault.Configuration; | ||
|
||
namespace Skoruba.IdentityServer4.STS.KeyVault.Stores | ||
{ | ||
public class AzureKeyVaultKeyStore : ISigningCredentialStore, IValidationKeysStore | ||
{ | ||
private const string SigningAlgorithm = "PS256"; | ||
|
||
private readonly AzureKeyVaultConfiguration _configuration; | ||
private readonly IKeyVaultClient _keyVaultClient; | ||
|
||
public AzureKeyVaultKeyStore( | ||
AzureKeyVaultConfiguration configuration, | ||
IKeyVaultClient keyVaultClient) | ||
{ | ||
_configuration = configuration; | ||
_keyVaultClient = keyVaultClient; | ||
} | ||
|
||
public async Task<SigningCredentials> GetSigningCredentialsAsync() | ||
{ | ||
var response = await _keyVaultClient.GetKeyAsync(_configuration.KeyIdentifier); | ||
var key = new RsaSecurityKey(response.Key.ToRSA()) | ||
{ | ||
KeyId = response.KeyIdentifier.Version | ||
}; | ||
|
||
return new SigningCredentials(key, SigningAlgorithm); | ||
} | ||
|
||
public async Task<IEnumerable<SecurityKeyInfo>> GetValidationKeysAsync() | ||
{ | ||
var validationKeys = new List<SecurityKeyInfo>(); | ||
var keyItemPage = await _keyVaultClient.GetKeyVersionsAsync(_configuration.KeyVaultUri, _configuration.KeyName); | ||
|
||
while (true) | ||
{ | ||
var validKeys = keyItemPage.Where(key => | ||
key.Attributes?.Enabled == true && | ||
key.Attributes?.Expires > DateTime.UtcNow); | ||
|
||
foreach (var keyItem in validKeys) | ||
{ | ||
var keyBundle = await _keyVaultClient.GetKeyAsync(keyItem.Identifier.Identifier); | ||
var key = new RsaSecurityKey(keyBundle.Key.ToRSA()) | ||
{ | ||
KeyId = keyBundle.KeyIdentifier.Version | ||
}; | ||
|
||
validationKeys.Add(new SecurityKeyInfo | ||
{ | ||
Key = key, | ||
SigningAlgorithm = SigningAlgorithm | ||
}); | ||
} | ||
|
||
if (keyItemPage.NextPageLink == null) | ||
{ | ||
break; | ||
} | ||
|
||
keyItemPage = await _keyVaultClient.GetKeyVersionsNextAsync(keyItemPage.NextPageLink); | ||
} | ||
|
||
return validationKeys; | ||
} | ||
} | ||
} |
43 changes: 43 additions & 0 deletions
43
src/Skoruba.IdentityServer4.STS.KeyVault/Tokens/KeyVaultTokenCreationService.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
using System.IdentityModel.Tokens.Jwt; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
using IdentityServer4.Configuration; | ||
using IdentityServer4.Services; | ||
using Microsoft.AspNetCore.Authentication; | ||
using Microsoft.Azure.KeyVault; | ||
using Microsoft.Extensions.Logging; | ||
using Skoruba.IdentityServer4.STS.KeyVault.Configuration; | ||
|
||
namespace Skoruba.IdentityServer4.STS.KeyVault.Tokens | ||
{ | ||
public class KeyVaultTokenCreationService : DefaultTokenCreationService | ||
{ | ||
private readonly AzureKeyVaultConfiguration _configuration; | ||
private readonly IKeyVaultClient _keyVaultClient; | ||
|
||
public KeyVaultTokenCreationService( | ||
AzureKeyVaultConfiguration configuration, | ||
ISystemClock clock, | ||
IKeyMaterialService keys, | ||
IdentityServerOptions options, | ||
ILogger<DefaultTokenCreationService> logger, | ||
IKeyVaultClient keyVaultClient) | ||
: base(clock, keys, options, logger) | ||
{ | ||
_configuration = configuration; | ||
_keyVaultClient = keyVaultClient; | ||
} | ||
|
||
protected override async Task<string> CreateJwtAsync(JwtSecurityToken jwt) | ||
{ | ||
var plaintext = $"{jwt.EncodedHeader}.{jwt.EncodedPayload}"; | ||
|
||
using var hasher = CryptoHelper.GetHashAlgorithmForSigningAlgorithm(jwt.SignatureAlgorithm); | ||
var hash = hasher.ComputeHash(Encoding.UTF8.GetBytes(plaintext)); | ||
|
||
var response = await _keyVaultClient.SignAsync(_configuration.KeyIdentifier, jwt.SignatureAlgorithm, hash); | ||
|
||
return $"{plaintext}.{Base64UrlTextEncoder.Encode(response.Result)}"; | ||
} | ||
} | ||
} |