< Summary

Class:Microsoft.Azure.KeyVault.WebKey.EllipticCurveHsmKeyVerifier
Assembly:Microsoft.Azure.KeyVault.WebKey
File(s):C:\Git\azure-sdk-for-net\sdk\keyvault\Microsoft.Azure.KeyVault.WebKey\src\JsonWebKeyVerifier.cs
Covered lines:3
Uncovered lines:11
Coverable lines:14
Total lines:1134
Line coverage:21.4% (3 of 14)
Covered branches:0
Total branches:4
Branch coverage:0% (0 of 4)

Metrics

MethodCyclomatic complexity Line coverage Branch coverage
get_Instance()-100%100%
.ctor()-100%100%
get_HasSecretKey()-0%100%
AddUsedProperties(...)-0%100%
IsPrivateKeyComplete(...)-0%100%
IsPrivateKeyValid(...)-0%100%
IsSecretKeyComplete(...)-0%0%
IsSecretKeyValid(...)-0%100%

File(s)

C:\Git\azure-sdk-for-net\sdk\keyvault\Microsoft.Azure.KeyVault.WebKey\src\JsonWebKeyVerifier.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.Collections.Generic;
 7using System.Diagnostics;
 8using System.Text;
 9
 10namespace Microsoft.Azure.KeyVault.WebKey
 11{
 12    /// <summary>
 13    /// A class that verifies instances of <see cref="JsonWebKey"/> according to key type.
 14    /// </summary>
 15    public abstract class JsonWebKeyVerifier
 16    {
 17        /// <summary>
 18        /// Indicates which type of key this verifier applies to.
 19        /// </summary>
 20        /// This is typically a value of <see cref="JsonWebKeyType"/>, though other values are allowed if registered
 21        /// with the <see cref="Register"/> method.
 22        public string Kty { get; }
 23
 24        /// <summary>
 25        /// Initializes a new instance setting the specified value in the <see cref="Kty"/> property.
 26        /// </summary>
 27        /// <param name="kty">Indicates which type of key this verifier applies to.</param>
 28        /// <exception cref="ArgumentNullException">If the specified value is <code>null</code>, empty or whitespace.</e
 29        /// <exception cref="ArgumentException">If the specified value contains invalid characters.</exception>
 30        protected JsonWebKeyVerifier( string kty )
 31        {
 32            if ( string.IsNullOrWhiteSpace( kty ) )
 33                throw new ArgumentNullException( nameof( kty ) );
 34
 35            foreach ( var ch in kty )
 36            {
 37                if ( ch >= '0' && ch <= '9' )
 38                    continue;
 39                if ( ch >= 'A' && ch <= 'Z' )
 40                    continue;
 41                if ( ch >= 'a' && ch <= 'z' )
 42                    continue;
 43                if ( ch == '-' || ch == '.' || ch == '_' )
 44                    continue;
 45
 46                throw new ArgumentException( "Value contains invalid characters.", nameof( kty ) );
 47            }
 48
 49            Kty = kty;
 50        }
 51
 52        /// <summary>
 53        /// Tells if the type of key verified by this object supports public key algorithms.
 54        /// </summary>
 55        /// Note to implementers: if this method returns <code>true</code>, the methods
 56        /// <see cref="IsPublicKeyComplete"/>, <see cref="IsPublicKeyValid"/>, <see cref="IsPrivateKeyComplete"/> and
 57        /// <see cref="IsPrivateKeyValid"/> must be overriden.
 58        public abstract bool IsPublicKeyCrypto { get; }
 59
 60        /// <summary>
 61        /// Tells if the type of key verified by this object supports symmetric key algorithms.
 62        /// </summary>
 63        /// Note to implementers: if this method returns <code>true</code>, the methods
 64        /// <see cref="IsSymmetricKeyComplete"/> and <see cref="IsSymmetricKeyValid"/> must be overriden.
 65        public abstract bool IsSymmetricKeyCrypto { get; }
 66
 67        /// <summary>
 68        /// Tells if the type of key verified by this object contains a secret component, such as a hardware key token.
 69        /// </summary>
 70        /// Note to implementers: if this method returns <code>true</code>, the methods
 71        /// <see cref="IsSecretKeyComplete"/> and <see cref="IsSecretKeyValid"/> must be overriden.
 72        public abstract bool HasSecretKey { get; }
 73
 74        /// <summary>
 75        /// Determines if the specified <see cref="JsonWebKey"/> instance contains values at properties that represent t
 76        /// </summary>
 77        /// <para>If all required public key properties (for the key type) are specified in the instance, the method mus
 78        /// <code>true</code> and not modify the <paramref name="missingProps"/> parameter.</para>
 79        /// <para>If some public key property is missing, the method must return <code>false</code> and set <paramref na
 80        /// with a value - typically a <see cref="List{T}"/> - containing all missing properties.</para>
 81        /// <param name="webKey">The instance to verify.</param>
 82        /// <param name="missingProps">A reference to a variable that tells the list of missing properties. Callers must
 83        /// set the variable to <code>null</code>, and examine the value only if this method returns <code>false</code>.
 84        public virtual bool IsPublicKeyComplete( JsonWebKey webKey, ref ICollection<string> missingProps )
 85        {
 86            throw ThrowDefaultForPublicKeyCrypto( nameof( IsPublicKeyComplete ) );
 87        }
 88
 89        /// <summary>
 90        /// Determines if the specified <see cref="JsonWebKey"/> instance contains a possibly valid public key (see rema
 91        /// </summary>
 92        /// <para>Because fully validating a key may require unfeasable amount of resources, this method only has to che
 93        /// for obvious issues. As a guideline, we say that the code only verifies obvious issues if it runs in constant
 94        /// It's perfectly valid for implementors to do nothing and simply return <code>true</code>.</para>
 95        /// <para>This method assumes that <see cref="IsPublicKeyComplete"/> was called and returned <code>true</code>. 
 96        /// doesn't test again for the presence of required properties. It may throw <see cref="NullReferenceException"/
 97        /// if the caller doesn't see <see cref="IsPublicKeyComplete"/> returning <code>true</code> first.</para>
 98        /// <para>If the valiation code finds no issue, this method must return <code>true</code> without modifying the 
 99        /// <paramref name="errorMsg"/>.</para>
 100        /// <para>If some issue is found, this method must return <code>false</code> and tell more details in the
 101        /// <paramref name="errorMsg"/> parameter.</para>
 102        /// <param name="webKey">The instance to verify.</param>
 103        /// <param name="errorMsg">A reference to a variable that will contain an error message. Callers must
 104        /// set the variable to <code>null</code>, and examine the value only if this method returns <code>false</code>.
 105        public virtual bool IsPublicKeyValid( JsonWebKey webKey, ref string errorMsg )
 106        {
 107            throw ThrowDefaultForPublicKeyCrypto( nameof( IsPublicKeyValid ) );
 108        }
 109
 110        /// <summary>
 111        /// Same as <see cref="IsPublicKeyComplete"/>, but for the private key.
 112        /// </summary>
 113        public virtual bool IsPrivateKeyComplete( JsonWebKey webKey, ref ICollection<string> missingProps )
 114        {
 115            throw ThrowDefaultForPublicKeyCrypto( nameof( IsPrivateKeyComplete ) );
 116        }
 117
 118        /// <summary>
 119        /// Same as <see cref="IsPublicKeyValid"/>, but for the private key.
 120        /// </summary>
 121        public virtual bool IsPrivateKeyValid( JsonWebKey webKey, ref string errorMsg )
 122        {
 123            throw ThrowDefaultForPublicKeyCrypto( nameof( IsPrivateKeyValid ) );
 124        }
 125
 126        /// <summary>
 127        /// Determines if the specified <see cref="JsonWebKey"/> instance contains values in one or more properties that
 128        /// represent the private key.
 129        /// </summary>
 130        /// <para>This method is used to protect private key material from accidental leakage.</para>
 131        /// <para>If no private key property (for the key type) is specified in the instance, the method must return
 132        /// <code>false</code> and not modify the <paramref name="specifiedProps"/> parameter.</para>
 133        /// <para>If one or more private key property is specified, the method must return <code>true</code> and optiona
 134        /// set <paramref name="specifiedProps"/> with a value - typically a <see cref="List{T}"/> - containing the spec
 135        /// <param name="webKey">The instance to verify.</param>
 136        /// <param name="specifiedProps">A reference to a variable that tells the list of specified properties. Callers 
 137        /// set the variable to <code>null</code> and examine the value only if this method returns <code>true</code>.</
 138        /// <returns><code>true</code> if a value is found in at least one property that describe the private key;
 139        /// <code>false</code> otherwise.</returns>
 140        public virtual bool IsAnyPrivateKeyParamSpecified( JsonWebKey webKey, ref ICollection<string> specifiedProps )
 141        {
 142            throw ThrowDefaultForPublicKeyCrypto( nameof( IsAnyPrivateKeyParamSpecified ) );
 143        }
 144
 145        private Exception ThrowDefaultForPublicKeyCrypto( string methodName )
 146        {
 147            if ( IsPublicKeyCrypto )
 148                throw new NotImplementedException( $"Type {GetType().Name} is a bad implementation. If {nameof( IsPublic
 149
 150            throw new InvalidOperationException( $"Type {GetType().Name} is not intended for public key cryptography." )
 151        }
 152
 153        /// <summary>
 154        /// Determines if the specified <see cref="JsonWebKey"/> instance contains values at properties that represent t
 155        /// </summary>
 156        /// <para>If all required symmetric key properties (for the key type) are specified in the instance, the method 
 157        /// <code>true</code> and not modify the <paramref name="missingProps"/> parameter.</para>
 158        /// <para>If some property is missing, the method must return <code>false</code> and set <paramref name="missing
 159        /// to a value - typically a <see cref="List{T}"/> - containing all missing properties.</para>
 160        /// <param name="webKey">The instance to verify.</param>
 161        /// <param name="missingProps">A reference to a variable that tells the list of missing properties. Callers must
 162        /// set the variable to <code>null</code>, and examine the value only if this method returns <code>false</code>.
 163        public virtual bool IsSymmetricKeyComplete( JsonWebKey webKey, ref ICollection<string> missingProps )
 164        {
 165            throw ThrowDefaultForSymmetricKeyCrypto( nameof( IsSymmetricKeyComplete ) );
 166        }
 167
 168        /// <summary>
 169        /// Determines if the specified <see cref="JsonWebKey"/> instance contains a possibly valid symmetric key (see r
 170        /// </summary>
 171        /// <para>Because fully validating a key may require unfeasable amount of resources, this method only has to che
 172        /// for obvious issues. As a guideline, we say that the code only verifies obvious issues if it runs in constant
 173        /// It's perfectly valid for implementors to do nothing and simply return <code>true</code>.</para>
 174        /// <para>This method assumes that <see cref="IsSymmetricKeyComplete"/> was called and returned <code>true</code
 175        /// doesn't test again for the presence of required properties. It may throw <see cref="NullReferenceException"/
 176        /// if the caller doesn't see <see cref="IsSymmetricKeyComplete"/> returning <code>true</code> first.</para>
 177        /// <para>If the valiation code finds no issue, this method must return <code>true</code> without modifying the 
 178        /// <paramref name="errorMsg"/>.</para>
 179        /// <para>If some issue is found, this method must return <code>false</code> and tell more details in the
 180        /// <paramref name="errorMsg"/> parameter.</para>
 181        /// <param name="webKey">The instance to verify.</param>
 182        /// <param name="errorMsg">A reference to a variable that will contain an error message. Callers must
 183        /// set the variable to <code>null</code>, and examine the value only if this method returns <code>false</code>.
 184        public virtual bool IsSymmetricKeyValid( JsonWebKey webKey, ref string errorMsg )
 185        {
 186            throw ThrowDefaultForSymmetricKeyCrypto( nameof( IsSymmetricKeyValid ) );
 187        }
 188
 189        private Exception ThrowDefaultForSymmetricKeyCrypto( string methodName )
 190        {
 191            if ( IsSymmetricKeyCrypto )
 192                throw new NotImplementedException( $"Type {GetType().Name} is a bad implementation. If {nameof( IsSymmet
 193
 194            throw new InvalidOperationException( $"Type {GetType().Name} is not intended for symmetric key cryptography.
 195        }
 196
 197        /// <summary>
 198        /// Determines if the specified <see cref="JsonWebKey"/> instance contains values at properties that represent t
 199        /// </summary>
 200        /// <para>If all required secret key properties (for the key type) are specified in the instance, the method mus
 201        /// <code>true</code> and not modify the <paramref name="missingProps"/> parameter.</para>
 202        /// <para>If some property is missing, the method must return <code>false</code> and set <paramref name="missing
 203        /// to a value - typically a <see cref="List{T}"/> - containing all missing properties.</para>
 204        /// <param name="webKey">The instance to verify.</param>
 205        /// <param name="missingProps">A reference to a variable that tells the list of missing properties. Callers must
 206        /// set the variable to <code>null</code>, and examine the value only if this method returns <code>false</code>.
 207        public virtual bool IsSecretKeyComplete( JsonWebKey webKey, ref ICollection<string> missingProps )
 208        {
 209            throw ThrowDefaultForSecretKeyCrypto( nameof( IsSecretKeyComplete ) );
 210        }
 211
 212        /// <summary>
 213        /// Determines if the specified <see cref="JsonWebKey"/> instance contains a possibly valid secret key (see rema
 214        /// </summary>
 215        /// <para>Because fully validating a key may require unfeasable amount of resources, this method only has to che
 216        /// for obvious issues. As a guideline, we say that the code only verifies obvious issues if it runs in constant
 217        /// It's perfectly valid for implementors to do nothing and simply return <code>true</code>.</para>
 218        /// <para>This method assumes that <see cref="IsSecretKeyComplete"/> was called and returned <code>true</code>. 
 219        /// doesn't test again for the presence of required properties. It may throw <see cref="NullReferenceException"/
 220        /// if the caller doesn't see <see cref="IsSecretKeyComplete"/> returning <code>true</code> first.</para>
 221        /// <para>If the valiation code finds no issue, this method must return <code>true</code> without modifying the 
 222        /// <paramref name="errorMsg"/>.</para>
 223        /// <para>If some issue is found, this method must return <code>false</code> and tell more details in the
 224        /// <paramref name="errorMsg"/> parameter.</para>
 225        /// <param name="webKey">The instance to verify.</param>
 226        /// <param name="errorMsg">A reference to a variable that will contain an error message. Callers must
 227        /// set the variable to <code>null</code>, and examine the value only if this method returns <code>false</code>.
 228        public virtual bool IsSecretKeyValid( JsonWebKey webKey, ref string errorMsg )
 229        {
 230            throw ThrowDefaultForSecretKeyCrypto( nameof( IsSecretKeyValid ) );
 231        }
 232
 233        private Exception ThrowDefaultForSecretKeyCrypto( string methodName )
 234        {
 235            if ( HasSecretKey )
 236                throw new NotImplementedException( $"Type {GetType().Name} is a bad implementation. If {nameof( HasSecre
 237
 238            throw new InvalidOperationException( $"Type {GetType().Name} is not intended to keys that have a secret comp
 239        }
 240
 241        private static volatile SortedSet<string> _validOperations;
 242
 243        public static bool IsOperationValid( string opName )
 244        {
 245            if ( _validOperations == null )
 246            {
 247                // No lock here; we accept multiple threads initializing.
 248                var validOperations = new SortedSet<string>();
 249
 250                foreach ( var op in JsonWebKeyOperation.AllOperations )
 251                    validOperations.Add( op );
 252
 253                _validOperations = validOperations;
 254            }
 255
 256            // No lock here; collection is read-only at this time.
 257            return _validOperations.Contains( opName );
 258        }
 259
 260        private volatile SortedSet<string> _compatibleOperations;
 261
 262        public bool IsOperationCompatible( string opName )
 263        {
 264            if ( _compatibleOperations == null )
 265            {
 266                // No lock here; we accept multiple threads initializing.
 267                var compatibleOperations = new SortedSet<string>();
 268
 269                AddCompatibleOperations( compatibleOperations );
 270
 271                _compatibleOperations = compatibleOperations;
 272            }
 273
 274            // No lock here; collection is read-only at this time.
 275            return _compatibleOperations.Contains( opName );
 276        }
 277
 278        /// <summary>
 279        /// Adds to the specified collection all operations that can be performed with keys whose type is handled by
 280        /// this object.
 281        /// </summary>
 282        /// For instance, if keys can only be used for digital signatures, this method should add only
 283        /// <see cref="JsonWebKeyOperation.Sign"/> and <see cref="JsonWebKeyOperation.Verify"/>.
 284        protected abstract void AddCompatibleOperations( ICollection<string> compatibleOperations );
 285
 286        private HashSet<string> _usedProperties;
 287
 288        public bool IsPropertyUsed( string propName )
 289        {
 290            if ( _usedProperties == null )
 291            {
 292                // No lock here; we accept multiple threads initializing.
 293                var usedProperties = new HashSet<string>();
 294
 295                AddUsedProperties( usedProperties );
 296
 297                usedProperties.Add( JsonWebKey.Property_Kid );
 298                usedProperties.Add( JsonWebKey.Property_Kty );
 299                usedProperties.Add( JsonWebKey.Property_KeyOps );
 300
 301                _usedProperties = usedProperties;
 302            }
 303
 304            // No lock here; collection is read-only at this point.
 305            return _usedProperties.Contains( propName );
 306        }
 307
 308        /// <summary>
 309        /// Adds to the specified collection all JsonWebKey properties that are useful to keys whose type is handled by
 310        /// this object.
 311        /// </summary>
 312        /// <para>This method must add JSON property names, such as <code>"crv"</code>, <code>"p"</code>, etc. It must n
 313        /// C# property names.</para>
 314        /// <para>This method doesn't have to add <code>"kid"</code>, <code>"kty"</code> and <code>"key_ops"</code>.
 315        /// Thes properties are assumed to be useful to all keys.</para>.
 316        protected abstract void AddUsedProperties( ICollection<string> usedProperties );
 317
 318        [Flags]
 319        public enum Options
 320        {
 321            /// <summary>
 322            /// Use this value if you don't want to specify any other.
 323            /// </summary>
 324            None = 0,
 325
 326            /// <summary>
 327            /// Fails if any private key material is present. Use this to defend against leakage.
 328            /// </summary>
 329            /// This value is only used for keys that support public key cryptography. It's ignored in other key types.
 330            DenyPrivateKey /*********/ = 1 << 0,
 331
 332            /// <summary>
 333            /// Fails if private key material is not fully present. Use this before storing or importing a JsonWebKey
 334            /// value into a subsystem that needs to keep the private key.
 335            /// </summary>
 336            /// This value is only used for keys that support public key cryptography. It's ignored in other key types.
 337            RequirePrivateKey /******/ = 1 << 1,
 338
 339            /// <summary>
 340            /// Fails if there the <code>"key_ops"</code> value of the verified key contains an incompatible operation.
 341            /// </summary>
 342            DenyIncompatibleOperations = 1 << 2,
 343
 344            /// <summary>
 345            /// Fails if the JsonWebKey object describes values at properties that are not used by the corresponding key
 346            /// Use this to defend against properties incorrectly set, and also some forms of leakage.
 347            /// </summary>
 348            DenyExtraneousFields /***/ = 1 << 3,
 349
 350            /// <summary>
 351            /// Reserved for future use.
 352            /// </summary>
 353            VerifyDecrypt /**********/ = 1 << 10,
 354
 355            /// <summary>
 356            /// Reserved for future use.
 357            /// </summary>
 358            VerifySign /*************/ = 1 << 11,
 359
 360            /// <summary>
 361            /// Do not return <code>false</code> if the verification fails; throws an exception instead.
 362            /// </summary>
 363            ThrowException /*********/ = 1 << 20
 364        }
 365
 366        /// <summary>
 367        /// Verifies the specified JsonWebKey instance.
 368        /// </summary>
 369        /// <para>Verification first examines the <see cref="JsonWebKey.Kty"/> property to select a verifier instance
 370        /// (for more information, see the <see cref="Register"/> method). If a verifier is found, it's used to check
 371        /// if the key conforms to the corresponding key type.</para>
 372        /// <param name="webKey">The instance to verify.</param>
 373        /// <param name="options">Tells how verification is to behave.</param>
 374        /// <param name="error">A reference to a variable that will tell the error message, if verification fails. This
 375        /// is only set if the method returns <code>false</code>. If the method returns <code>true</code> or throws
 376        /// an exception, the <paramref name="error"/> will not me modified.</param>
 377        /// <returns><code>true</code> if the JsonWebKey value is valid, <code>false otherwise</code>.</returns>
 378        /// <exception cref="ArgumentNullException">If the <paramref name="webKey"/> parameter is null.</exception>
 379        /// <exception cref="ArgumentException">If the <paramref name="options"/> parameter contains invalid options.</e
 380        /// <exception cref="JsonWebKeyVerificationException">If the JsonWebKey object is invalid and the option
 381        ///     <see cref="Options.ThrowException"/> was specified.</exception>
 382        /// <seealso cref="Options"/>
 383        public bool Verify( JsonWebKey webKey, Options options, ref string error )
 384        {
 385            if ( webKey == null )
 386                throw new ArgumentNullException( nameof( webKey ) );
 387
 388            if ( options.HasFlag( Options.DenyPrivateKey ) && options.HasFlag( Options.RequirePrivateKey ) )
 389                throw new ArgumentException( $"Cannot use {Options.DenyPrivateKey} and {Options.RequirePrivateKey} at sa
 390
 391            if ( options.HasFlag( Options.VerifyDecrypt ) || options.HasFlag( Options.VerifySign ) )
 392                throw new NotImplementedException();
 393
 394            if ( !VerifyNotNullOrWhiteSpace( webKey.Kty, JsonWebKey.Property_Kty, options, ref error ) )
 395                return false;
 396
 397            if ( webKey.Kty != Kty )
 398                return SetError( options, ref error, $"Expected {JsonWebKey.Property_Kty} to be \"{Kty}\", but found som
 399
 400            if ( IsPublicKeyCrypto )
 401            {
 402                if ( !VerifyPublicKey( this, webKey, options, ref error ) )
 403                    return false;
 404
 405                if ( options.HasFlag( Options.DenyPrivateKey ) && !VerifyNoPrivateKey( this, webKey, options, ref error 
 406                    return false;
 407
 408                if ( options.HasFlag( Options.RequirePrivateKey ) && !VerifyPrivateKey( this, webKey, options, ref error
 409                    return false;
 410            }
 411
 412            if ( IsSymmetricKeyCrypto )
 413            {
 414                if ( !VerifySymmetricKey( this, webKey, options, ref error ) )
 415                    return false;
 416            }
 417
 418            if ( HasSecretKey && !VerifySecretKey( this, webKey, options, ref error ) )
 419                return false;
 420
 421            if ( options.HasFlag( Options.DenyIncompatibleOperations ) && !VerifyOperationsAreCompatible( this, webKey, 
 422                return false;
 423
 424            if ( !options.HasFlag( Options.DenyIncompatibleOperations ) && !VerifyOperationsAreValid( webKey, options, r
 425                return false;
 426
 427            if ( options.HasFlag( Options.DenyExtraneousFields ) && !VerifyNoExtraneousFields( this, webKey, options, re
 428                return false;
 429
 430            return true;
 431        }
 432
 433        /// <summary>
 434        /// Verifies the specified JsonWebKey instance according to <see cref="JsonWebKey.Kty"/>.
 435        /// </summary>
 436        /// This method selects a verifier based on the value of <see cref="JsonWebKey.Kty"/>,
 437        /// then calls the verifier's <see cref="Verify"/> method.
 438        /// <param name="webKey"></param>
 439        /// <param name="options"></param>
 440        /// <param name="error"></param>
 441        /// <seealso cref="Verify"/>
 442        public static bool VerifyByKeyType( JsonWebKey webKey, Options options, ref string error )
 443        {
 444            if ( webKey == null )
 445                throw new ArgumentNullException( nameof( webKey ) );
 446
 447            if ( !VerifyNotNullOrWhiteSpace( webKey.Kty, JsonWebKey.Property_Kty, options, ref error ) )
 448                return false;
 449
 450            JsonWebKeyVerifier verifier;
 451
 452            lock ( _verifiersLock )
 453            {
 454                InitVerifiers();
 455                if ( !VerifyEnum( webKey.Kty, JsonWebKey.Property_Kty, _verifiers, out verifier, options, ref error ) )
 456                    return false;
 457            }
 458
 459            return verifier.Verify( webKey, options, ref error );
 460        }
 461
 462        private static readonly object _verifiersLock = new object();
 463
 464        private static volatile SortedDictionary<string, JsonWebKeyVerifier> _verifiers;
 465
 466        private static void InitVerifiers()
 467        {
 468            if ( _verifiers != null )
 469                return;
 470
 471            var verifiers = new SortedDictionary<string, JsonWebKeyVerifier>();
 472
 473            RegisterAt( verifiers, OctetKeyVerifier.Instance );
 474            RegisterAt( verifiers, EllipticCurveKeyVerifier.Instance );
 475            RegisterAt( verifiers, EllipticCurveHsmKeyVerifier.Instance );
 476            RegisterAt( verifiers, RsaKeyVerifier.Instance );
 477            RegisterAt( verifiers, RsaHsmKeyVerifier.Instance );
 478
 479            _verifiers = verifiers;
 480        }
 481
 482        /// <summary>
 483        /// Registers a verifier for a <see cref="Kty"/> value.
 484        /// </summary>
 485        /// Throws exception is a previous verifier for same <see cref="Kty"/> value is already registered.
 486        /// There is no need to register verifiers for values described on <see cref="JsonWebKeyType"/>.
 487        /// <param name="verifier">The verifier to register.</param>
 488        /// <seealso cref="GetVerifier"/>
 489        public static void Register( JsonWebKeyVerifier verifier )
 490        {
 491            if ( verifier == null )
 492                throw new ArgumentNullException( nameof( verifier ) );
 493
 494            lock ( _verifiersLock )
 495            {
 496                InitVerifiers();
 497                RegisterAt( _verifiers, verifier );
 498            }
 499        }
 500
 501        private static void RegisterAt( IDictionary<string, JsonWebKeyVerifier> verifiers, JsonWebKeyVerifier verifier )
 502        {
 503            Debug.Assert( verifiers != null );
 504
 505            if ( verifiers.TryGetValue( verifier.Kty, out JsonWebKeyVerifier _ ) )
 506                throw new InvalidOperationException( $"Value already registered for \"{verifier.Kty}\"." );
 507
 508            verifiers[verifier.Kty] = verifier;
 509        }
 510
 511        /// <summary>
 512        /// Returns the verifier registered for the specified kty value, or null if the kty value was not registered.
 513        /// </summary>
 514        /// This method never returns null for values described on <see cref="JsonWebKeyType"/>.
 515        /// <seealso cref="Register"/>
 516        public static JsonWebKeyVerifier GetVerifier( string kty )
 517        {
 518            if ( string.IsNullOrWhiteSpace( kty ) )
 519                throw new ArgumentNullException( nameof( kty ) );
 520
 521            lock ( _verifiersLock )
 522            {
 523                InitVerifiers();
 524                _verifiers.TryGetValue( kty, out JsonWebKeyVerifier result );
 525                return result;
 526            }
 527        }
 528
 529        private static bool VerifyPublicKey( JsonWebKeyVerifier verifier, JsonWebKey webKey, Options options, ref string
 530        {
 531            return VerifyKeyParameters(
 532                "public",
 533                verifier.IsPublicKeyComplete,
 534                verifier.IsPublicKeyValid,
 535                webKey,
 536                options,
 537                ref error );
 538        }
 539
 540        private static bool VerifyNoPrivateKey( JsonWebKeyVerifier verifier, JsonWebKey webKey, Options options, ref str
 541        {
 542            ICollection<string> specifiedProps = null;
 543            if ( !verifier.IsAnyPrivateKeyParamSpecified( webKey, ref specifiedProps ) )
 544                return true;
 545
 546            if ( specifiedProps == null || specifiedProps.Count == 0 )
 547                return SetError( options, ref error, "Private key parameters must not be specified." );
 548
 549            return SetError( options, ref error, $"Private key parameters must not be specified: {SurroundWithQuotes( sp
 550        }
 551
 552        private static bool VerifyPrivateKey( JsonWebKeyVerifier verifier, JsonWebKey webKey, Options options, ref strin
 553        {
 554            return VerifyKeyParameters(
 555                "private",
 556                verifier.IsPrivateKeyComplete,
 557                verifier.IsPrivateKeyValid,
 558                webKey,
 559                options,
 560                ref error );
 561        }
 562
 563        private static bool VerifySymmetricKey( JsonWebKeyVerifier verifier, JsonWebKey webKey, Options options, ref str
 564        {
 565            return VerifyKeyParameters(
 566                "symmetric",
 567                verifier.IsSymmetricKeyComplete,
 568                verifier.IsSymmetricKeyValid,
 569                webKey,
 570                options,
 571                ref error );
 572        }
 573
 574        private static bool VerifySecretKey( JsonWebKeyVerifier verifier, JsonWebKey webKey, Options options, ref string
 575        {
 576            return VerifyKeyParameters(
 577                "secret",
 578                verifier.IsSecretKeyComplete,
 579                verifier.IsSecretKeyValid,
 580                webKey,
 581                options,
 582                ref error );
 583        }
 584
 585        private delegate bool IsCompleteProc( JsonWebKey webKey, ref ICollection<string> missingProps );
 586
 587        private delegate bool IsValidProc( JsonWebKey webKey, ref string errorMsg );
 588
 589        private static bool VerifyKeyParameters( string name, IsCompleteProc isCompleteProc, IsValidProc isValidProc, Js
 590        {
 591            ICollection<string> missingParams = null;
 592            if ( !isCompleteProc( webKey, ref missingParams ) )
 593            {
 594                if ( missingParams == null || missingParams.Count == 0 )
 595                    return SetError( options, ref error, $"Missing {name} key parameters." );
 596
 597                return SetError( options, ref error, $"Missing {name} key parameters: {SurroundWithQuotes( missingParams
 598            }
 599
 600            string errorMsg = null;
 601            if ( !isValidProc( webKey, ref errorMsg ) )
 602            {
 603                if ( errorMsg != null )
 604                    errorMsg = $"Invalid {name} key parameters: {errorMsg}";
 605                else
 606                    errorMsg = $"Invalid {name} key parameters.";
 607
 608                return SetError( options, ref error, errorMsg );
 609            }
 610
 611            return true;
 612        }
 613
 614        private static bool VerifyOperationsAreValid( JsonWebKey webKey, Options options, ref string error )
 615        {
 616            if ( webKey.KeyOps == null )
 617                return true;
 618
 619            StringBuilder invalid = null;
 620
 621            foreach ( var keyOp in webKey.KeyOps )
 622                if ( !IsOperationValid( keyOp ) )
 623                {
 624                    if ( invalid == null )
 625                        invalid = new StringBuilder();
 626                    else
 627                        invalid.Append( ", " );
 628
 629                    invalid.Append( '"' ).Append( keyOp ).Append( '"' );
 630                }
 631
 632            if ( invalid != null )
 633            {
 634                var validOps = SurroundWithQuotes( _validOperations );
 635                return SetError( options, ref error, $"Found invalid operations: {invalid}. Valid operations are: {valid
 636            }
 637
 638            return true;
 639        }
 640
 641        private static bool VerifyOperationsAreCompatible( JsonWebKeyVerifier verifier, JsonWebKey webKey, Options optio
 642        {
 643            if ( webKey.KeyOps == null )
 644                return true;
 645
 646            StringBuilder incompatible = null;
 647
 648            foreach ( var keyOp in webKey.KeyOps )
 649                if ( !verifier.IsOperationCompatible( keyOp ) )
 650                {
 651                    if ( incompatible == null )
 652                        incompatible = new StringBuilder();
 653                    else
 654                        incompatible.Append( ", " );
 655
 656                    incompatible.Append( '"' ).Append( keyOp ).Append( '"' );
 657                }
 658
 659            if ( incompatible != null )
 660            {
 661                var compatibleOps = SurroundWithQuotes( verifier._compatibleOperations );
 662                return SetError( options, ref error, $"Found invalid or incompatible operations: {incompatible}. Valid a
 663            }
 664
 665            return true;
 666        }
 667
 668        private static bool VerifyNoExtraneousFields( JsonWebKeyVerifier verifier, JsonWebKey webKey, Options options, r
 669        {
 670            StringBuilder extraneous = null;
 671
 672            void VerifyProperty( string name, object value )
 673            {
 674                if ( value == null || verifier.IsPropertyUsed( name ) )
 675                    return;
 676
 677                if ( extraneous == null )
 678                    extraneous = new StringBuilder();
 679                else
 680                    extraneous.Append( ", " );
 681
 682                extraneous.Append( '"' ).Append( name ).Append( '"' );
 683            }
 684
 685            webKey.VisitProperties( VerifyProperty );
 686
 687            if ( extraneous != null )
 688                return SetError( options, ref error, $"Extraneous properties: {extraneous}" );
 689
 690            return true;
 691        }
 692
 693        private static bool VerifyEnum<T>( string value, string name, IDictionary<string, T> dictionary, out T item, Opt
 694        {
 695            if ( !dictionary.TryGetValue( value, out item ) )
 696                return SetError( options, ref error, $"Expected {name} to be one of {SurroundWithQuotes( dictionary.Keys
 697
 698            if ( item == null )
 699                throw new InvalidOperationException( $"Dictionary for {name} is returning null for \"{value}\"." );
 700
 701            return true;
 702        }
 703
 704        private static bool VerifyNotNullOrWhiteSpace( string value, string name, Options options, ref string error )
 705        {
 706            if ( string.IsNullOrWhiteSpace( value ) )
 707                return SetError( options, ref error, $"A value for {name} is mandatory." );
 708
 709            return true;
 710        }
 711
 712        private static bool SetError( Options options, ref string error, string message )
 713        {
 714            if ( options.HasFlag( Options.ThrowException ) )
 715                throw new JsonWebKeyVerificationException( message );
 716            error = message;
 717            return false;
 718        }
 719
 720        /// <summary>
 721        /// Helper method that surrounds string values with double-quotes.
 722        /// </summary>
 723        /// For instance, the strings Foo, Bar cause this method to return <code>"Foo", "Bar"</code>.
 724        protected static string SurroundWithQuotes( ICollection<string> items )
 725        {
 726            return "\"" + string.Join( "\", \"", items ) + "\"";
 727        }
 728
 729        /// <summary>
 730        /// Helper method that joins the operation of creating a collection (if required) and adding an item to it.
 731        /// </summary>
 732        /// If the collection is null, this method creates one of type <see cref="List{T}"/>. Then it adds the specified
 733        protected static void AddItem<T>( ref ICollection<T> items, T newItem )
 734        {
 735            if ( items == null )
 736                items = new List<T>();
 737            items.Add( newItem );
 738        }
 739
 740        /// <summary>
 741        /// Helper method that validates the size of a byte array.
 742        /// </summary>
 743        /// <para>A valid array meets the following criteria:</para>
 744        /// <list type="bullet">
 745        /// <item><description>is not <code>null</code>;</description></item>
 746        /// <item><description>the length is at least <paramref name="requiredSize"/>; and</description></item>
 747        /// <item><description>excess leading bytes are all zeros.</description></item>
 748        /// </list>
 749        /// <param name="value">The array to validate.</param>
 750        /// <param name="name">The array name, which may be used to build error messages.</param>
 751        /// <param name="requiredSize">The required size, in bytes.</param>
 752        /// <param name="errorMsg">A reference to a variable that will contain the error message. This is only set
 753        /// if the method returns <code>false</code>.</param>
 754        /// <returns><code>true</code> if the array has a valid size; <code>false otherwise</code>.</returns>
 755        protected static bool ValidateKeyParameterSize( byte[] value, string name, int requiredSize, ref string errorMsg
 756        {
 757            if ( value == null || value.Length < requiredSize )
 758            {
 759                var sizeDesc = value == null ? "null" : value.Length.ToString();
 760                errorMsg = $"Expected {name} to have at least {requiredSize} bytes, but found {sizeDesc}.";
 761                return false;
 762            }
 763
 764            var excess = value.Length - requiredSize;
 765            for ( var i = 0; i < excess; ++i )
 766            {
 767                if ( value[i] != 0 )
 768                    errorMsg = $"Expected {name} to have at most {requiredSize} bytes, but found {requiredSize + excess}
 769                --excess;
 770            }
 771
 772            return true;
 773        }
 774    }
 775
 776    internal sealed class OctetKeyVerifier : JsonWebKeyVerifier
 777    {
 778        public static JsonWebKeyVerifier Instance { get; } = new OctetKeyVerifier();
 779
 780        private OctetKeyVerifier() : base( JsonWebKeyType.Octet )
 781        {
 782        }
 783
 784        public override bool IsPublicKeyCrypto => false;
 785        public override bool IsSymmetricKeyCrypto => true;
 786        public override bool HasSecretKey => false;
 787
 788        protected override void AddCompatibleOperations( ICollection<string> compatibleOperations )
 789        {
 790            compatibleOperations.Add( JsonWebKeyOperation.Encrypt );
 791            compatibleOperations.Add( JsonWebKeyOperation.Decrypt );
 792            compatibleOperations.Add( JsonWebKeyOperation.Wrap );
 793            compatibleOperations.Add( JsonWebKeyOperation.Unwrap );
 794        }
 795
 796        protected override void AddUsedProperties( ICollection<string> usedProperties )
 797        {
 798            usedProperties.Add( JsonWebKey.Property_K );
 799        }
 800
 801        public override bool IsSymmetricKeyComplete( JsonWebKey arg, ref ICollection<string> missingProps )
 802        {
 803            if ( arg.K == null || arg.K.Length == 0 )
 804            {
 805                AddItem( ref missingProps, JsonWebKey.Property_K );
 806                return false;
 807            }
 808
 809            return true;
 810        }
 811
 812        public override bool IsSymmetricKeyValid( JsonWebKey arg, ref string errorMsg )
 813        {
 814            return true;
 815        }
 816    }
 817
 818    internal abstract class EllipticCurveKeyVerifierBase : JsonWebKeyVerifier
 819    {
 820        protected EllipticCurveKeyVerifierBase( string kty ) : base( kty )
 821        {
 822        }
 823
 824        public override bool IsPublicKeyCrypto => true;
 825        public override bool IsSymmetricKeyCrypto => false;
 826
 827        protected override void AddCompatibleOperations( ICollection<string> compatibleOperations )
 828        {
 829            compatibleOperations.Add( JsonWebKeyOperation.Sign );
 830            compatibleOperations.Add( JsonWebKeyOperation.Verify );
 831        }
 832
 833        protected override void AddUsedProperties( ICollection<string> usedProperties )
 834        {
 835            usedProperties.Add( JsonWebKey.Property_Crv );
 836            usedProperties.Add( JsonWebKey.Property_X );
 837            usedProperties.Add( JsonWebKey.Property_Y );
 838        }
 839
 840        public override bool IsPublicKeyComplete( JsonWebKey webKey, ref ICollection<string> missingProps )
 841        {
 842            var result = true;
 843
 844            if ( string.IsNullOrWhiteSpace( webKey.CurveName ) )
 845            {
 846                AddItem( ref missingProps, JsonWebKey.Property_Crv );
 847                result = false;
 848            }
 849
 850            if ( webKey.X == null || webKey.X.Length == 0 )
 851            {
 852                AddItem( ref missingProps, JsonWebKey.Property_X );
 853                result = false;
 854            }
 855
 856            if ( webKey.Y == null || webKey.Y.Length == 0 )
 857            {
 858                AddItem( ref missingProps, JsonWebKey.Property_Y );
 859                result = false;
 860            }
 861
 862            return result;
 863        }
 864
 865        public override bool IsPublicKeyValid( JsonWebKey webKey, ref string errorMsg )
 866        {
 867            var requiredSize = JsonWebKeyCurveName.GetKeyParameterSize( webKey.CurveName );
 868
 869            if ( requiredSize < 0 )
 870            {
 871                errorMsg = $"Unsupported curve: \"{webKey.CurveName}\". Supported curves are: {SurroundWithQuotes( JsonW
 872                return false;
 873            }
 874
 875            if ( !ValidateKeyParameterSize( webKey.X, nameof( webKey.X ), requiredSize, ref errorMsg ) )
 876                return false;
 877
 878            if ( !ValidateKeyParameterSize( webKey.Y, nameof( webKey.Y ), requiredSize, ref errorMsg ) )
 879                return false;
 880
 881            return true;
 882        }
 883    }
 884
 885    internal sealed class EllipticCurveKeyVerifier : EllipticCurveKeyVerifierBase
 886    {
 887        public static JsonWebKeyVerifier Instance { get; } = new EllipticCurveKeyVerifier();
 888
 889        private EllipticCurveKeyVerifier() : base( JsonWebKeyType.EllipticCurve )
 890        {
 891        }
 892
 893        public override bool HasSecretKey => false;
 894
 895        protected override void AddUsedProperties( ICollection<string> usedProperties )
 896        {
 897            base.AddUsedProperties( usedProperties );
 898            usedProperties.Add( JsonWebKey.Property_D );
 899        }
 900
 901        public override bool IsPrivateKeyComplete( JsonWebKey webKey, ref ICollection<string> missingProps )
 902        {
 903            if ( webKey.D == null || webKey.D.Length == 0 )
 904            {
 905                AddItem( ref missingProps, JsonWebKey.Property_D );
 906                return false;
 907            }
 908
 909            return true;
 910        }
 911
 912        public override bool IsPrivateKeyValid( JsonWebKey webKey, ref string errorMsg )
 913        {
 914            var requiredSize = JsonWebKeyCurveName.GetKeyParameterSize( webKey.CurveName );
 915
 916            if ( requiredSize < 0 )
 917            {
 918                errorMsg = $"Unsupported curve: \"{webKey.CurveName}\". Supported curves are: {SurroundWithQuotes( JsonW
 919                return false;
 920            }
 921
 922            if ( !ValidateKeyParameterSize( webKey.D, nameof( webKey.D ), requiredSize, ref errorMsg ) )
 923                return false;
 924
 925            return true;
 926        }
 927    }
 928
 929    internal sealed class EllipticCurveHsmKeyVerifier : EllipticCurveKeyVerifierBase
 930    {
 4931        public static JsonWebKeyVerifier Instance { get; } = new EllipticCurveHsmKeyVerifier();
 932
 2933        private EllipticCurveHsmKeyVerifier() : base( JsonWebKeyType.EllipticCurveHsm )
 934        {
 2935        }
 936
 0937        public override bool HasSecretKey => true;
 938
 939        protected override void AddUsedProperties( ICollection<string> usedProperties )
 940        {
 0941            base.AddUsedProperties( usedProperties );
 0942            usedProperties.Add( JsonWebKey.Property_T );
 0943        }
 944
 945        public override bool IsPrivateKeyComplete( JsonWebKey webKey, ref ICollection<string> missingProps )
 946        {
 0947            return true;
 948        }
 949
 950        public override bool IsPrivateKeyValid( JsonWebKey webKey, ref string errorMsg )
 951        {
 0952            return true;
 953        }
 954
 955        public override bool IsSecretKeyComplete( JsonWebKey webKey, ref ICollection<string> missingProps )
 956        {
 0957            if ( webKey.T == null || webKey.T.Length == 0 )
 958            {
 0959                AddItem( ref missingProps, JsonWebKey.Property_T );
 0960                return false;
 961            }
 962
 0963            return true;
 964        }
 965
 966        public override bool IsSecretKeyValid( JsonWebKey webKey, ref string errorMsg )
 967        {
 0968            return true;
 969        }
 970    }
 971
 972    internal abstract class RsaKeyVerifierBase : JsonWebKeyVerifier
 973    {
 974        protected RsaKeyVerifierBase( string kty ) : base( kty )
 975        {
 976        }
 977
 978        public override bool IsPublicKeyCrypto => true;
 979        public override bool IsSymmetricKeyCrypto => false;
 980
 981        protected override void AddCompatibleOperations( ICollection<string> compatibleOperations )
 982        {
 983            compatibleOperations.Add( JsonWebKeyOperation.Encrypt );
 984            compatibleOperations.Add( JsonWebKeyOperation.Decrypt );
 985            compatibleOperations.Add( JsonWebKeyOperation.Wrap );
 986            compatibleOperations.Add( JsonWebKeyOperation.Unwrap );
 987            compatibleOperations.Add( JsonWebKeyOperation.Sign );
 988            compatibleOperations.Add( JsonWebKeyOperation.Verify );
 989        }
 990
 991        protected override void AddUsedProperties( ICollection<string> usedProperties )
 992        {
 993            usedProperties.Add( JsonWebKey.Property_E );
 994            usedProperties.Add( JsonWebKey.Property_N );
 995        }
 996
 997        public override bool IsPublicKeyComplete( JsonWebKey webKey, ref ICollection<string> missingProps )
 998        {
 999            var result = true;
 1000
 1001            if ( webKey.E == null || webKey.E.Length == 0 )
 1002            {
 1003                AddItem( ref missingProps, JsonWebKey.Property_E );
 1004                result = false;
 1005            }
 1006
 1007            if ( webKey.N == null || webKey.N.Length == 0 )
 1008            {
 1009                AddItem( ref missingProps, JsonWebKey.Property_N );
 1010                result = false;
 1011            }
 1012
 1013            return result;
 1014        }
 1015
 1016        public override bool IsPublicKeyValid( JsonWebKey webKey, ref string errorMsg )
 1017        {
 1018            return true;
 1019        }
 1020    }
 1021
 1022    internal sealed class RsaKeyVerifier : RsaKeyVerifierBase
 1023    {
 1024        public static JsonWebKeyVerifier Instance { get; } = new RsaKeyVerifier();
 1025
 1026        private RsaKeyVerifier() : base( JsonWebKeyType.Rsa )
 1027        {
 1028        }
 1029
 1030        public override bool HasSecretKey => false;
 1031
 1032        protected override void AddUsedProperties( ICollection<string> usedProperties )
 1033        {
 1034            base.AddUsedProperties( usedProperties );
 1035            usedProperties.Add( JsonWebKey.Property_D );
 1036            usedProperties.Add( JsonWebKey.Property_DP );
 1037            usedProperties.Add( JsonWebKey.Property_DQ );
 1038            usedProperties.Add( JsonWebKey.Property_P );
 1039            usedProperties.Add( JsonWebKey.Property_Q );
 1040            usedProperties.Add( JsonWebKey.Property_QI );
 1041        }
 1042
 1043        public override bool IsPrivateKeyComplete( JsonWebKey webKey, ref ICollection<string> missingProps )
 1044        {
 1045            var result = true;
 1046
 1047            if ( webKey.D == null || webKey.D.Length == 0 )
 1048            {
 1049                AddItem( ref missingProps, JsonWebKey.Property_D );
 1050                result = false;
 1051            }
 1052
 1053            if ( webKey.DP == null || webKey.DP.Length == 0 )
 1054            {
 1055                AddItem( ref missingProps, JsonWebKey.Property_DP );
 1056                result = false;
 1057            }
 1058
 1059            if ( webKey.DQ == null || webKey.DQ.Length == 0 )
 1060            {
 1061                AddItem( ref missingProps, JsonWebKey.Property_DQ );
 1062                result = false;
 1063            }
 1064
 1065            if ( webKey.P == null || webKey.P.Length == 0 )
 1066            {
 1067                AddItem( ref missingProps, JsonWebKey.Property_P );
 1068                result = false;
 1069            }
 1070
 1071            if ( webKey.Q == null || webKey.Q.Length == 0 )
 1072            {
 1073                AddItem( ref missingProps, JsonWebKey.Property_Q );
 1074                result = false;
 1075            }
 1076
 1077            if ( webKey.QI == null || webKey.QI.Length == 0 )
 1078            {
 1079                AddItem( ref missingProps, JsonWebKey.Property_QI );
 1080                result = false;
 1081            }
 1082
 1083            return result;
 1084        }
 1085
 1086        public override bool IsPrivateKeyValid( JsonWebKey webKey, ref string errorMsg )
 1087        {
 1088            return true;
 1089        }
 1090    }
 1091
 1092    internal sealed class RsaHsmKeyVerifier : RsaKeyVerifierBase
 1093    {
 1094        public static JsonWebKeyVerifier Instance { get; } = new RsaHsmKeyVerifier();
 1095
 1096        private RsaHsmKeyVerifier() : base( JsonWebKeyType.RsaHsm )
 1097        {
 1098        }
 1099
 1100        public override bool HasSecretKey => true;
 1101
 1102        protected override void AddUsedProperties( ICollection<string> usedProperties )
 1103        {
 1104            base.AddUsedProperties( usedProperties );
 1105            usedProperties.Add( JsonWebKey.Property_T );
 1106        }
 1107
 1108        public override bool IsPrivateKeyComplete( JsonWebKey webKey, ref ICollection<string> missingProps )
 1109        {
 1110            return true;
 1111        }
 1112
 1113        public override bool IsPrivateKeyValid( JsonWebKey webKey, ref string errorMsg )
 1114        {
 1115            return true;
 1116        }
 1117
 1118        public override bool IsSecretKeyComplete( JsonWebKey webKey, ref ICollection<string> missingProps )
 1119        {
 1120            if ( webKey.T == null || webKey.T.Length == 0 )
 1121            {
 1122                AddItem( ref missingProps, JsonWebKey.Property_T );
 1123                return false;
 1124            }
 1125
 1126            return true;
 1127        }
 1128
 1129        public override bool IsSecretKeyValid( JsonWebKey webKey, ref string errorMsg )
 1130        {
 1131            return true;
 1132        }
 1133    }
 1134}