< Summary

Class:Microsoft.Azure.KeyVault.WebKey.JsonWebKey
Assembly:Microsoft.Azure.KeyVault.WebKey
File(s):C:\Git\azure-sdk-for-net\sdk\keyvault\Microsoft.Azure.KeyVault.WebKey\src\JsonWebKey.cs
Covered lines:236
Uncovered lines:29
Coverable lines:265
Total lines:757
Line coverage:89% (236 of 265)
Covered branches:133
Total branches:170
Branch coverage:78.2% (133 of 170)

Metrics

MethodCyclomatic complexity Line coverage Branch coverage
get_Kid()-100%100%
get_Kty()-100%100%
get_KeyOps()-100%100%
get_N()-100%100%
get_E()-100%100%
get_DP()-100%100%
get_DQ()-100%100%
get_QI()-100%100%
get_P()-100%100%
get_Q()-100%100%
get_CurveName()-100%100%
get_X()-100%100%
get_Y()-100%100%
get_D()-100%100%
get_K()-100%100%
get_T()-100%100%
VisitProperties(...)-85.71%33.33%
.ctor()-100%100%
.ctor(...)-83.33%50%
.ctor(...)-100%100%
.ctor(...)-100%100%
.ctor(...)-0%100%
.ctor(...)-100%100%
Equals(...)-83.33%75%
Equals(...)-86.49%86.11%
AreEqual(...)-83.33%83.33%
AreEqual(...)-80%80%
GetHashCode()-77.78%83.33%
GetHashCode(...)-100%100%
HasPrivateKey()-60%60%
IsValid()-100%100%
ToAes()-75%66.67%
CanonicalizeRSA()-100%100%
ToRSA(...)-100%100%
ToRSAParameters(...)-93.75%66.67%
ToECDsa(...)-100%100%
ToEcParameters(...)-81.25%50%
VerifyNonZero(...)-100%100%
RemoveLeadingZeros(...)-87.5%90%
ForceLength(...)-93.33%91.67%
ToString()-100%100%
ClearMemory()-95%90%
ZeroArray(...)-100%100%

File(s)

C:\Git\azure-sdk-for-net\sdk\keyvault\Microsoft.Azure.KeyVault.WebKey\src\JsonWebKey.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//
 5
 6using System;
 7using System.Collections.Generic;
 8using System.Security.Cryptography;
 9using Newtonsoft.Json;
 10
 11namespace Microsoft.Azure.KeyVault.WebKey
 12{
 13    /// <summary>
 14    /// As of http://tools.ietf.org/html/draft-ietf-jose-json-web-key-18
 15    /// </summary>
 16    [JsonObject]
 17    public sealed class JsonWebKey
 18    {
 19        // DataContract property names
 20        internal const string Property_Kid = "kid";
 21
 22        internal const string Property_Kty = "kty";
 23        internal const string Property_KeyOps = "key_ops";
 24
 25        // RSA Key Property Names
 26        internal const string Property_D = "d";
 27
 28        internal const string Property_DP = "dp";
 29        internal const string Property_DQ = "dq";
 30        internal const string Property_E = "e";
 31        internal const string Property_QI = "qi";
 32        internal const string Property_N = "n";
 33        internal const string Property_P = "p";
 34        internal const string Property_Q = "q";
 35
 36        // ECC Key Property Names
 37        internal const string Property_Crv = "crv";
 38
 39        internal const string Property_X = "x";
 40
 41        internal const string Property_Y = "y";
 42        // Property_D the same as RSA Key
 43
 44        // Symmetric Key Property Names
 45        internal const string Property_K = "k";
 46
 47        // HSM Token Property Names
 48        internal const string Property_T = "key_hsm";
 49
 50        /// <summary>
 51        /// Key Identifier
 52        /// </summary>
 53        [JsonProperty( DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore,
 163254        public string Kid { get; set; }
 55
 56        /// <summary>
 57        /// Gets or sets supported JsonWebKey key types (kty) for Elliptic
 58        /// Curve, RSA, HSM, Octet, usually RSA. Possible values include:
 59        /// 'EC', 'RSA', 'RSA-HSM', 'oct'
 60        /// </summary>
 61        [JsonProperty( DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore,
 334862        public string Kty { get; set; }
 63
 64        /// <summary>
 65        /// Supported Key Operations
 66        /// </summary>
 67        [JsonProperty( DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore,
 180268        public IList<string> KeyOps { get; set; }
 69
 70        #region RSA Public Key Parameters
 71
 72        /// <summary>
 73        /// RSA modulus, in Base64.
 74        /// </summary>
 75        [JsonProperty( DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore,
 76        [JsonConverter( typeof( Base64UrlJsonConverter ) )]
 453677        public byte[] N { get; set; }
 78
 79        /// <summary>
 80        /// RSA public exponent, in Base64.
 81        /// </summary>
 82        [JsonProperty( DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore,
 83        [JsonConverter( typeof( Base64UrlJsonConverter ) )]
 442684        public byte[] E { get; set; }
 85
 86        #endregion
 87
 88        #region RSA Private Key Parameters
 89
 90        /// <summary>
 91        /// RSA Private Key Parameter
 92        /// </summary>
 93        [JsonProperty( DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore,
 94        [JsonConverter( typeof( Base64UrlJsonConverter ) )]
 274695        public byte[] DP { get; set; }
 96
 97        /// <summary>
 98        /// RSA Private Key Parameter
 99        /// </summary>
 100        [JsonProperty( DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore,
 101        [JsonConverter( typeof( Base64UrlJsonConverter ) )]
 2686102        public byte[] DQ { get; set; }
 103
 104        /// <summary>
 105        /// RSA Private Key Parameter
 106        /// </summary>
 107        [JsonProperty( DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore,
 108        [JsonConverter( typeof( Base64UrlJsonConverter ) )]
 2622109        public byte[] QI { get; set; }
 110
 111        /// <summary>
 112        /// RSA secret prime
 113        /// </summary>
 114        [JsonProperty( DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore,
 115        [JsonConverter( typeof( Base64UrlJsonConverter ) )]
 2566116        public byte[] P { get; set; }
 117
 118        /// <summary>
 119        /// RSA secret prime, with p &lt; q
 120        /// </summary>
 121        [JsonProperty( DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore,
 122        [JsonConverter( typeof( Base64UrlJsonConverter ) )]
 2506123        public byte[] Q { get; set; }
 124
 125        #endregion
 126
 127        #region EC Public Key Parameters
 128
 129        /// <summary>
 130        /// The curve for Elliptic Curve Cryptography (ECC) algorithms
 131        /// </summary>
 132        [JsonProperty( DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore,
 1728133        public string CurveName { get; set; }
 134
 135        /// <summary>
 136        /// X coordinate for the Elliptic Curve point.
 137        /// </summary>
 138        [JsonProperty( DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore,
 139        [JsonConverter( typeof( Base64UrlJsonConverter ) )]
 1778140        public byte[] X { get; set; }
 141
 142        /// <summary>
 143        /// Y coordinate for the Elliptic Curve point.
 144        /// </summary>
 145        [JsonProperty( DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore,
 146        [JsonConverter( typeof( Base64UrlJsonConverter ) )]
 1746147        public byte[] Y { get; set; }
 148
 149        #endregion
 150
 151        #region EC and RSA Private Key Parameters
 152
 153        /// <summary>
 154        /// RSA private exponent or ECC private key.
 155        /// </summary>
 156        [JsonProperty( DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore,
 157        [JsonConverter( typeof( Base64UrlJsonConverter ) )]
 3186158        public byte[] D { get; set; }
 159
 160        #endregion
 161
 162        #region Symmetric Key Parameters
 163
 164        /// <summary>
 165        /// Symmetric key
 166        /// </summary>
 167        [JsonProperty( DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore,
 168        [JsonConverter( typeof( Base64UrlJsonConverter ) )]
 1364169        public byte[] K { get; set; }
 170
 171        #endregion
 172
 173        /// <summary>
 174        /// HSM Token, used with "Bring Your Own Key"
 175        /// </summary>
 176        [JsonProperty( DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore,
 177        [JsonConverter( typeof( Base64UrlJsonConverter ) )]
 1088178        public byte[] T { get; set; }
 179
 180        /// <summary>
 181        /// Holds properties that are not part of current schema.
 182        /// </summary>
 183        [JsonExtensionData]
 184        public IDictionary<string, object> ExtensionData;
 185
 186        /// <summary>
 187        /// Iterates over all JSON properties of this object, calling the specified visitor.
 188        /// </summary>
 189        /// All JSON properties are visited. This includes normal properties, properties that are not useful for the
 190        /// key type, and properties that are not part of current schema (extension data).
 191        /// Users must assume the properties are visited in random order.
 192        /// <param name="visitor">A visitor that will be called for each property.</param>
 193        public void VisitProperties( Action<string, object> visitor )
 194        {
 4195            if ( visitor == null )
 0196                throw new ArgumentNullException( nameof( visitor ) );
 197
 4198            visitor( Property_Crv, CurveName );
 4199            visitor( Property_D, D );
 4200            visitor( Property_DP, DP );
 4201            visitor( Property_DQ, DQ );
 4202            visitor( Property_E, E );
 4203            visitor( Property_K, K );
 4204            visitor( Property_KeyOps, KeyOps );
 4205            visitor( Property_Kid, Kid );
 4206            visitor( Property_Kty, Kty );
 4207            visitor( Property_N, N );
 4208            visitor( Property_P, P );
 4209            visitor( Property_Q, Q );
 4210            visitor( Property_T, T );
 4211            visitor( Property_X, X );
 4212            visitor( Property_Y, Y );
 213
 4214            if ( ExtensionData != null )
 0215                foreach ( var entry in ExtensionData )
 0216                    visitor( entry.Key, entry.Value );
 4217        }
 218
 219        /// <summary>
 220        /// Creates an instance of <see cref="JsonWebKey"/>
 221        /// </summary>
 222        [JsonConstructor]
 670223        public JsonWebKey()
 224        {
 225            // Intentionally empty
 670226        }
 227
 228        /// <summary>
 229        /// Converts an AES object to a WebKey of type Octet
 230        /// </summary>
 231        /// <param name="aesProvider"></param>
 12232        public JsonWebKey( Aes aesProvider )
 233        {
 12234            if ( aesProvider == null )
 0235                throw new ArgumentNullException( "aesProvider" );
 236
 12237            Kty = JsonWebKeyType.Octet;
 12238            K = aesProvider.Key;
 12239        }
 240
 241        /// <summary>
 242        /// Initializes a new instance with the key provided by the ECDsa object.
 243        /// </summary>
 244        /// <param name="ecsda">The ECDsa object previously initialized with the desired key.</param>
 245        /// <param name="includePrivateParameters">Tells if the instance must inclue private parameters.
 246        /// This requires the key in the ECDsa object to include private material and be marked as exportable.</param>
 247        public JsonWebKey(ECDsa ecsda, bool includePrivateParameters = false )
 44248            : this( ecParameters: EccExtension.ExportParameters( ecsda, includePrivateParameters ) )
 249        {
 44250            KeyOps = ecsda.GetKeyOperations();
 44251        }
 252
 253        /// <summary>
 254        /// Converts a ECParameters object to a WebKey of type EC.
 255        /// </summary>
 256        /// <param name="ecParameters">The EC object to convert</param>
 257        /// <returns>A WebKey representing the EC object</returns>
 48258        public JsonWebKey( ECParameters ecParameters )
 259        {
 48260            Kty = JsonWebKeyType.EllipticCurve;
 261
 48262            CurveName = ecParameters.Curve;
 48263            D = ecParameters.D;
 48264            X = ecParameters.X;
 48265            Y = ecParameters.Y;
 48266        }
 267
 268        /// <summary>
 269        /// Converts a RSA object to a WebKey of type RSA.
 270        /// </summary>
 271        /// <param name="rsaProvider">The RSA object to convert</param>
 272        /// <param name="includePrivateParameters">True to include the RSA private key parameters</param>
 273        /// <returns>A WebKey representing the RSA object</returns>
 0274        public JsonWebKey( RSA rsaProvider, bool includePrivateParameters = false ) : this( rsaProvider.ExportParameters
 275        {
 0276        }
 277
 278        /// <summary>
 279        /// Converts a RSAParameters object to a WebKey of type RSA.
 280        /// </summary>
 281        /// <param name="rsaParameters">The RSA object to convert</param>
 282        /// <returns>A WebKey representing the RSA object</returns>
 18283        public JsonWebKey( RSAParameters rsaParameters )
 284        {
 18285            Kty = JsonWebKeyType.Rsa;
 286
 18287            E = rsaParameters.Exponent;
 18288            N = rsaParameters.Modulus;
 289
 18290            D = rsaParameters.D;
 18291            DP = rsaParameters.DP;
 18292            DQ = rsaParameters.DQ;
 18293            QI = rsaParameters.InverseQ;
 18294            P = rsaParameters.P;
 18295            Q = rsaParameters.Q;
 18296        }
 297
 298        public override bool Equals( object obj )
 299        {
 366300            if ( obj == this )
 0301                return true;
 302
 366303            var other = obj as JsonWebKey;
 304
 366305            if ( other == null )
 26306                return false;
 307
 340308            return Equals( other );
 309        }
 310
 311        /// <summary>
 312        /// Compares <see cref="JsonWebKey"/> objects
 313        /// </summary>
 314        /// <param name="other"> the <see cref="JsonWebKey"/> object to compare with </param>
 315        /// <returns> whether the <see cref="JsonWebKey"/> objects are equals </returns>
 316        public bool Equals( JsonWebKey other )
 317        {
 442318            if ( other == this )
 0319                return true;
 320
 442321            if ( other == null )
 2322                return false;
 323
 440324            if ( !string.Equals( Kid, other.Kid ) )
 0325                return false;
 326
 440327            if ( !string.Equals( Kty, other.Kty ) )
 0328                return false;
 329
 440330            if ( !AreEqual( KeyOps, other.KeyOps ) )
 4331                return false;
 332
 436333            if ( !string.Equals( CurveName, other.CurveName ) )
 16334                return false;
 335
 420336            if ( !AreEqual( K, other.K ) )
 0337                return false;
 338
 339            // Public parameters
 420340            if ( !AreEqual( N, other.N ) )
 10341                return false;
 342
 410343            if ( !AreEqual( E, other.E ) )
 10344                return false;
 345
 400346            if ( !AreEqual( X, other.X ) )
 16347                return false;
 348
 384349            if ( !AreEqual( Y, other.Y ) )
 16350                return false;
 351
 352            // Private parameters
 368353            if ( !AreEqual( D, other.D ) )
 26354                return false;
 355
 342356            if ( !AreEqual( DP, other.DP ) )
 10357                return false;
 358
 332359            if ( !AreEqual( DQ, other.DQ ) )
 10360                return false;
 361
 322362            if ( !AreEqual( QI, other.QI ) )
 10363                return false;
 364
 312365            if ( !AreEqual( P, other.P ) )
 10366                return false;
 367
 302368            if ( !AreEqual( Q, other.Q ) )
 10369                return false;
 370
 371            // HSM token
 292372            if ( !AreEqual( T, other.T ) )
 0373                return false;
 374
 292375            return true;
 376        }
 377
 378        private static bool AreEqual( byte[] a, byte[] b )
 379        {
 4304380            if ( a == b )
 1818381                return true;
 382
 2486383            if ( a == null )
 384                // b can't be null because otherwise we would return true above.
 0385                return b.Length == 0;
 386
 2486387            if ( b == null )
 388                // Likewise, a can't be null.
 128389                return a.Length == 0;
 390
 2358391            if ( a.Length != b.Length )
 80392                return false;
 393
 678376394            for ( var i = 0; i < a.Length; ++i )
 336910395                if ( a[i] != b[i] )
 0396                    return false;
 397
 2278398            return true;
 399        }
 400
 401        private static bool AreEqual( IList<string> a, IList<string> b )
 402        {
 440403            if ( a == b )
 340404                return true;
 405
 100406            if ( ( a == null ) != ( b == null ) )
 4407                return false;
 408
 96409            if ( a.Count != b.Count )
 0410                return false;
 411
 688412            for ( var i = 0; i < a.Count; ++i )
 248413                if ( a[i] != b[i] )
 0414                    return false;
 415
 96416            return true;
 417        }
 418
 419        public override int GetHashCode()
 420        {
 40421            var hashCode = 48313; // setting it to a random prime number
 422
 40423            if ( Kid != null )
 424            {
 22425                hashCode += Kid.GetHashCode();
 426            }
 427
 40428            switch ( Kty )
 429            {
 430                case JsonWebKeyType.Octet:
 14431                    return hashCode + GetHashCode( K );
 432
 433                case JsonWebKeyType.EllipticCurve:
 0434                    return hashCode + GetHashCode( X );
 435
 436                case JsonWebKeyType.Rsa:
 0437                    return hashCode + GetHashCode( N );
 438
 439                case JsonWebKeyType.EllipticCurveHsm:
 440                case JsonWebKeyType.RsaHsm:
 18441                    return hashCode + GetHashCode( T );
 442
 443                default:
 8444                    return hashCode;
 445            }
 446        }
 447
 448        private static int GetHashCode( byte[] obj )
 449        {
 32450            if ( obj == null || obj.Length == 0 )
 10451                return 0;
 452
 22453            var hashCode = 0;
 454
 455            // Rotate by 3 bits and XOR the new value.
 912456            foreach ( var v in obj )
 434457                hashCode = ( hashCode << 3 ) | ( hashCode >> 29 ) ^ v;
 458
 22459            return hashCode;
 460        }
 461
 462        /// <summary>
 463        /// Verifies whether this object has a private key
 464        /// </summary>
 465        /// <returns> True if the object has private key; false otherwise.</returns>
 466        public bool HasPrivateKey()
 467        {
 6468            switch ( Kty )
 469            {
 470                case JsonWebKeyType.Octet:
 2471                    return K != null;
 472
 473                case JsonWebKeyType.EllipticCurve:
 474                case JsonWebKeyType.EllipticCurveHsm:
 0475                    return D != null;
 476
 477                case JsonWebKeyType.Rsa:
 478                case JsonWebKeyType.RsaHsm:
 4479                    return D != null && DP != null && DQ != null && QI != null && P != null && Q != null;
 480
 481                default:
 0482                    return false;
 483            }
 484        }
 485
 486        /// <summary>
 487        /// Determines if the WebKey object is valid according to the rules for
 488        /// each of value of JsonWebKeyType.
 489        /// </summary>
 490        /// <returns>true if the WebKey is valid</returns>
 491        public bool IsValid()
 492        {
 8493            var verifierOptions =
 8494                JsonWebKeyVerifier.Options.DenyIncompatibleOperations |
 8495                JsonWebKeyVerifier.Options.DenyExtraneousFields;
 496
 8497            string unused = null;
 8498            return JsonWebKeyVerifier.VerifyByKeyType( this, verifierOptions, ref unused );
 499        }
 500
 501        /// <summary>
 502        /// Converts a WebKey of type Octet to an AES object.
 503        /// </summary>
 504        /// <returns>An AES object</returns>
 505        public Aes ToAes()
 506        {
 2507            if ( !Kty.Equals( JsonWebKeyType.Octet ) )
 0508                throw new InvalidOperationException( "key is not an octet key" );
 509
 2510            if ( K == null )
 0511                throw new InvalidOperationException( "key does not contain a value" );
 512
 2513            var result = Aes.Create();
 514
 2515            if ( result != null )
 2516                result.Key = K;
 517
 2518            return result;
 519        }
 520
 521        /// <summary>
 522        /// Remove leading zeros from all RSA parameters.
 523        /// </summary>
 524        public void CanonicalizeRSA()
 525        {
 90526            N = RemoveLeadingZeros( N );
 90527            E = RemoveLeadingZeros( E );
 90528            D = RemoveLeadingZeros( D );
 90529            DP = RemoveLeadingZeros( DP );
 90530            DQ = RemoveLeadingZeros( DQ );
 90531            QI = RemoveLeadingZeros( QI );
 90532            P = RemoveLeadingZeros( P );
 90533            Q = RemoveLeadingZeros( Q );
 90534        }
 535
 536        /// <summary>
 537        /// Converts a WebKey of type RSA or RSAHSM to a RSA object
 538        /// </summary>
 539        /// <param name="includePrivateParameters">Tells if private material must be included.</param>
 540        /// <returns>An initialized RSA instance</returns>
 541        public RSA ToRSA( bool includePrivateParameters = false )
 542        {
 786543            var rsaParameters = ToRSAParameters( includePrivateParameters );
 586544            var result = RSA.Create();
 586545            result.ImportParameters( rsaParameters );
 586546            return result;
 547        }
 548
 549        /// <summary>
 550        /// Converts a WebKey of type RSA or RSAHSM to a RSA parameter object
 551        /// </summary>
 552        /// <param name="includePrivateParameters">Tells if private material must be included.</param>
 553        /// <returns>An RSA parameter</returns>
 554        public RSAParameters ToRSAParameters( bool includePrivateParameters = false )
 555        {
 996556            if ( Kty != JsonWebKeyType.Rsa && Kty != JsonWebKeyType.RsaHsm )
 0557                throw new ArgumentException( "JsonWebKey is not a RSA key" );
 558
 996559            VerifyNonZero( nameof( N ), N );
 916560            VerifyNonZero( nameof( E ), E );
 561
 562            // Length requirements defined by 2.2.2.9.1 RSA Private Key BLOB (https://msdn.microsoft.com/en-us/library/c
 563            // See KV bugs 190589 and 183469.
 564
 836565            var result = new RSAParameters();
 836566            result.Modulus = RemoveLeadingZeros( N );
 836567            result.Exponent = ForceLength( nameof( E ), E, 4 );
 568
 836569            if ( includePrivateParameters )
 570            {
 488571                var bitlen = result.Modulus.Length * 8;
 572
 488573                result.D = ForceLength( nameof( D ), D, bitlen / 8 );
 448574                result.DP = ForceLength( nameof( DP ), DP, bitlen / 16 );
 408575                result.DQ = ForceLength( nameof( DQ ), DQ, bitlen / 16 );
 368576                result.InverseQ = ForceLength( nameof( QI ), QI, bitlen / 16 );
 328577                result.P = ForceLength( nameof( P ), P, bitlen / 16 );
 288578                result.Q = ForceLength( nameof( Q ), Q, bitlen / 16 );
 579            }
 580
 596581            return result;
 582        }
 583
 584        /// <summary>
 585        /// Converts a WebKey of type EC or EC-HSM to an ECDsa object
 586        /// </summary>
 587        /// <param name="includePrivateParameters">Tells if private material must be included.</param>
 588        /// <returns>An initialized ECDsa instance</returns>
 589        public ECDsa ToECDsa( bool includePrivateParameters = false )
 590        {
 108591            return ToEcParameters( includePrivateParameters ).ToEcdsa( includePrivateParameters );
 592        }
 593
 594        /// <summary>
 595        /// Converts a WebKey of type EC or EC-HSM to an EC parameter object.
 596        /// </summary>
 597        /// <param name="includePrivateParameters">Tells if private material must be included.</param>
 598        /// <returns>An EC parameter object</returns>
 599        public ECParameters ToEcParameters( bool includePrivateParameters = false )
 600        {
 116601            if ( Kty != JsonWebKeyType.EllipticCurve && Kty != JsonWebKeyType.EllipticCurveHsm )
 0602                throw new ArgumentException( "JsonWebKey is not an EC key" );
 603
 116604            VerifyNonZero( nameof( X ), X );
 116605            VerifyNonZero( nameof( Y ), Y );
 606
 116607            var requiredSize = JsonWebKeyCurveName.GetKeyParameterSize( CurveName );
 116608            if ( requiredSize < 0 )
 609            {
 0610                var curveDesc = CurveName == null ? "null" : $"\"{CurveName}\"";
 0611                throw new ArgumentException( $"Invalid curve type: {curveDesc}" );
 612            }
 613
 116614            var result = new ECParameters();
 116615            result.Curve = CurveName;
 116616            result.X = ForceLength( nameof( X ), X, requiredSize );
 116617            result.Y = ForceLength( nameof( Y ), Y, requiredSize );
 618
 116619            if ( includePrivateParameters )
 620            {
 36621                VerifyNonZero( nameof( D ), D );
 28622                result.D = ForceLength( nameof( D ), D, requiredSize );
 623            }
 624
 108625            return result;
 626        }
 627
 628        private static void VerifyNonZero( string name, byte[] value )
 629        {
 2180630            if ( value != null )
 6872631                foreach ( var t in value )
 2350632                    if ( t != 0 )
 2012633                        return;
 634
 168635            throw new ArgumentException( $"Value of \"{name}\" must be non-zero." );
 636        }
 637
 638        private static byte[] RemoveLeadingZeros( byte[] value )
 639        {
 640            // Do nothing if:
 641            // 1) value is null.
 642            // 2) value is empty.
 643            // 3) value has length of 1 (this is considered a useful zero).
 644            // 4) first byte is already non-zero (optimization).
 1556645            if ( value == null || value.Length <= 1 || value[0] != 0 )
 1446646                return value;
 647
 648            // We know that value[0] is zero, so we start from 1.
 484649            for ( var i = 1; i < value.Length; ++i )
 650            {
 242651                if ( value[i] != 0 )
 652                {
 110653                    var result = new byte[value.Length - i];
 110654                    Array.Copy( value, i, result, 0, result.Length );
 110655                    return result;
 656                }
 657            }
 658
 659            // If all is zero, return an array with a single useful zero.
 0660            return new byte[] {0};
 661        }
 662
 663        private static byte[] ForceLength( string name, byte[] value, int requiredLength )
 664        {
 3424665            if ( value == null || value.Length == 0 )
 240666                throw new ArgumentException( $"Value of \"{name}\" is null or empty." );
 667
 3184668            if ( value.Length == requiredLength )
 2216669                return value;
 670
 968671            if ( value.Length < requiredLength )
 672            {
 888673                var padded = new byte[requiredLength];
 888674                Array.Copy( value, 0, padded, requiredLength - value.Length, value.Length );
 888675                return padded;
 676            }
 677
 678            // value.Length > requiredLength
 679
 680            // Make sure the extra bytes are all zeros.
 80681            var extraLen = value.Length - requiredLength;
 800682            for ( var i = 0; i < extraLen; ++i )
 320683                if ( value[i] != 0 )
 0684                    throw new ArgumentException( $"Invalid length of \"{name}\": expected at most {requiredLength} bytes
 685
 80686            var trimmed = new byte[requiredLength];
 80687            Array.Copy( value, value.Length - requiredLength, trimmed, 0, requiredLength );
 80688            return trimmed;
 689        }
 690
 691        public override string ToString()
 692        {
 430693            return JsonConvert.SerializeObject( this );
 694        }
 695
 696        /// <summary>
 697        /// Best effort to clear private key material
 698        /// Not strong guarantee since GC may move the arrays during compact.
 699        /// </summary>
 700        public void ClearMemory()
 701        {
 702            // We ignore kty and clear everything.
 703
 704            // Octet keys:
 10705            ZeroArray( K );
 10706            K = null;
 707
 708            // Rsa keys:
 709
 710            // We want to clear public key to avoid identification.
 10711            ZeroArray( N );
 10712            ZeroArray( E );
 713
 714            // Private material of RSA:
 10715            ZeroArray( D );
 10716            ZeroArray( DP );
 10717            ZeroArray( DQ );
 10718            ZeroArray( QI );
 10719            ZeroArray( P );
 10720            ZeroArray( Q );
 10721            N = E = D = DP = DQ = QI = P = Q = null;
 722
 723            // RsaHsm keys:
 10724            ZeroArray( T );
 10725            T = null;
 726
 727            // Elliptic curve
 10728            ZeroArray( X );
 10729            ZeroArray( Y );
 10730            ZeroArray( D ); // D is intentionally repeated.
 10731            X = Y = D = null;
 732
 10733            switch ( Kty )
 734            {
 735                case JsonWebKeyType.Octet:
 736                case JsonWebKeyType.EllipticCurve:
 737                case JsonWebKeyType.EllipticCurveHsm:
 738                case JsonWebKeyType.Rsa:
 739                case JsonWebKeyType.RsaHsm:
 740                    // Supported types fall here.
 741                    break;
 742
 743                default:
 744                    // Unsupported types fall here.
 745                    // If someone forgets to implement ClearMemory() for a new kty, this exception will reveal the mista
 0746                    throw new NotImplementedException( $"Unsupported kty: {Kty}" );
 747            }
 10748        }
 749
 750        private static void ZeroArray( byte[] a )
 751        {
 130752            if ( a == null )
 10753                return;
 120754            Array.Clear( a, 0, a.Length );
 120755        }
 756    }
 757}