#nullable enable
using System;
using System.Formats.Asn1;
using System.Globalization;
using System.Numerics;
using Org.BouncyCastle.Asn1.EdEC;
using Org.BouncyCastle.Asn1.Pkcs;
using Org.BouncyCastle.Asn1.X9;
using Org.BouncyCastle.Pkcs;
using Renci.SshNet.Common;
using Renci.SshNet.Security;
namespace Renci.SshNet
{
public partial class PrivateKeyFile
{
private sealed class PKCS8 : IPrivateKeyParser
{
private readonly bool _encrypted;
private readonly byte[] _data;
private readonly string? _passPhrase;
public PKCS8(bool encrypted, byte[] data, string? passPhrase)
{
_encrypted = encrypted;
_data = data;
_passPhrase = passPhrase;
}
///
/// Parses an OpenSSL PKCS#8 key file according to RFC5208:
/// .
///
/// Algorithm not supported.
public Key Parse()
{
PrivateKeyInfo privateKeyInfo;
if (_encrypted)
{
var encryptedPrivateKeyInfo = EncryptedPrivateKeyInfo.GetInstance(_data);
privateKeyInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(_passPhrase?.ToCharArray(), encryptedPrivateKeyInfo);
}
else
{
privateKeyInfo = PrivateKeyInfo.GetInstance(_data);
}
var algorithmOid = privateKeyInfo.PrivateKeyAlgorithm.Algorithm;
var key = privateKeyInfo.PrivateKey.GetOctets();
if (algorithmOid.Equals(PkcsObjectIdentifiers.RsaEncryption))
{
return new RsaKey(key);
}
if (algorithmOid.Equals(X9ObjectIdentifiers.IdECPublicKey))
{
var parameters = privateKeyInfo.PrivateKeyAlgorithm.Parameters.GetDerEncoded();
var parametersReader = new AsnReader(parameters, AsnEncodingRules.DER);
var curve = parametersReader.ReadObjectIdentifier();
parametersReader.ThrowIfNotEmpty();
var privateKeyReader = new AsnReader(key, AsnEncodingRules.DER);
var sequenceReader = privateKeyReader.ReadSequence();
privateKeyReader.ThrowIfNotEmpty();
var version = sequenceReader.ReadInteger();
if (version != BigInteger.One)
{
throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "EC version '{0}' is not supported.", version));
}
var privatekey = sequenceReader.ReadOctetString();
var publicKeyReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 1, isConstructed: true));
var publickey = publicKeyReader.ReadBitString(out _);
publicKeyReader.ThrowIfNotEmpty();
sequenceReader.ThrowIfNotEmpty();
return new EcdsaKey(curve, publickey, privatekey.TrimLeadingZeros());
}
if (algorithmOid.Equals(EdECObjectIdentifiers.id_Ed25519))
{
return new ED25519Key(key);
}
throw new SshException(string.Format(CultureInfo.InvariantCulture, "Private key algorithm \"{0}\" is not supported.", algorithmOid));
}
}
}
}