PrivateKeyFile.PKCS8.cs 3.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. #nullable enable
  2. using System;
  3. using System.Formats.Asn1;
  4. using System.Globalization;
  5. using System.Numerics;
  6. using Org.BouncyCastle.Asn1.EdEC;
  7. using Org.BouncyCastle.Asn1.Pkcs;
  8. using Org.BouncyCastle.Asn1.X9;
  9. using Org.BouncyCastle.Pkcs;
  10. using Renci.SshNet.Common;
  11. using Renci.SshNet.Security;
  12. namespace Renci.SshNet
  13. {
  14. public partial class PrivateKeyFile
  15. {
  16. private sealed class PKCS8 : IPrivateKeyParser
  17. {
  18. private readonly bool _encrypted;
  19. private readonly byte[] _data;
  20. private readonly string? _passPhrase;
  21. public PKCS8(bool encrypted, byte[] data, string? passPhrase)
  22. {
  23. _encrypted = encrypted;
  24. _data = data;
  25. _passPhrase = passPhrase;
  26. }
  27. /// <summary>
  28. /// Parses an OpenSSL PKCS#8 key file according to RFC5208:
  29. /// <see href="https://www.rfc-editor.org/rfc/rfc5208#section-5"/>.
  30. /// </summary>
  31. /// <exception cref="SshException">Algorithm not supported.</exception>
  32. public Key Parse()
  33. {
  34. PrivateKeyInfo privateKeyInfo;
  35. if (_encrypted)
  36. {
  37. var encryptedPrivateKeyInfo = EncryptedPrivateKeyInfo.GetInstance(_data);
  38. privateKeyInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(_passPhrase?.ToCharArray(), encryptedPrivateKeyInfo);
  39. }
  40. else
  41. {
  42. privateKeyInfo = PrivateKeyInfo.GetInstance(_data);
  43. }
  44. var algorithmOid = privateKeyInfo.PrivateKeyAlgorithm.Algorithm;
  45. var key = privateKeyInfo.PrivateKey.GetOctets();
  46. if (algorithmOid.Equals(PkcsObjectIdentifiers.RsaEncryption))
  47. {
  48. return new RsaKey(key);
  49. }
  50. if (algorithmOid.Equals(X9ObjectIdentifiers.IdECPublicKey))
  51. {
  52. var parameters = privateKeyInfo.PrivateKeyAlgorithm.Parameters.GetDerEncoded();
  53. var parametersReader = new AsnReader(parameters, AsnEncodingRules.DER);
  54. var curve = parametersReader.ReadObjectIdentifier();
  55. parametersReader.ThrowIfNotEmpty();
  56. var privateKeyReader = new AsnReader(key, AsnEncodingRules.DER);
  57. var sequenceReader = privateKeyReader.ReadSequence();
  58. privateKeyReader.ThrowIfNotEmpty();
  59. var version = sequenceReader.ReadInteger();
  60. if (version != BigInteger.One)
  61. {
  62. throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "EC version '{0}' is not supported.", version));
  63. }
  64. var privatekey = sequenceReader.ReadOctetString();
  65. var publicKeyReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 1, isConstructed: true));
  66. var publickey = publicKeyReader.ReadBitString(out _);
  67. publicKeyReader.ThrowIfNotEmpty();
  68. sequenceReader.ThrowIfNotEmpty();
  69. return new EcdsaKey(curve, publickey, privatekey.TrimLeadingZeros());
  70. }
  71. if (algorithmOid.Equals(EdECObjectIdentifiers.id_Ed25519))
  72. {
  73. return new ED25519Key(key);
  74. }
  75. throw new SshException(string.Format(CultureInfo.InvariantCulture, "Private key algorithm \"{0}\" is not supported.", algorithmOid));
  76. }
  77. }
  78. }
  79. }