| | 1 | | // Copyright (c) Microsoft Corporation. All rights reserved. |
| | 2 | | // Licensed under the MIT License. |
| | 3 | |
|
| | 4 | | using System; |
| | 5 | | using System.Collections.Generic; |
| | 6 | | using System.Collections.ObjectModel; |
| | 7 | | using System.Runtime.CompilerServices; |
| | 8 | | using System.Security.Cryptography; |
| | 9 | | using System.Text.Json; |
| | 10 | | using Azure.Core; |
| | 11 | |
|
| | 12 | | namespace Azure.Security.KeyVault.Keys |
| | 13 | | { |
| | 14 | | /// <summary> |
| | 15 | | /// A JSON Web Key (JWK) is a JavaScript Object Notation (JSON) data |
| | 16 | | /// structure that represents a cryptographic key. |
| | 17 | | /// For more information, see <see href="http://tools.ietf.org/html/draft-ietf-jose-json-web-key-18">JSON Web Key (J |
| | 18 | | /// </summary> |
| | 19 | | public class JsonWebKey : IJsonDeserializable, IJsonSerializable |
| | 20 | | { |
| | 21 | | private const string KeyIdPropertyName = "kid"; |
| | 22 | | private const string KeyTypePropertyName = "kty"; |
| | 23 | | private const string KeyOpsPropertyName = "key_ops"; |
| | 24 | | private const string CurveNamePropertyName = "crv"; |
| | 25 | | private const string NPropertyName = "n"; |
| | 26 | | private const string EPropertyName = "e"; |
| | 27 | | private const string DPPropertyName = "dp"; |
| | 28 | | private const string DQPropertyName = "dq"; |
| | 29 | | private const string QIPropertyName = "qi"; |
| | 30 | | private const string PPropertyName = "p"; |
| | 31 | | private const string QPropertyName = "q"; |
| | 32 | | private const string XPropertyName = "x"; |
| | 33 | | private const string YPropertyName = "y"; |
| | 34 | | private const string DPropertyName = "d"; |
| | 35 | | private const string KPropertyName = "k"; |
| | 36 | | private const string TPropertyName = "key_hsm"; |
| | 37 | |
|
| 2 | 38 | | private static readonly JsonEncodedText s_keyTypePropertyNameBytes = JsonEncodedText.Encode(KeyTypePropertyName) |
| 2 | 39 | | private static readonly JsonEncodedText s_keyOpsPropertyNameBytes = JsonEncodedText.Encode(KeyOpsPropertyName); |
| 2 | 40 | | private static readonly JsonEncodedText s_curveNamePropertyNameBytes = JsonEncodedText.Encode(CurveNamePropertyN |
| 2 | 41 | | private static readonly JsonEncodedText s_nPropertyNameBytes = JsonEncodedText.Encode(NPropertyName); |
| 2 | 42 | | private static readonly JsonEncodedText s_ePropertyNameBytes = JsonEncodedText.Encode(EPropertyName); |
| 2 | 43 | | private static readonly JsonEncodedText s_dPPropertyNameBytes = JsonEncodedText.Encode(DPPropertyName); |
| 2 | 44 | | private static readonly JsonEncodedText s_dQPropertyNameBytes = JsonEncodedText.Encode(DQPropertyName); |
| 2 | 45 | | private static readonly JsonEncodedText s_qIPropertyNameBytes = JsonEncodedText.Encode(QIPropertyName); |
| 2 | 46 | | private static readonly JsonEncodedText s_pPropertyNameBytes = JsonEncodedText.Encode(PPropertyName); |
| 2 | 47 | | private static readonly JsonEncodedText s_qPropertyNameBytes = JsonEncodedText.Encode(QPropertyName); |
| 2 | 48 | | private static readonly JsonEncodedText s_xPropertyNameBytes = JsonEncodedText.Encode(XPropertyName); |
| 2 | 49 | | private static readonly JsonEncodedText s_yPropertyNameBytes = JsonEncodedText.Encode(YPropertyName); |
| 2 | 50 | | private static readonly JsonEncodedText s_dPropertyNameBytes = JsonEncodedText.Encode(DPropertyName); |
| 2 | 51 | | private static readonly JsonEncodedText s_kPropertyNameBytes = JsonEncodedText.Encode(KPropertyName); |
| 2 | 52 | | private static readonly JsonEncodedText s_tPropertyNameBytes = JsonEncodedText.Encode(TPropertyName); |
| | 53 | |
|
| 2 | 54 | | private static readonly KeyOperation[] s_aesKeyOperation = { KeyOperation.Encrypt, KeyOperation.Decrypt, KeyOper |
| 2 | 55 | | private static readonly KeyOperation[] s_rSAPublicKeyOperation = { KeyOperation.Encrypt, KeyOperation.Verify, Ke |
| 2 | 56 | | private static readonly KeyOperation[] s_rSAPrivateKeyOperation = { KeyOperation.Encrypt, KeyOperation.Decrypt, |
| 2 | 57 | | private static readonly KeyOperation[] s_eCPublicKeyOperation = { KeyOperation.Sign }; |
| 2 | 58 | | private static readonly KeyOperation[] s_eCPrivateKeyOperation = { KeyOperation.Sign, KeyOperation.Verify }; |
| | 59 | |
|
| | 60 | | private readonly IList<KeyOperation> _keyOps; |
| | 61 | |
|
| | 62 | | /// <summary> |
| | 63 | | /// Gets the identifier of the key. This is not limited to a <see cref="Uri"/>. |
| | 64 | | /// </summary> |
| 2604 | 65 | | public string Id { get; set; } |
| | 66 | |
|
| | 67 | | /// <summary> |
| | 68 | | /// Gets the <see cref="KeyType"/> for this <see cref="JsonWebKey"/>. |
| | 69 | | /// </summary> |
| 2602 | 70 | | public KeyType KeyType { get; set; } |
| | 71 | |
|
| | 72 | | /// <summary> |
| | 73 | | /// Gets a list of <see cref="KeyOperation"/> values supported by this key. |
| | 74 | | /// </summary> |
| 1274 | 75 | | public IReadOnlyCollection<KeyOperation> KeyOps { get; } |
| | 76 | |
|
| 744 | 77 | | internal JsonWebKey() : this(null) |
| | 78 | | { |
| 744 | 79 | | } |
| | 80 | |
|
| 784 | 81 | | internal JsonWebKey(IEnumerable<KeyOperation> keyOps) |
| | 82 | | { |
| 784 | 83 | | _keyOps = keyOps is null ? new List<KeyOperation>() : new List<KeyOperation>(keyOps); |
| 784 | 84 | | KeyOps = new ReadOnlyCollection<KeyOperation>(_keyOps); |
| 784 | 85 | | } |
| | 86 | |
|
| | 87 | | /// <summary> |
| | 88 | | /// Initializes a new instance of the <see cref="JsonWebKey"/> class using type <see cref="KeyType.Oct"/>. |
| | 89 | | /// </summary> |
| | 90 | | /// <param name="aesProvider">An <see cref="Aes"/> provider.</param> |
| | 91 | | /// <param name="keyOps"> |
| | 92 | | /// Optional list of supported <see cref="KeyOperation"/> values. If null, the default for the key type is used, |
| | 93 | | /// <see cref="KeyOperation.Encrypt"/>, <see cref="KeyOperation.Decrypt"/>, <see cref="KeyOperation.WrapKey"/>, |
| | 94 | | /// </param> |
| | 95 | | /// <exception cref="ArgumentNullException"><paramref name="aesProvider"/> is null.</exception> |
| 20 | 96 | | public JsonWebKey(Aes aesProvider, IEnumerable<KeyOperation> keyOps = default) |
| | 97 | | { |
| 20 | 98 | | Argument.AssertNotNull(aesProvider, nameof(aesProvider)); |
| | 99 | |
|
| 20 | 100 | | _keyOps = new List<KeyOperation>(keyOps ?? s_aesKeyOperation); |
| 20 | 101 | | KeyOps = new ReadOnlyCollection<KeyOperation>(_keyOps); |
| | 102 | |
|
| 20 | 103 | | KeyType = KeyType.Oct; |
| 20 | 104 | | K = aesProvider.Key; |
| 20 | 105 | | } |
| | 106 | |
|
| | 107 | | /// <summary> |
| | 108 | | /// Initializes a new instance of the <see cref="JsonWebKey"/> class using type <see cref="KeyType.Ec"/>. |
| | 109 | | /// </summary> |
| | 110 | | /// <param name="ecdsa">An <see cref="ECDsa"/> provider.</param> |
| | 111 | | /// <param name="includePrivateParameters">Whether to include the private key.</param> |
| | 112 | | /// <param name="keyOps"> |
| | 113 | | /// Optional list of supported <see cref="KeyOperation"/> values. If null, the default for the key type is used, |
| | 114 | | /// <see cref="KeyOperation.Sign"/>, and <see cref="KeyOperation.Decrypt"/> if <paramref name="includePrivatePar |
| | 115 | | /// </param> |
| | 116 | | /// <exception cref="ArgumentNullException"><paramref name="ecdsa"/> is null.</exception> |
| | 117 | | /// <exception cref="InvalidOperationException">The elliptic curve name is invalid.</exception> |
| 92 | 118 | | public JsonWebKey(ECDsa ecdsa, bool includePrivateParameters = default, IEnumerable<KeyOperation> keyOps = defau |
| | 119 | | { |
| 92 | 120 | | Argument.AssertNotNull(ecdsa, nameof(ecdsa)); |
| | 121 | |
|
| 92 | 122 | | _keyOps = new List<KeyOperation>(keyOps ?? (includePrivateParameters ? s_eCPrivateKeyOperation : s_eCPublicK |
| 92 | 123 | | KeyOps = new ReadOnlyCollection<KeyOperation>(_keyOps); |
| | 124 | |
|
| 92 | 125 | | Initialize(ecdsa, includePrivateParameters); |
| 90 | 126 | | } |
| | 127 | |
|
| | 128 | | /// <summary> |
| | 129 | | /// Initializes a new instance of the <see cref="JsonWebKey"/> class using type <see cref="KeyType.Rsa"/>. |
| | 130 | | /// </summary> |
| | 131 | | /// <param name="rsaProvider">An <see cref="RSA"/> provider.</param> |
| | 132 | | /// <param name="includePrivateParameters">Whether to include the private key.</param> |
| | 133 | | /// <param name="keyOps"> |
| | 134 | | /// Optional list of supported <see cref="KeyOperation"/> values. If null, the default for the key type is used, |
| | 135 | | /// <see cref="KeyOperation.Encrypt"/>, <see cref="KeyOperation.Verify"/>, and <see cref="KeyOperation.WrapKey"/ |
| | 136 | | /// and <see cref="KeyOperation.Decrypt"/>, <see cref="KeyOperation.Sign"/>, and <see cref="KeyOperation.UnwrapK |
| | 137 | | /// </param> |
| | 138 | | /// <exception cref="ArgumentNullException"><paramref name="rsaProvider"/> is null.</exception> |
| 116 | 139 | | public JsonWebKey(RSA rsaProvider, bool includePrivateParameters = default, IEnumerable<KeyOperation> keyOps = d |
| | 140 | | { |
| 116 | 141 | | Argument.AssertNotNull(rsaProvider, nameof(rsaProvider)); |
| | 142 | |
|
| 116 | 143 | | _keyOps = new List<KeyOperation>(keyOps ?? (includePrivateParameters ? s_rSAPrivateKeyOperation : s_rSAPubli |
| 116 | 144 | | KeyOps = new ReadOnlyCollection<KeyOperation>(_keyOps); |
| | 145 | |
|
| 116 | 146 | | KeyType = KeyType.Rsa; |
| 116 | 147 | | RSAParameters rsaParameters = rsaProvider.ExportParameters(includePrivateParameters); |
| | 148 | |
|
| 114 | 149 | | E = rsaParameters.Exponent; |
| 114 | 150 | | N = rsaParameters.Modulus; |
| | 151 | |
|
| 114 | 152 | | D = rsaParameters.D; |
| 114 | 153 | | DP = rsaParameters.DP; |
| 114 | 154 | | DQ = rsaParameters.DQ; |
| 114 | 155 | | P = rsaParameters.P; |
| 114 | 156 | | Q = rsaParameters.Q; |
| 114 | 157 | | QI = rsaParameters.InverseQ; |
| 114 | 158 | | } |
| | 159 | |
|
| | 160 | | #region RSA Public Key Parameters |
| | 161 | |
|
| | 162 | | /// <summary> |
| | 163 | | /// Gets the RSA modulus. |
| | 164 | | /// </summary> |
| 978 | 165 | | public byte[] N { get; set; } |
| | 166 | |
|
| | 167 | | /// <summary> |
| | 168 | | /// Gets RSA public exponent. |
| | 169 | | /// </summary> |
| 956 | 170 | | public byte[] E { get; set; } |
| | 171 | |
|
| | 172 | | #endregion |
| | 173 | |
|
| | 174 | | #region RSA Private Key Parameters |
| | 175 | |
|
| | 176 | | /// <summary> |
| | 177 | | /// Gets the RSA private key parameter. |
| | 178 | | /// </summary> |
| 570 | 179 | | public byte[] DP { get; set; } |
| | 180 | |
|
| | 181 | | /// <summary> |
| | 182 | | /// Gets the RSA private key parameter. |
| | 183 | | /// </summary> |
| 570 | 184 | | public byte[] DQ { get; set; } |
| | 185 | |
|
| | 186 | | /// <summary> |
| | 187 | | /// Gets the RSA private key parameter. |
| | 188 | | /// </summary> |
| 570 | 189 | | public byte[] QI { get; set; } |
| | 190 | |
|
| | 191 | | /// <summary> |
| | 192 | | /// Gets the RSA secret prime. |
| | 193 | | /// </summary> |
| 570 | 194 | | public byte[] P { get; set; } |
| | 195 | |
|
| | 196 | | /// <summary> |
| | 197 | | /// Gets the RSA secret prime. |
| | 198 | | /// </summary> |
| 570 | 199 | | public byte[] Q { get; set; } |
| | 200 | |
|
| | 201 | | #endregion |
| | 202 | |
|
| | 203 | | #region EC Public Key Parameters |
| | 204 | |
|
| | 205 | | /// <summary> |
| | 206 | | /// Gets the name of the elliptical curve. |
| | 207 | | /// </summary> |
| 1334 | 208 | | public KeyCurveName? CurveName { get; set; } |
| | 209 | |
|
| | 210 | | /// <summary> |
| | 211 | | /// Gets the X coordinate of the elliptic curve point. |
| | 212 | | /// </summary> |
| 1050 | 213 | | public byte[] X { get; set; } |
| | 214 | |
|
| | 215 | | /// <summary> |
| | 216 | | /// Gets the Y coordinate for the elliptic curve point. |
| | 217 | | /// </summary> |
| 1042 | 218 | | public byte[] Y { get; set; } |
| | 219 | |
|
| | 220 | | #endregion |
| | 221 | |
|
| | 222 | | #region EC and RSA Private Key Parameters |
| | 223 | |
|
| | 224 | | /// <summary> |
| | 225 | | /// Gets the RSA private exponent or EC private key. |
| | 226 | | /// </summary> |
| 956 | 227 | | public byte[] D { get; set; } |
| | 228 | |
|
| | 229 | | #endregion |
| | 230 | |
|
| | 231 | | #region Symmetric Key Parameters |
| | 232 | |
|
| | 233 | | /// <summary> |
| | 234 | | /// Gets the symmetric key. |
| | 235 | | /// </summary> |
| 462 | 236 | | public byte[] K { get; set; } |
| | 237 | |
|
| | 238 | | #endregion |
| | 239 | |
|
| | 240 | | /// <summary> |
| | 241 | | /// Gets the HSM token used with "Bring Your Own Key". |
| | 242 | | /// </summary> |
| 390 | 243 | | public byte[] T { get; set; } |
| | 244 | |
|
| | 245 | | internal bool HasPrivateKey |
| | 246 | | { |
| | 247 | | get |
| | 248 | | { |
| 192 | 249 | | if (KeyType == KeyType.Rsa || KeyType == KeyType.Ec || KeyType == KeyType.RsaHsm || KeyType == KeyType.E |
| | 250 | | { |
| 192 | 251 | | return D != null; |
| | 252 | | } |
| | 253 | |
|
| 0 | 254 | | if (KeyType == KeyType.Oct) |
| | 255 | | { |
| 0 | 256 | | return K != null; |
| | 257 | | } |
| | 258 | |
|
| 0 | 259 | | return false; |
| | 260 | | } |
| | 261 | | } |
| | 262 | |
|
| | 263 | | /// <summary> |
| | 264 | | /// Converts this <see cref="JsonWebKey"/> of type <see cref="KeyType.Oct"/> to an <see cref="Aes"/> object. |
| | 265 | | /// </summary> |
| | 266 | | /// <returns>An <see cref="Aes"/> object.</returns> |
| | 267 | | /// <exception cref="InvalidOperationException">This key is not of type <see cref="KeyType.Oct"/> or <see cref=" |
| | 268 | | public Aes ToAes() |
| | 269 | | { |
| 6 | 270 | | if (KeyType != KeyType.Oct) |
| | 271 | | { |
| 2 | 272 | | throw new InvalidOperationException($"key is not an {nameof(KeyType.Oct)} key"); |
| | 273 | | } |
| | 274 | |
|
| 4 | 275 | | if (K is null) |
| | 276 | | { |
| 2 | 277 | | throw new InvalidOperationException("key does not contain a value"); |
| | 278 | | } |
| | 279 | |
|
| 2 | 280 | | Aes key = Aes.Create(); |
| 2 | 281 | | key.Key = K; |
| | 282 | |
|
| 2 | 283 | | return key; |
| | 284 | | } |
| | 285 | |
|
| | 286 | | /// <summary> |
| | 287 | | /// Converts this <see cref="JsonWebKey"/> of type <see cref="KeyType.Ec"/> or <see cref="KeyType.EcHsm"/> to an |
| | 288 | | /// </summary> |
| | 289 | | /// <param name="includePrivateParameters">Whether to include private parameters.</param> |
| | 290 | | /// <returns>An <see cref="ECDsa"/> object.</returns> |
| | 291 | | /// <exception cref="InvalidOperationException">This key is not of type <see cref="KeyType.Ec"/> or <see cref="K |
| 40 | 292 | | public ECDsa ToECDsa(bool includePrivateParameters = false) => ToECDsa(includePrivateParameters, true); |
| | 293 | |
|
| | 294 | | internal ECDsa ToECDsa(bool includePrivateParameters, bool throwIfNotSupported) |
| | 295 | | { |
| 72 | 296 | | if (KeyType != KeyType.Ec && KeyType != KeyType.EcHsm) |
| | 297 | | { |
| 2 | 298 | | throw new InvalidOperationException($"key is not an {nameof(KeyType.Ec)} or {nameof(KeyType.EcHsm)} type |
| | 299 | | } |
| | 300 | |
|
| 70 | 301 | | ValidateKeyParameter(nameof(X), X); |
| 64 | 302 | | ValidateKeyParameter(nameof(Y), Y); |
| | 303 | |
|
| 58 | 304 | | return Convert(includePrivateParameters, throwIfNotSupported); |
| | 305 | | } |
| | 306 | |
|
| | 307 | | /// <summary> |
| | 308 | | /// Converts this <see cref="JsonWebKey"/> of type <see cref="KeyType.Rsa"/> or <see cref="KeyType.RsaHsm"/> to |
| | 309 | | /// </summary> |
| | 310 | | /// <param name="includePrivateParameters">Whether to include private parameters.</param> |
| | 311 | | /// <returns>An <see cref="RSA"/> object.</returns> |
| | 312 | | /// <exception cref="InvalidOperationException">This key is not of type <see cref="KeyType.Rsa"/> or <see cref=" |
| | 313 | | public RSA ToRSA(bool includePrivateParameters = false) |
| | 314 | | { |
| 72 | 315 | | if (KeyType != KeyType.Rsa && KeyType != KeyType.RsaHsm) |
| | 316 | | { |
| 2 | 317 | | throw new InvalidOperationException($"key is not an {nameof(KeyType.Rsa)} or {nameof(KeyType.RsaHsm)} ty |
| | 318 | | } |
| | 319 | |
|
| 70 | 320 | | ValidateKeyParameter(nameof(E), E); |
| 64 | 321 | | ValidateKeyParameter(nameof(N), N); |
| | 322 | |
|
| | 323 | | // Key parameter length requirements defined by 2.2.2.9.1 RSA Private Key BLOB specification: https://docs.m |
| 58 | 324 | | var rsaParameters = new RSAParameters |
| 58 | 325 | | { |
| 58 | 326 | | Exponent = E, |
| 58 | 327 | | Modulus = TrimBuffer(N), |
| 58 | 328 | | }; |
| | 329 | |
|
| 58 | 330 | | if (includePrivateParameters && HasPrivateKey) |
| | 331 | | { |
| 26 | 332 | | int byteLength = rsaParameters.Modulus.Length; |
| 26 | 333 | | rsaParameters.D = ForceBufferLength(nameof(D), D, byteLength); |
| | 334 | |
|
| 26 | 335 | | byteLength >>= 1; |
| 26 | 336 | | rsaParameters.DP = ForceBufferLength(nameof(DP), DP, byteLength); |
| 26 | 337 | | rsaParameters.DQ = ForceBufferLength(nameof(DQ), DQ, byteLength); |
| 26 | 338 | | rsaParameters.P = ForceBufferLength(nameof(P), P, byteLength); |
| 26 | 339 | | rsaParameters.Q = ForceBufferLength(nameof(Q), Q, byteLength); |
| 26 | 340 | | rsaParameters.InverseQ = ForceBufferLength(nameof(QI), QI, byteLength); |
| | 341 | | } |
| | 342 | |
|
| 58 | 343 | | RSA rsa = RSA.Create(); |
| 58 | 344 | | rsa.ImportParameters(rsaParameters); |
| | 345 | |
|
| 58 | 346 | | return rsa; |
| | 347 | | } |
| | 348 | |
|
| | 349 | | internal bool SupportsOperation(KeyOperation operation) |
| | 350 | | { |
| 188 | 351 | | if (KeyOps != null) |
| | 352 | | { |
| 1088 | 353 | | for (int i = 0; i < KeyOps.Count; ++i) |
| | 354 | | { |
| 542 | 355 | | if (_keyOps[i] == operation) |
| | 356 | | { |
| 186 | 357 | | return true; |
| | 358 | | } |
| | 359 | | } |
| | 360 | | } |
| | 361 | |
|
| 2 | 362 | | return false; |
| | 363 | | } |
| | 364 | |
|
| | 365 | | internal void ReadProperties(JsonElement json) |
| | 366 | | { |
| 8816 | 367 | | foreach (JsonProperty prop in json.EnumerateObject()) |
| | 368 | | { |
| 3710 | 369 | | switch (prop.Name) |
| | 370 | | { |
| | 371 | | case KeyIdPropertyName: |
| 676 | 372 | | Id = prop.Value.GetString(); |
| 676 | 373 | | break; |
| | 374 | | case KeyTypePropertyName: |
| 698 | 375 | | KeyType = prop.Value.GetString(); |
| 698 | 376 | | break; |
| | 377 | | case KeyOpsPropertyName: |
| 5928 | 378 | | foreach (JsonElement element in prop.Value.EnumerateArray()) |
| | 379 | | { |
| 2314 | 380 | | _keyOps.Add(element.ToString()); |
| | 381 | | } |
| | 382 | | break; |
| | 383 | | case CurveNamePropertyName: |
| 368 | 384 | | CurveName = prop.Value.GetString(); |
| 368 | 385 | | break; |
| | 386 | | case NPropertyName: |
| 280 | 387 | | N = Base64Url.Decode(prop.Value.GetString()); |
| 280 | 388 | | break; |
| | 389 | | case EPropertyName: |
| 280 | 390 | | E = Base64Url.Decode(prop.Value.GetString()); |
| 280 | 391 | | break; |
| | 392 | | case DPPropertyName: |
| 2 | 393 | | DP = Base64Url.Decode(prop.Value.GetString()); |
| 2 | 394 | | break; |
| | 395 | | case DQPropertyName: |
| 2 | 396 | | DQ = Base64Url.Decode(prop.Value.GetString()); |
| 2 | 397 | | break; |
| | 398 | | case QIPropertyName: |
| 2 | 399 | | QI = Base64Url.Decode(prop.Value.GetString()); |
| 2 | 400 | | break; |
| | 401 | | case PPropertyName: |
| 2 | 402 | | P = Base64Url.Decode(prop.Value.GetString()); |
| 2 | 403 | | break; |
| | 404 | | case QPropertyName: |
| 2 | 405 | | Q = Base64Url.Decode(prop.Value.GetString()); |
| 2 | 406 | | break; |
| | 407 | | case XPropertyName: |
| 368 | 408 | | X = Base64Url.Decode(prop.Value.GetString()); |
| 368 | 409 | | break; |
| | 410 | | case YPropertyName: |
| 368 | 411 | | Y = Base64Url.Decode(prop.Value.GetString()); |
| 368 | 412 | | break; |
| | 413 | | case DPropertyName: |
| 10 | 414 | | D = Base64Url.Decode(prop.Value.GetString()); |
| 10 | 415 | | break; |
| | 416 | | case KPropertyName: |
| 2 | 417 | | K = Base64Url.Decode(prop.Value.GetString()); |
| 2 | 418 | | break; |
| | 419 | | case TPropertyName: |
| 0 | 420 | | T = Base64Url.Decode(prop.Value.GetString()); |
| | 421 | | break; |
| | 422 | | } |
| | 423 | | } |
| 698 | 424 | | } |
| | 425 | |
|
| | 426 | | internal void WriteProperties(Utf8JsonWriter json) |
| | 427 | | { |
| 58 | 428 | | if (KeyType != default) |
| | 429 | | { |
| 58 | 430 | | json.WriteString(s_keyTypePropertyNameBytes, KeyType.ToString()); |
| | 431 | | } |
| 58 | 432 | | if (KeyOps != null) |
| | 433 | | { |
| 58 | 434 | | json.WriteStartArray(s_keyOpsPropertyNameBytes); |
| 552 | 435 | | foreach (KeyOperation operation in KeyOps) |
| | 436 | | { |
| 218 | 437 | | json.WriteStringValue(operation.ToString()); |
| | 438 | | } |
| 58 | 439 | | json.WriteEndArray(); |
| | 440 | | } |
| 58 | 441 | | if (CurveName.HasValue) |
| | 442 | | { |
| 28 | 443 | | json.WriteString(s_curveNamePropertyNameBytes, CurveName.Value.ToString()); |
| | 444 | | } |
| 58 | 445 | | if (N != null) |
| | 446 | | { |
| 28 | 447 | | json.WriteString(s_nPropertyNameBytes, Base64Url.Encode(N)); |
| | 448 | | } |
| 58 | 449 | | if (E != null) |
| | 450 | | { |
| 28 | 451 | | json.WriteString(s_ePropertyNameBytes, Base64Url.Encode(E)); |
| | 452 | | } |
| 58 | 453 | | if (DP != null) |
| | 454 | | { |
| 26 | 455 | | json.WriteString(s_dPPropertyNameBytes, Base64Url.Encode(DP)); |
| | 456 | | } |
| 58 | 457 | | if (DQ != null) |
| | 458 | | { |
| 26 | 459 | | json.WriteString(s_dQPropertyNameBytes, Base64Url.Encode(DQ)); |
| | 460 | | } |
| 58 | 461 | | if (QI != null) |
| | 462 | | { |
| 26 | 463 | | json.WriteString(s_qIPropertyNameBytes, Base64Url.Encode(QI)); |
| | 464 | | } |
| 58 | 465 | | if (P != null) |
| | 466 | | { |
| 26 | 467 | | json.WriteString(s_pPropertyNameBytes, Base64Url.Encode(P)); |
| | 468 | | } |
| 58 | 469 | | if (Q != null) |
| | 470 | | { |
| 26 | 471 | | json.WriteString(s_qPropertyNameBytes, Base64Url.Encode(Q)); |
| | 472 | | } |
| 58 | 473 | | if (X != null) |
| | 474 | | { |
| 28 | 475 | | json.WriteString(s_xPropertyNameBytes, Base64Url.Encode(X)); |
| | 476 | | } |
| 58 | 477 | | if (Y != null) |
| | 478 | | { |
| 28 | 479 | | json.WriteString(s_yPropertyNameBytes, Base64Url.Encode(Y)); |
| | 480 | | } |
| 58 | 481 | | if (D != null) |
| | 482 | | { |
| 46 | 483 | | json.WriteString(s_dPropertyNameBytes, Base64Url.Encode(D)); |
| | 484 | | } |
| 58 | 485 | | if (K != null) |
| | 486 | | { |
| 2 | 487 | | json.WriteString(s_kPropertyNameBytes, Base64Url.Encode(K)); |
| | 488 | | } |
| 58 | 489 | | if (T != null) |
| | 490 | | { |
| 0 | 491 | | json.WriteString(s_tPropertyNameBytes, Base64Url.Encode(T)); |
| | 492 | | } |
| 58 | 493 | | } |
| | 494 | |
|
| 22 | 495 | | void IJsonDeserializable.ReadProperties(JsonElement json) => ReadProperties(json); |
| | 496 | |
|
| 22 | 497 | | void IJsonSerializable.WriteProperties(Utf8JsonWriter json) => WriteProperties(json); |
| | 498 | |
|
| | 499 | | private static byte[] ForceBufferLength(string name, byte[] value, int requiredLengthInBytes) |
| | 500 | | { |
| 282 | 501 | | if (value is null || value.Length == 0) |
| | 502 | | { |
| 0 | 503 | | throw new InvalidOperationException($"key parameter {name} is null or empty"); |
| | 504 | | } |
| | 505 | |
|
| 282 | 506 | | if (value.Length == requiredLengthInBytes) |
| | 507 | | { |
| 278 | 508 | | return value; |
| | 509 | | } |
| | 510 | |
|
| 4 | 511 | | if (value.Length < requiredLengthInBytes) |
| | 512 | | { |
| 0 | 513 | | byte[] padded = new byte[requiredLengthInBytes]; |
| 0 | 514 | | Array.Copy(value, 0, padded, requiredLengthInBytes - value.Length, value.Length); |
| | 515 | |
|
| 0 | 516 | | return padded; |
| | 517 | | } |
| | 518 | |
|
| | 519 | | // Throw if any extra bytes are non-zero. |
| 4 | 520 | | var extraLength = value.Length - requiredLengthInBytes; |
| 0 | 521 | | for (int i = 0; i < extraLength; ++i) |
| | 522 | | { |
| 4 | 523 | | if (value[i] != 0) |
| | 524 | | { |
| 4 | 525 | | throw new InvalidOperationException($"key parameter {name} is too long: expected at most {requiredLe |
| | 526 | | } |
| | 527 | | } |
| | 528 | |
|
| 0 | 529 | | byte[] trimmed = new byte[requiredLengthInBytes]; |
| 0 | 530 | | Array.Copy(value, value.Length - requiredLengthInBytes, trimmed, 0, requiredLengthInBytes); |
| | 531 | |
|
| 0 | 532 | | return trimmed; |
| | 533 | | } |
| | 534 | |
|
| 2 | 535 | | private static readonly byte[] s_zeroBuffer = new byte[] { 0 }; |
| | 536 | | private static byte[] TrimBuffer(byte[] value) |
| | 537 | | { |
| 58 | 538 | | if (value is null || value.Length <= 1 || value[0] != 0) |
| | 539 | | { |
| 58 | 540 | | return value; |
| | 541 | | } |
| | 542 | |
|
| 0 | 543 | | for (int i = 1; i < value.Length; ++i) |
| | 544 | | { |
| 0 | 545 | | if (value[i] != 0) |
| | 546 | | { |
| 0 | 547 | | var trimmed = new byte[value.Length - i]; |
| 0 | 548 | | Array.Copy(value, i, trimmed, 0, trimmed.Length); |
| | 549 | |
|
| 0 | 550 | | return trimmed; |
| | 551 | | } |
| | 552 | | } |
| | 553 | |
|
| 0 | 554 | | return s_zeroBuffer; |
| | 555 | | } |
| | 556 | |
|
| | 557 | | private static void ValidateKeyParameter(string name, byte[] value) |
| | 558 | | { |
| 268 | 559 | | if (value != null) |
| | 560 | | { |
| 840 | 561 | | for (int i = 0; i < value.Length; ++i) |
| | 562 | | { |
| 404 | 563 | | if (value[i] != 0) |
| | 564 | | { |
| 244 | 565 | | return; |
| | 566 | | } |
| | 567 | | } |
| | 568 | | } |
| | 569 | |
|
| 24 | 570 | | throw new InvalidOperationException($"key parameter {name} is null or zeros"); |
| | 571 | | } |
| | 572 | |
|
| | 573 | | // ECParameters is defined in netstandard2.0 but not net461 (introduced in net47). Separate method with no inlin |
| | 574 | | [MethodImpl(MethodImplOptions.NoInlining)] |
| | 575 | | private void Initialize(ECDsa ecdsa, bool includePrivateParameters) |
| | 576 | | { |
| 92 | 577 | | KeyType = KeyType.Ec; |
| | 578 | |
|
| 92 | 579 | | ECParameters ecParameters = ecdsa.ExportParameters(includePrivateParameters); |
| 90 | 580 | | CurveName = KeyCurveName.FromOid(ecParameters.Curve.Oid, ecdsa.KeySize).ToString() ?? throw new InvalidOpera |
| 90 | 581 | | D = ecParameters.D; |
| 90 | 582 | | X = ecParameters.Q.X; |
| 90 | 583 | | Y = ecParameters.Q.Y; |
| 90 | 584 | | } |
| | 585 | |
|
| | 586 | | [MethodImpl(MethodImplOptions.NoInlining)] |
| | 587 | | private ECDsa Convert(bool includePrivateParameters, bool throwIfNotSupported) |
| | 588 | | { |
| 58 | 589 | | if (!CurveName.HasValue) |
| | 590 | | { |
| 2 | 591 | | if (throwIfNotSupported) |
| | 592 | | { |
| 2 | 593 | | throw new InvalidOperationException("missing required curve name"); |
| | 594 | | } |
| | 595 | |
|
| 0 | 596 | | return null; |
| | 597 | | } |
| | 598 | |
|
| 56 | 599 | | KeyCurveName curveName = CurveName.Value; |
| | 600 | |
|
| 56 | 601 | | int requiredParameterSize = curveName.KeyParameterSize; |
| 56 | 602 | | if (requiredParameterSize <= 0) |
| | 603 | | { |
| 4 | 604 | | if (throwIfNotSupported) |
| | 605 | | { |
| 2 | 606 | | throw new InvalidOperationException($"invalid curve name: {CurveName.ToString()}"); |
| | 607 | | } |
| | 608 | |
|
| 2 | 609 | | return null; |
| | 610 | | } |
| | 611 | |
|
| 52 | 612 | | ECParameters ecParameters = new ECParameters |
| 52 | 613 | | { |
| 52 | 614 | | Curve = ECCurve.CreateFromOid(curveName.Oid), |
| 52 | 615 | | Q = new ECPoint |
| 52 | 616 | | { |
| 52 | 617 | | X = ForceBufferLength(nameof(X), X, requiredParameterSize), |
| 52 | 618 | | Y = ForceBufferLength(nameof(Y), Y, requiredParameterSize), |
| 52 | 619 | | }, |
| 52 | 620 | | }; |
| | 621 | |
|
| 48 | 622 | | if (includePrivateParameters && HasPrivateKey) |
| | 623 | | { |
| 24 | 624 | | ecParameters.D = ForceBufferLength(nameof(D), D, requiredParameterSize); |
| | 625 | | } |
| | 626 | |
|
| 48 | 627 | | ECDsa ecdsa = ECDsa.Create(); |
| | 628 | | try |
| | 629 | | { |
| 48 | 630 | | ecdsa.ImportParameters(ecParameters); |
| 48 | 631 | | } |
| 0 | 632 | | catch when (!throwIfNotSupported) |
| | 633 | | { |
| 0 | 634 | | ecdsa.Dispose(); |
| | 635 | |
|
| 0 | 636 | | return null; |
| | 637 | | } |
| | 638 | |
|
| 48 | 639 | | return ecdsa; |
| 0 | 640 | | } |
| | 641 | | } |
| | 642 | | } |