|   |  | 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 |  |  | 
|   |  | 6 |  | using System; | 
|   |  | 7 |  | using System.Collections.Generic; | 
|   |  | 8 |  | using System.Security.Cryptography; | 
|   |  | 9 |  | using Newtonsoft.Json; | 
|   |  | 10 |  |  | 
|   |  | 11 |  | namespace 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, | 
|   | 1632 | 54 |  |         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, | 
|   | 3348 | 62 |  |         public string Kty { get; set; } | 
|   |  | 63 |  |  | 
|   |  | 64 |  |         /// <summary> | 
|   |  | 65 |  |         /// Supported Key Operations | 
|   |  | 66 |  |         /// </summary> | 
|   |  | 67 |  |         [JsonProperty( DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, | 
|   | 1802 | 68 |  |         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 ) )] | 
|   | 4536 | 77 |  |         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 ) )] | 
|   | 4426 | 84 |  |         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 ) )] | 
|   | 2746 | 95 |  |         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 ) )] | 
|   | 2686 | 102 |  |         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 ) )] | 
|   | 2622 | 109 |  |         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 ) )] | 
|   | 2566 | 116 |  |         public byte[] P { get; set; } | 
|   |  | 117 |  |  | 
|   |  | 118 |  |         /// <summary> | 
|   |  | 119 |  |         /// RSA secret prime, with p < q | 
|   |  | 120 |  |         /// </summary> | 
|   |  | 121 |  |         [JsonProperty( DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, | 
|   |  | 122 |  |         [JsonConverter( typeof( Base64UrlJsonConverter ) )] | 
|   | 2506 | 123 |  |         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, | 
|   | 1728 | 133 |  |         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 ) )] | 
|   | 1778 | 140 |  |         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 ) )] | 
|   | 1746 | 147 |  |         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 ) )] | 
|   | 3186 | 158 |  |         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 ) )] | 
|   | 1364 | 169 |  |         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 ) )] | 
|   | 1088 | 178 |  |         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 |  |         { | 
|   | 4 | 195 |  |             if ( visitor == null ) | 
|   | 0 | 196 |  |                 throw new ArgumentNullException( nameof( visitor ) ); | 
|   |  | 197 |  |  | 
|   | 4 | 198 |  |             visitor( Property_Crv, CurveName ); | 
|   | 4 | 199 |  |             visitor( Property_D, D ); | 
|   | 4 | 200 |  |             visitor( Property_DP, DP ); | 
|   | 4 | 201 |  |             visitor( Property_DQ, DQ ); | 
|   | 4 | 202 |  |             visitor( Property_E, E ); | 
|   | 4 | 203 |  |             visitor( Property_K, K ); | 
|   | 4 | 204 |  |             visitor( Property_KeyOps, KeyOps ); | 
|   | 4 | 205 |  |             visitor( Property_Kid, Kid ); | 
|   | 4 | 206 |  |             visitor( Property_Kty, Kty ); | 
|   | 4 | 207 |  |             visitor( Property_N, N ); | 
|   | 4 | 208 |  |             visitor( Property_P, P ); | 
|   | 4 | 209 |  |             visitor( Property_Q, Q ); | 
|   | 4 | 210 |  |             visitor( Property_T, T ); | 
|   | 4 | 211 |  |             visitor( Property_X, X ); | 
|   | 4 | 212 |  |             visitor( Property_Y, Y ); | 
|   |  | 213 |  |  | 
|   | 4 | 214 |  |             if ( ExtensionData != null ) | 
|   | 0 | 215 |  |                 foreach ( var entry in ExtensionData ) | 
|   | 0 | 216 |  |                     visitor( entry.Key, entry.Value ); | 
|   | 4 | 217 |  |         } | 
|   |  | 218 |  |  | 
|   |  | 219 |  |         /// <summary> | 
|   |  | 220 |  |         /// Creates an instance of <see cref="JsonWebKey"/> | 
|   |  | 221 |  |         /// </summary> | 
|   |  | 222 |  |         [JsonConstructor] | 
|   | 670 | 223 |  |         public JsonWebKey() | 
|   |  | 224 |  |         { | 
|   |  | 225 |  |             // Intentionally empty | 
|   | 670 | 226 |  |         } | 
|   |  | 227 |  |  | 
|   |  | 228 |  |         /// <summary> | 
|   |  | 229 |  |         /// Converts an AES object to a WebKey of type Octet | 
|   |  | 230 |  |         /// </summary> | 
|   |  | 231 |  |         /// <param name="aesProvider"></param> | 
|   | 12 | 232 |  |         public JsonWebKey( Aes aesProvider ) | 
|   |  | 233 |  |         { | 
|   | 12 | 234 |  |             if ( aesProvider == null ) | 
|   | 0 | 235 |  |                 throw new ArgumentNullException( "aesProvider" ); | 
|   |  | 236 |  |  | 
|   | 12 | 237 |  |             Kty = JsonWebKeyType.Octet; | 
|   | 12 | 238 |  |             K = aesProvider.Key; | 
|   | 12 | 239 |  |         } | 
|   |  | 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 ) | 
|   | 44 | 248 |  |             : this( ecParameters: EccExtension.ExportParameters( ecsda, includePrivateParameters ) ) | 
|   |  | 249 |  |         { | 
|   | 44 | 250 |  |             KeyOps = ecsda.GetKeyOperations(); | 
|   | 44 | 251 |  |         } | 
|   |  | 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> | 
|   | 48 | 258 |  |         public JsonWebKey( ECParameters ecParameters ) | 
|   |  | 259 |  |         { | 
|   | 48 | 260 |  |             Kty = JsonWebKeyType.EllipticCurve; | 
|   |  | 261 |  |  | 
|   | 48 | 262 |  |             CurveName = ecParameters.Curve; | 
|   | 48 | 263 |  |             D = ecParameters.D; | 
|   | 48 | 264 |  |             X = ecParameters.X; | 
|   | 48 | 265 |  |             Y = ecParameters.Y; | 
|   | 48 | 266 |  |         } | 
|   |  | 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> | 
|   | 0 | 274 |  |         public JsonWebKey( RSA rsaProvider, bool includePrivateParameters = false ) : this( rsaProvider.ExportParameters | 
|   |  | 275 |  |         { | 
|   | 0 | 276 |  |         } | 
|   |  | 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> | 
|   | 18 | 283 |  |         public JsonWebKey( RSAParameters rsaParameters ) | 
|   |  | 284 |  |         { | 
|   | 18 | 285 |  |             Kty = JsonWebKeyType.Rsa; | 
|   |  | 286 |  |  | 
|   | 18 | 287 |  |             E = rsaParameters.Exponent; | 
|   | 18 | 288 |  |             N = rsaParameters.Modulus; | 
|   |  | 289 |  |  | 
|   | 18 | 290 |  |             D = rsaParameters.D; | 
|   | 18 | 291 |  |             DP = rsaParameters.DP; | 
|   | 18 | 292 |  |             DQ = rsaParameters.DQ; | 
|   | 18 | 293 |  |             QI = rsaParameters.InverseQ; | 
|   | 18 | 294 |  |             P = rsaParameters.P; | 
|   | 18 | 295 |  |             Q = rsaParameters.Q; | 
|   | 18 | 296 |  |         } | 
|   |  | 297 |  |  | 
|   |  | 298 |  |         public override bool Equals( object obj ) | 
|   |  | 299 |  |         { | 
|   | 366 | 300 |  |             if ( obj == this ) | 
|   | 0 | 301 |  |                 return true; | 
|   |  | 302 |  |  | 
|   | 366 | 303 |  |             var other = obj as JsonWebKey; | 
|   |  | 304 |  |  | 
|   | 366 | 305 |  |             if ( other == null ) | 
|   | 26 | 306 |  |                 return false; | 
|   |  | 307 |  |  | 
|   | 340 | 308 |  |             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 |  |         { | 
|   | 442 | 318 |  |             if ( other == this ) | 
|   | 0 | 319 |  |                 return true; | 
|   |  | 320 |  |  | 
|   | 442 | 321 |  |             if ( other == null ) | 
|   | 2 | 322 |  |                 return false; | 
|   |  | 323 |  |  | 
|   | 440 | 324 |  |             if ( !string.Equals( Kid, other.Kid ) ) | 
|   | 0 | 325 |  |                 return false; | 
|   |  | 326 |  |  | 
|   | 440 | 327 |  |             if ( !string.Equals( Kty, other.Kty ) ) | 
|   | 0 | 328 |  |                 return false; | 
|   |  | 329 |  |  | 
|   | 440 | 330 |  |             if ( !AreEqual( KeyOps, other.KeyOps ) ) | 
|   | 4 | 331 |  |                 return false; | 
|   |  | 332 |  |  | 
|   | 436 | 333 |  |             if ( !string.Equals( CurveName, other.CurveName ) ) | 
|   | 16 | 334 |  |                 return false; | 
|   |  | 335 |  |  | 
|   | 420 | 336 |  |             if ( !AreEqual( K, other.K ) ) | 
|   | 0 | 337 |  |                 return false; | 
|   |  | 338 |  |  | 
|   |  | 339 |  |             // Public parameters | 
|   | 420 | 340 |  |             if ( !AreEqual( N, other.N ) ) | 
|   | 10 | 341 |  |                 return false; | 
|   |  | 342 |  |  | 
|   | 410 | 343 |  |             if ( !AreEqual( E, other.E ) ) | 
|   | 10 | 344 |  |                 return false; | 
|   |  | 345 |  |  | 
|   | 400 | 346 |  |             if ( !AreEqual( X, other.X ) ) | 
|   | 16 | 347 |  |                 return false; | 
|   |  | 348 |  |  | 
|   | 384 | 349 |  |             if ( !AreEqual( Y, other.Y ) ) | 
|   | 16 | 350 |  |                 return false; | 
|   |  | 351 |  |  | 
|   |  | 352 |  |             // Private parameters | 
|   | 368 | 353 |  |             if ( !AreEqual( D, other.D ) ) | 
|   | 26 | 354 |  |                 return false; | 
|   |  | 355 |  |  | 
|   | 342 | 356 |  |             if ( !AreEqual( DP, other.DP ) ) | 
|   | 10 | 357 |  |                 return false; | 
|   |  | 358 |  |  | 
|   | 332 | 359 |  |             if ( !AreEqual( DQ, other.DQ ) ) | 
|   | 10 | 360 |  |                 return false; | 
|   |  | 361 |  |  | 
|   | 322 | 362 |  |             if ( !AreEqual( QI, other.QI ) ) | 
|   | 10 | 363 |  |                 return false; | 
|   |  | 364 |  |  | 
|   | 312 | 365 |  |             if ( !AreEqual( P, other.P ) ) | 
|   | 10 | 366 |  |                 return false; | 
|   |  | 367 |  |  | 
|   | 302 | 368 |  |             if ( !AreEqual( Q, other.Q ) ) | 
|   | 10 | 369 |  |                 return false; | 
|   |  | 370 |  |  | 
|   |  | 371 |  |             // HSM token | 
|   | 292 | 372 |  |             if ( !AreEqual( T, other.T ) ) | 
|   | 0 | 373 |  |                 return false; | 
|   |  | 374 |  |  | 
|   | 292 | 375 |  |             return true; | 
|   |  | 376 |  |         } | 
|   |  | 377 |  |  | 
|   |  | 378 |  |         private static bool AreEqual( byte[] a, byte[] b ) | 
|   |  | 379 |  |         { | 
|   | 4304 | 380 |  |             if ( a == b ) | 
|   | 1818 | 381 |  |                 return true; | 
|   |  | 382 |  |  | 
|   | 2486 | 383 |  |             if ( a == null ) | 
|   |  | 384 |  |                 // b can't be null because otherwise we would return true above. | 
|   | 0 | 385 |  |                 return b.Length == 0; | 
|   |  | 386 |  |  | 
|   | 2486 | 387 |  |             if ( b == null ) | 
|   |  | 388 |  |                 // Likewise, a can't be null. | 
|   | 128 | 389 |  |                 return a.Length == 0; | 
|   |  | 390 |  |  | 
|   | 2358 | 391 |  |             if ( a.Length != b.Length ) | 
|   | 80 | 392 |  |                 return false; | 
|   |  | 393 |  |  | 
|   | 678376 | 394 |  |             for ( var i = 0; i < a.Length; ++i ) | 
|   | 336910 | 395 |  |                 if ( a[i] != b[i] ) | 
|   | 0 | 396 |  |                     return false; | 
|   |  | 397 |  |  | 
|   | 2278 | 398 |  |             return true; | 
|   |  | 399 |  |         } | 
|   |  | 400 |  |  | 
|   |  | 401 |  |         private static bool AreEqual( IList<string> a, IList<string> b ) | 
|   |  | 402 |  |         { | 
|   | 440 | 403 |  |             if ( a == b ) | 
|   | 340 | 404 |  |                 return true; | 
|   |  | 405 |  |  | 
|   | 100 | 406 |  |             if ( ( a == null ) != ( b == null ) ) | 
|   | 4 | 407 |  |                 return false; | 
|   |  | 408 |  |  | 
|   | 96 | 409 |  |             if ( a.Count != b.Count ) | 
|   | 0 | 410 |  |                 return false; | 
|   |  | 411 |  |  | 
|   | 688 | 412 |  |             for ( var i = 0; i < a.Count; ++i ) | 
|   | 248 | 413 |  |                 if ( a[i] != b[i] ) | 
|   | 0 | 414 |  |                     return false; | 
|   |  | 415 |  |  | 
|   | 96 | 416 |  |             return true; | 
|   |  | 417 |  |         } | 
|   |  | 418 |  |  | 
|   |  | 419 |  |         public override int GetHashCode() | 
|   |  | 420 |  |         { | 
|   | 40 | 421 |  |             var hashCode = 48313; // setting it to a random prime number | 
|   |  | 422 |  |  | 
|   | 40 | 423 |  |             if ( Kid != null ) | 
|   |  | 424 |  |             { | 
|   | 22 | 425 |  |                 hashCode += Kid.GetHashCode(); | 
|   |  | 426 |  |             } | 
|   |  | 427 |  |  | 
|   | 40 | 428 |  |             switch ( Kty ) | 
|   |  | 429 |  |             { | 
|   |  | 430 |  |                 case JsonWebKeyType.Octet: | 
|   | 14 | 431 |  |                     return hashCode + GetHashCode( K ); | 
|   |  | 432 |  |  | 
|   |  | 433 |  |                 case JsonWebKeyType.EllipticCurve: | 
|   | 0 | 434 |  |                     return hashCode + GetHashCode( X ); | 
|   |  | 435 |  |  | 
|   |  | 436 |  |                 case JsonWebKeyType.Rsa: | 
|   | 0 | 437 |  |                     return hashCode + GetHashCode( N ); | 
|   |  | 438 |  |  | 
|   |  | 439 |  |                 case JsonWebKeyType.EllipticCurveHsm: | 
|   |  | 440 |  |                 case JsonWebKeyType.RsaHsm: | 
|   | 18 | 441 |  |                     return hashCode + GetHashCode( T ); | 
|   |  | 442 |  |  | 
|   |  | 443 |  |                 default: | 
|   | 8 | 444 |  |                     return hashCode; | 
|   |  | 445 |  |             } | 
|   |  | 446 |  |         } | 
|   |  | 447 |  |  | 
|   |  | 448 |  |         private static int GetHashCode( byte[] obj ) | 
|   |  | 449 |  |         { | 
|   | 32 | 450 |  |             if ( obj == null || obj.Length == 0 ) | 
|   | 10 | 451 |  |                 return 0; | 
|   |  | 452 |  |  | 
|   | 22 | 453 |  |             var hashCode = 0; | 
|   |  | 454 |  |  | 
|   |  | 455 |  |             // Rotate by 3 bits and XOR the new value. | 
|   | 912 | 456 |  |             foreach ( var v in obj ) | 
|   | 434 | 457 |  |                 hashCode = ( hashCode << 3 ) | ( hashCode >> 29 ) ^ v; | 
|   |  | 458 |  |  | 
|   | 22 | 459 |  |             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 |  |         { | 
|   | 6 | 468 |  |             switch ( Kty ) | 
|   |  | 469 |  |             { | 
|   |  | 470 |  |                 case JsonWebKeyType.Octet: | 
|   | 2 | 471 |  |                     return K != null; | 
|   |  | 472 |  |  | 
|   |  | 473 |  |                 case JsonWebKeyType.EllipticCurve: | 
|   |  | 474 |  |                 case JsonWebKeyType.EllipticCurveHsm: | 
|   | 0 | 475 |  |                     return D != null; | 
|   |  | 476 |  |  | 
|   |  | 477 |  |                 case JsonWebKeyType.Rsa: | 
|   |  | 478 |  |                 case JsonWebKeyType.RsaHsm: | 
|   | 4 | 479 |  |                     return D != null && DP != null && DQ != null && QI != null && P != null && Q != null; | 
|   |  | 480 |  |  | 
|   |  | 481 |  |                 default: | 
|   | 0 | 482 |  |                     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 |  |         { | 
|   | 8 | 493 |  |             var verifierOptions = | 
|   | 8 | 494 |  |                 JsonWebKeyVerifier.Options.DenyIncompatibleOperations | | 
|   | 8 | 495 |  |                 JsonWebKeyVerifier.Options.DenyExtraneousFields; | 
|   |  | 496 |  |  | 
|   | 8 | 497 |  |             string unused = null; | 
|   | 8 | 498 |  |             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 |  |         { | 
|   | 2 | 507 |  |             if ( !Kty.Equals( JsonWebKeyType.Octet ) ) | 
|   | 0 | 508 |  |                 throw new InvalidOperationException( "key is not an octet key" ); | 
|   |  | 509 |  |  | 
|   | 2 | 510 |  |             if ( K == null ) | 
|   | 0 | 511 |  |                 throw new InvalidOperationException( "key does not contain a value" ); | 
|   |  | 512 |  |  | 
|   | 2 | 513 |  |             var result = Aes.Create(); | 
|   |  | 514 |  |  | 
|   | 2 | 515 |  |             if ( result != null ) | 
|   | 2 | 516 |  |                 result.Key = K; | 
|   |  | 517 |  |  | 
|   | 2 | 518 |  |             return result; | 
|   |  | 519 |  |         } | 
|   |  | 520 |  |  | 
|   |  | 521 |  |         /// <summary> | 
|   |  | 522 |  |         /// Remove leading zeros from all RSA parameters. | 
|   |  | 523 |  |         /// </summary> | 
|   |  | 524 |  |         public void CanonicalizeRSA() | 
|   |  | 525 |  |         { | 
|   | 90 | 526 |  |             N = RemoveLeadingZeros( N ); | 
|   | 90 | 527 |  |             E = RemoveLeadingZeros( E ); | 
|   | 90 | 528 |  |             D = RemoveLeadingZeros( D ); | 
|   | 90 | 529 |  |             DP = RemoveLeadingZeros( DP ); | 
|   | 90 | 530 |  |             DQ = RemoveLeadingZeros( DQ ); | 
|   | 90 | 531 |  |             QI = RemoveLeadingZeros( QI ); | 
|   | 90 | 532 |  |             P = RemoveLeadingZeros( P ); | 
|   | 90 | 533 |  |             Q = RemoveLeadingZeros( Q ); | 
|   | 90 | 534 |  |         } | 
|   |  | 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 |  |         { | 
|   | 786 | 543 |  |             var rsaParameters = ToRSAParameters( includePrivateParameters ); | 
|   | 586 | 544 |  |             var result = RSA.Create(); | 
|   | 586 | 545 |  |             result.ImportParameters( rsaParameters ); | 
|   | 586 | 546 |  |             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 |  |         { | 
|   | 996 | 556 |  |             if ( Kty != JsonWebKeyType.Rsa && Kty != JsonWebKeyType.RsaHsm ) | 
|   | 0 | 557 |  |                 throw new ArgumentException( "JsonWebKey is not a RSA key" ); | 
|   |  | 558 |  |  | 
|   | 996 | 559 |  |             VerifyNonZero( nameof( N ), N ); | 
|   | 916 | 560 |  |             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 |  |  | 
|   | 836 | 565 |  |             var result = new RSAParameters(); | 
|   | 836 | 566 |  |             result.Modulus = RemoveLeadingZeros( N ); | 
|   | 836 | 567 |  |             result.Exponent = ForceLength( nameof( E ), E, 4 ); | 
|   |  | 568 |  |  | 
|   | 836 | 569 |  |             if ( includePrivateParameters ) | 
|   |  | 570 |  |             { | 
|   | 488 | 571 |  |                 var bitlen = result.Modulus.Length * 8; | 
|   |  | 572 |  |  | 
|   | 488 | 573 |  |                 result.D = ForceLength( nameof( D ), D, bitlen / 8 ); | 
|   | 448 | 574 |  |                 result.DP = ForceLength( nameof( DP ), DP, bitlen / 16 ); | 
|   | 408 | 575 |  |                 result.DQ = ForceLength( nameof( DQ ), DQ, bitlen / 16 ); | 
|   | 368 | 576 |  |                 result.InverseQ = ForceLength( nameof( QI ), QI, bitlen / 16 ); | 
|   | 328 | 577 |  |                 result.P = ForceLength( nameof( P ), P, bitlen / 16 ); | 
|   | 288 | 578 |  |                 result.Q = ForceLength( nameof( Q ), Q, bitlen / 16 ); | 
|   |  | 579 |  |             } | 
|   |  | 580 |  |  | 
|   | 596 | 581 |  |             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 |  |         { | 
|   | 108 | 591 |  |             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 |  |         { | 
|   | 116 | 601 |  |             if ( Kty != JsonWebKeyType.EllipticCurve && Kty != JsonWebKeyType.EllipticCurveHsm ) | 
|   | 0 | 602 |  |                 throw new ArgumentException( "JsonWebKey is not an EC key" ); | 
|   |  | 603 |  |  | 
|   | 116 | 604 |  |             VerifyNonZero( nameof( X ), X ); | 
|   | 116 | 605 |  |             VerifyNonZero( nameof( Y ), Y ); | 
|   |  | 606 |  |  | 
|   | 116 | 607 |  |             var requiredSize = JsonWebKeyCurveName.GetKeyParameterSize( CurveName ); | 
|   | 116 | 608 |  |             if ( requiredSize < 0 ) | 
|   |  | 609 |  |             { | 
|   | 0 | 610 |  |                 var curveDesc = CurveName == null ? "null" : $"\"{CurveName}\""; | 
|   | 0 | 611 |  |                 throw new ArgumentException( $"Invalid curve type: {curveDesc}" ); | 
|   |  | 612 |  |             } | 
|   |  | 613 |  |  | 
|   | 116 | 614 |  |             var result = new ECParameters(); | 
|   | 116 | 615 |  |             result.Curve = CurveName; | 
|   | 116 | 616 |  |             result.X = ForceLength( nameof( X ), X, requiredSize ); | 
|   | 116 | 617 |  |             result.Y = ForceLength( nameof( Y ), Y, requiredSize ); | 
|   |  | 618 |  |  | 
|   | 116 | 619 |  |             if ( includePrivateParameters ) | 
|   |  | 620 |  |             { | 
|   | 36 | 621 |  |                 VerifyNonZero( nameof( D ), D ); | 
|   | 28 | 622 |  |                 result.D = ForceLength( nameof( D ), D, requiredSize ); | 
|   |  | 623 |  |             } | 
|   |  | 624 |  |  | 
|   | 108 | 625 |  |             return result; | 
|   |  | 626 |  |         } | 
|   |  | 627 |  |  | 
|   |  | 628 |  |         private static void VerifyNonZero( string name, byte[] value ) | 
|   |  | 629 |  |         { | 
|   | 2180 | 630 |  |             if ( value != null ) | 
|   | 6872 | 631 |  |                 foreach ( var t in value ) | 
|   | 2350 | 632 |  |                     if ( t != 0 ) | 
|   | 2012 | 633 |  |                         return; | 
|   |  | 634 |  |  | 
|   | 168 | 635 |  |             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). | 
|   | 1556 | 645 |  |             if ( value == null || value.Length <= 1 || value[0] != 0 ) | 
|   | 1446 | 646 |  |                 return value; | 
|   |  | 647 |  |  | 
|   |  | 648 |  |             // We know that value[0] is zero, so we start from 1. | 
|   | 484 | 649 |  |             for ( var i = 1; i < value.Length; ++i ) | 
|   |  | 650 |  |             { | 
|   | 242 | 651 |  |                 if ( value[i] != 0 ) | 
|   |  | 652 |  |                 { | 
|   | 110 | 653 |  |                     var result = new byte[value.Length - i]; | 
|   | 110 | 654 |  |                     Array.Copy( value, i, result, 0, result.Length ); | 
|   | 110 | 655 |  |                     return result; | 
|   |  | 656 |  |                 } | 
|   |  | 657 |  |             } | 
|   |  | 658 |  |  | 
|   |  | 659 |  |             // If all is zero, return an array with a single useful zero. | 
|   | 0 | 660 |  |             return new byte[] {0}; | 
|   |  | 661 |  |         } | 
|   |  | 662 |  |  | 
|   |  | 663 |  |         private static byte[] ForceLength( string name, byte[] value, int requiredLength ) | 
|   |  | 664 |  |         { | 
|   | 3424 | 665 |  |             if ( value == null || value.Length == 0 ) | 
|   | 240 | 666 |  |                 throw new ArgumentException( $"Value of \"{name}\" is null or empty." ); | 
|   |  | 667 |  |  | 
|   | 3184 | 668 |  |             if ( value.Length == requiredLength ) | 
|   | 2216 | 669 |  |                 return value; | 
|   |  | 670 |  |  | 
|   | 968 | 671 |  |             if ( value.Length < requiredLength ) | 
|   |  | 672 |  |             { | 
|   | 888 | 673 |  |                 var padded = new byte[requiredLength]; | 
|   | 888 | 674 |  |                 Array.Copy( value, 0, padded, requiredLength - value.Length, value.Length ); | 
|   | 888 | 675 |  |                 return padded; | 
|   |  | 676 |  |             } | 
|   |  | 677 |  |  | 
|   |  | 678 |  |             // value.Length > requiredLength | 
|   |  | 679 |  |  | 
|   |  | 680 |  |             // Make sure the extra bytes are all zeros. | 
|   | 80 | 681 |  |             var extraLen = value.Length - requiredLength; | 
|   | 800 | 682 |  |             for ( var i = 0; i < extraLen; ++i ) | 
|   | 320 | 683 |  |                 if ( value[i] != 0 ) | 
|   | 0 | 684 |  |                     throw new ArgumentException( $"Invalid length of \"{name}\": expected at most {requiredLength} bytes | 
|   |  | 685 |  |  | 
|   | 80 | 686 |  |             var trimmed = new byte[requiredLength]; | 
|   | 80 | 687 |  |             Array.Copy( value, value.Length - requiredLength, trimmed, 0, requiredLength ); | 
|   | 80 | 688 |  |             return trimmed; | 
|   |  | 689 |  |         } | 
|   |  | 690 |  |  | 
|   |  | 691 |  |         public override string ToString() | 
|   |  | 692 |  |         { | 
|   | 430 | 693 |  |             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: | 
|   | 10 | 705 |  |             ZeroArray( K ); | 
|   | 10 | 706 |  |             K = null; | 
|   |  | 707 |  |  | 
|   |  | 708 |  |             // Rsa keys: | 
|   |  | 709 |  |  | 
|   |  | 710 |  |             // We want to clear public key to avoid identification. | 
|   | 10 | 711 |  |             ZeroArray( N ); | 
|   | 10 | 712 |  |             ZeroArray( E ); | 
|   |  | 713 |  |  | 
|   |  | 714 |  |             // Private material of RSA: | 
|   | 10 | 715 |  |             ZeroArray( D ); | 
|   | 10 | 716 |  |             ZeroArray( DP ); | 
|   | 10 | 717 |  |             ZeroArray( DQ ); | 
|   | 10 | 718 |  |             ZeroArray( QI ); | 
|   | 10 | 719 |  |             ZeroArray( P ); | 
|   | 10 | 720 |  |             ZeroArray( Q ); | 
|   | 10 | 721 |  |             N = E = D = DP = DQ = QI = P = Q = null; | 
|   |  | 722 |  |  | 
|   |  | 723 |  |             // RsaHsm keys: | 
|   | 10 | 724 |  |             ZeroArray( T ); | 
|   | 10 | 725 |  |             T = null; | 
|   |  | 726 |  |  | 
|   |  | 727 |  |             // Elliptic curve | 
|   | 10 | 728 |  |             ZeroArray( X ); | 
|   | 10 | 729 |  |             ZeroArray( Y ); | 
|   | 10 | 730 |  |             ZeroArray( D ); // D is intentionally repeated. | 
|   | 10 | 731 |  |             X = Y = D = null; | 
|   |  | 732 |  |  | 
|   | 10 | 733 |  |             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 | 
|   | 0 | 746 |  |                     throw new NotImplementedException( $"Unsupported kty: {Kty}" ); | 
|   |  | 747 |  |             } | 
|   | 10 | 748 |  |         } | 
|   |  | 749 |  |  | 
|   |  | 750 |  |         private static void ZeroArray( byte[] a ) | 
|   |  | 751 |  |         { | 
|   | 130 | 752 |  |             if ( a == null ) | 
|   | 10 | 753 |  |                 return; | 
|   | 120 | 754 |  |             Array.Clear( a, 0, a.Length ); | 
|   | 120 | 755 |  |         } | 
|   |  | 756 |  |     } | 
|   |  | 757 |  | } |