< Summary

Class:Microsoft.Azure.KeyVault.KeyVaultKeyResolver
Assembly:Microsoft.Azure.KeyVault.Extensions
File(s):C:\Git\azure-sdk-for-net\sdk\keyvault\Microsoft.Azure.KeyVault.Extensions\src\KeyVaultKeyResolver.cs
Covered lines:56
Uncovered lines:15
Coverable lines:71
Total lines:191
Line coverage:78.8% (56 of 71)
Covered branches:19
Total branches:34
Branch coverage:55.8% (19 of 34)

Metrics

MethodCyclomatic complexity Line coverage Branch coverage
.ctor(...)-0%100%
.ctor(...)-100%50%
.ctor(...)-0%100%
.ctor(...)-75%50%
ResolveKeyAsync()-76.92%64.29%
NormalizeVaultName(...)-66.67%50%
ResolveKeyFromKeyAsync(...)-91.67%100%
ResolveKeyFromSecretAsync(...)-94.12%50%
FromBase64UrlString(...)-66.67%50%
Pad(...)-75%50%

File(s)

C:\Git\azure-sdk-for-net\sdk\keyvault\Microsoft.Azure.KeyVault.Extensions\src\KeyVaultKeyResolver.cs

#LineLine coverage
 1// Copyright (c) Microsoft Corporation. All rights reserved.
 2// Licensed under the MIT License. See License.txt in the project root for
 3// license information.
 4
 5using System;
 6using System.Threading;
 7using System.Threading.Tasks;
 8using Microsoft.Azure.KeyVault.Core;
 9
 10namespace Microsoft.Azure.KeyVault
 11{
 12    /// <summary>
 13    /// Azure Key Vault KeyResolver. This class resolves Key Vault Key Identifiers and
 14    /// Secret Identifiers to implementations of IKey. Secret Identifiers can only
 15    /// be resolved if the Secret is a byte array with a length matching one of the AES
 16    /// key lengths (128, 192, 256) and the content-type of the secret is application/octet-stream.
 17    /// </summary>
 18    public class KeyVaultKeyResolver : IKeyResolver
 19    {
 20        private readonly IKeyVaultClient _client;
 21        private readonly string         _name;
 22
 23        /// <summary>
 24        /// Creates a new Key Vault KeyResolver that uses a KeyVaultClient constructed
 25        /// with the provided authentication callback.
 26        /// </summary>
 27        /// <param name="authenticationCallback">Key Vault authentication callback</param>
 28        public KeyVaultKeyResolver( KeyVaultClient.AuthenticationCallback authenticationCallback )
 029            : this( new KeyVaultClient( authenticationCallback ) )
 30        {
 031        }
 32
 33        /// <summary>
 34        /// Create a new Key Vault KeyResolver that uses the specified KeyVaultClient
 35        /// </summary>
 36        /// <param name="client">Key Vault client</param>
 837        public KeyVaultKeyResolver( IKeyVaultClient client )
 38        {
 839            _name = null;
 840            _client = client ?? throw new ArgumentNullException( "client" );
 841        }
 42
 43        /// <summary>
 44        /// Creates a new Key Vault KeyResolver that uses a KeyVaultClient constructed
 45        /// with the provided authentication callback and only resolves keys for the
 46        /// specified key vault
 47        /// </summary>
 48        /// <param name="vaultName">The URL for the Key Vault, e.g. https://myvault.vault.azure.net/ </param>
 49        /// <param name="authenticationCallback">Key Vault authentication callback</param>
 50        public KeyVaultKeyResolver( string vaultName, KeyVaultClient.AuthenticationCallback authenticationCallback )
 051            : this( vaultName, new KeyVaultClient( authenticationCallback ) )
 52        {
 053        }
 54
 55        /// <summary>
 56        /// Creates a new Key Vault KeyResolver that uses the specified KeyVaultClient
 57        /// and only resolves keys for the specified key vault
 58        /// </summary>
 59        /// <param name="vaultName">The URL for the Key Vault, e.g. https://myvault.vault.azure.net/ </param>
 60        /// <param name="client">Key Vault client</param>
 861        public KeyVaultKeyResolver( string vaultName, IKeyVaultClient client )
 62        {
 863            if ( string.IsNullOrWhiteSpace( vaultName ) )
 064                throw new ArgumentNullException( "vaultName" );
 65
 866            if ( client == null )
 067                throw new ArgumentNullException( "client" );
 68
 869            _name   = NormalizeVaultName( vaultName );
 870            _client = client;
 871        }
 72
 73        #region IKeyResolver
 74
 75        /// <summary>
 76        /// Provides an IKey implementation for the specified key or secret identifier.
 77        /// </summary>
 78        /// <param name="kid">The key or secret identifier to resolve</param>
 79        /// <param name="token">Cancellation token</param>
 80        /// <returns>The resolved IKey implementation or null</returns>
 81        public async Task<IKey> ResolveKeyAsync( string kid, CancellationToken token )
 82        {
 3283            if ( string.IsNullOrWhiteSpace( kid ) )
 084                throw new ArgumentNullException( "kid" );
 85
 86            // If the resolver has a name prefix, only handle kid that have that prefix.
 3287            if ( _name != null )
 88            {
 1689                var vaultUrl = new Uri( _name );
 1690                var keyUrl   = new Uri( kid );
 91
 1692                if ( string.Compare( vaultUrl.Scheme, keyUrl.Scheme, true ) != 0 || string.Compare( vaultUrl.Authority, 
 093                    return null;
 94            }
 95
 3296            if ( KeyIdentifier.IsKeyIdentifier( kid ) )
 897                return await ResolveKeyFromKeyAsync( kid, token ).ConfigureAwait( false );
 98
 2499            if ( SecretIdentifier.IsSecretIdentifier( kid ) )
 24100                return await ResolveKeyFromSecretAsync( kid, token ).ConfigureAwait( false );
 101
 102            // Return null rather than throw an exception here
 0103            return null;
 32104        }
 105
 106        #endregion
 107
 108        private string NormalizeVaultName( string vaultName )
 109        {
 8110            Uri vaultUri = new Uri( vaultName, UriKind.Absolute );
 111
 8112            if ( string.Compare(vaultUri.Scheme, "https", true) != 0 )
 0113                throw new ArgumentException( "The vaultName must use the https scheme" );
 114
 8115            if ( string.CompareOrdinal( vaultUri.PathAndQuery, "/" ) != 0 )
 0116                throw new ArgumentException( "The vaultName cannot contain a path or query string" );
 117
 8118            return vaultUri.AbsoluteUri;
 119        }
 120
 121
 122        private Task<IKey> ResolveKeyFromKeyAsync( string kid, CancellationToken token )
 123        {
 124            // KeyVaultClient is thread-safe
 8125            return _client.GetKeyAsync( kid, token )
 8126                .ContinueWith<IKey>( task =>
 8127                {
 16128                    var keyBundle = task.Result;
 8129
 16130                    if ( keyBundle != null )
 8131                    {
 16132                        return new KeyVaultKey( _client, keyBundle );
 8133                    }
 8134
 0135                    return null;
 8136                }, token );
 137        }
 138
 139        private Task<IKey> ResolveKeyFromSecretAsync( string sid, CancellationToken token )
 140        {
 141            // KeyVaultClient is thread-safe
 24142            return _client.GetSecretAsync( sid, token )
 24143                .ContinueWith<IKey>( task =>
 24144                {
 48145                    var secret = task.Result;
 24146
 48147                    if ( secret != null && string.Equals( secret.ContentType, "application/octet-stream", StringComparis
 24148                    {
 48149                        var keyBytes = FromBase64UrlString( secret.Value );
 24150
 48151                        if ( keyBytes != null )
 24152                        {
 48153                            return new SymmetricKey( secret.Id, keyBytes );
 24154                        }
 24155                    }
 24156
 0157                    return null;
 24158                }, token );
 159        }
 160
 161        /// <summary>
 162        /// Converts a Base64 or Base64Url encoded string to a byte array
 163        /// </summary>
 164        /// <param name="input">The Base64Url encoded string</param>
 165        /// <returns>The byte array represented by the enconded string</returns>
 166        private static byte[] FromBase64UrlString( string input )
 167        {
 24168            if ( string.IsNullOrEmpty( input ) )
 0169                throw new ArgumentNullException( "input" );
 170
 24171            return Convert.FromBase64String( Pad( input.Replace( '-', '+' ).Replace( '_', '/' ) ) );
 172        }
 173
 174        /// <summary>
 175        /// Adds padding to the input
 176        /// </summary>
 177        /// <param name="input"> the input string </param>
 178        /// <returns> the padded string </returns>
 179        private static string Pad( string input )
 180        {
 24181            var count = 3 - ( ( input.Length + 3 ) % 4 );
 182
 24183            if ( count == 0 )
 184            {
 24185                return input;
 186            }
 187
 0188            return input + new string( '=', count );
 189        }
 190    }
 191}