Skip to content

PR #971 with conflicts resolved and building #1156

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ private static KeyHostAlgorithm GetKeyHostAlgorithm()
using (var s = executingAssembly.GetManifestResourceStream(string.Format("Renci.SshNet.Tests.Data.{0}", "Key.RSA.txt")))
{
var privateKey = new PrivateKeyFile(s);
return (KeyHostAlgorithm) privateKey.HostKey;
return privateKey.HostKeys[0];
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ private static KeyHostAlgorithm GetKeyHostAlgorithm()
using (var s = executingAssembly.GetManifestResourceStream(string.Format("Renci.SshNet.Tests.Data.{0}", "Key.RSA.txt")))
{
var privateKey = new PrivateKeyFile(s);
return (KeyHostAlgorithm)privateKey.HostKey;
return privateKey.HostKeys[0];
}
}
}
Expand Down
12 changes: 6 additions & 6 deletions src/Renci.SshNet.Tests/Classes/PrivateKeyFileTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,7 @@ public void ConstructorWithStreamAndPassphrase()
using (var stream = GetData("Key.RSA.Encrypted.Aes.128.CBC.12345.txt"))
{
var privateKeyFile = new PrivateKeyFile(stream, "12345");
Assert.IsNotNull(privateKeyFile.HostKey);
Assert.IsNotNull(privateKeyFile.HostKeys[0]);
}
}

Expand All @@ -427,7 +427,7 @@ public void ConstructorWithFileNameAndPassphrase()
using (var fs = File.Open(_temporaryFile, FileMode.Open, FileAccess.Read, FileShare.Read))
{
var privateKeyFile = new PrivateKeyFile(_temporaryFile, "12345");
Assert.IsNotNull(privateKeyFile.HostKey);
Assert.IsNotNull(privateKeyFile.HostKeys[0]);

fs.Close();
}
Expand Down Expand Up @@ -495,7 +495,7 @@ public void ConstructorWithFileName()
}

var privateKeyFile = new PrivateKeyFile(_temporaryFile, "12345");
Assert.IsNotNull(privateKeyFile.HostKey);
Assert.IsNotNull(privateKeyFile.HostKeys[0]);
}

/// <summary>
Expand All @@ -507,7 +507,7 @@ public void ConstructorWithStream()
using (var stream = GetData("Key.RSA.txt"))
{
var privateKeyFile = new PrivateKeyFile(stream);
Assert.IsNotNull(privateKeyFile.HostKey);
Assert.IsNotNull(privateKeyFile.HostKeys[0]);
}
}

Expand All @@ -523,7 +523,7 @@ public void ConstructorWithFileNameShouldBeAbleToReadFileThatIsSharedForReadAcce
using (var fs = File.Open(_temporaryFile, FileMode.Open, FileAccess.Read, FileShare.Read))
{
var privateKeyFile = new PrivateKeyFile(_temporaryFile);
Assert.IsNotNull(privateKeyFile.HostKey);
Assert.IsNotNull(privateKeyFile.HostKeys[0]);

fs.Close();
}
Expand All @@ -541,7 +541,7 @@ public void ConstructorWithFileNameAndPassPhraseShouldBeAbleToReadFileThatIsShar
using (var fs = File.Open(_temporaryFile, FileMode.Open, FileAccess.Read, FileShare.Read))
{
var privateKeyFile = new PrivateKeyFile(_temporaryFile, "12345");
Assert.IsNotNull(privateKeyFile.HostKey);
Assert.IsNotNull(privateKeyFile.HostKeys[0]);

fs.Close();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ private static KeyHostAlgorithm GetKeyHostAlgorithm()
using (var s = executingAssembly.GetManifestResourceStream(string.Format("Renci.SshNet.Tests.Data.{0}", "Key.RSA.txt")))
{
var privateKey = new PrivateKeyFile(s);
return (KeyHostAlgorithm)privateKey.HostKey;
return privateKey.HostKeys[0];
}
}
}
Expand Down
1 change: 1 addition & 0 deletions src/Renci.SshNet/ConnectionInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,7 @@ public ConnectionInfo(string host, int port, string username, ProxyTypes proxyTy
{ "ecdsa-sha2-nistp521", data => new KeyHostAlgorithm("ecdsa-sha2-nistp521", new EcdsaKey(), data) },
{ "ssh-rsa", data => new KeyHostAlgorithm("ssh-rsa", new RsaKey(), data) },
{ "ssh-dss", data => new KeyHostAlgorithm("ssh-dss", new DsaKey(), data) },
{ "rsa-sha2-256", data => new KeyHostAlgorithm("rsa-sha2-256", new RsaWithSha256SignatureKey(), data) },
};

CompressionAlgorithms = new Dictionary<string, Type>
Expand Down
4 changes: 2 additions & 2 deletions src/Renci.SshNet/IPrivateKeySource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@ public interface IPrivateKeySource
/// <summary>
/// Gets the host key.
/// </summary>
HostAlgorithm HostKey { get; }
KeyHostAlgorithm[] HostKeys { get; }
}
}
}
31 changes: 23 additions & 8 deletions src/Renci.SshNet/PrivateKeyAuthenticationMethod.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using Renci.SshNet.Security;
using System.Threading;

using Renci.SshNet.Common;
Expand Down Expand Up @@ -64,24 +65,38 @@ public override AuthenticationResult Authenticate(Session session)

session.RegisterMessage("SSH_MSG_USERAUTH_PK_OK");

var hostKeys = new HostAlgorithm[] { };

foreach (var keyFile in KeyFiles)
{
var idx = hostKeys.Length;

Array.Resize(ref hostKeys, idx + keyFile.HostKeys.Length);

for(var i = 0; i < keyFile.HostKeys.Length; i++)
{
hostKeys[idx++] = keyFile.HostKeys[i];
}
}

try
{
foreach (var keyFile in KeyFiles)
foreach (var hostKey in hostKeys)
{
_ = _authenticationCompleted.Reset();
_isSignatureRequired = false;

var message = new RequestMessagePublicKey(ServiceName.Connection,
Username,
keyFile.HostKey.Name,
keyFile.HostKey.Data);
hostKey.Name,
hostKey.Data);

if (KeyFiles.Count < 2)
if (hostKeys.Length < 2)
{
// If only one key file provided then send signature for very first request
var signatureData = new SignatureData(message, session.SessionId).GetBytes();

message.Signature = keyFile.HostKey.Sign(signatureData);
message.Signature = hostKey.Sign(signatureData);
}

// Send public key authentication request
Expand All @@ -95,12 +110,12 @@ public override AuthenticationResult Authenticate(Session session)

var signatureMessage = new RequestMessagePublicKey(ServiceName.Connection,
Username,
keyFile.HostKey.Name,
keyFile.HostKey.Data);
hostKey.Name,
hostKey.Data);

var signatureData = new SignatureData(message, session.SessionId).GetBytes();

signatureMessage.Signature = keyFile.HostKey.Sign(signatureData);
signatureMessage.Signature = hostKey.Sign(signatureData);

// Send public key authentication request with signature
session.SendMessage(signatureMessage);
Expand Down
20 changes: 11 additions & 9 deletions src/Renci.SshNet/PrivateKeyFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,15 +74,15 @@ public class PrivateKeyFile : IPrivateKeySource, IDisposable
/// <summary>
/// Gets the host key.
/// </summary>
public HostAlgorithm HostKey { get; private set; }
public KeyHostAlgorithm[] HostKeys { get; private set; }

/// <summary>
/// Initializes a new instance of the <see cref="PrivateKeyFile"/> class.
/// </summary>
/// <param name="key">The key.</param>
public PrivateKeyFile(Key key)
{
HostKey = new KeyHostAlgorithm(key.ToString(), key);
HostKeys = new KeyHostAlgorithm[] { new KeyHostAlgorithm(key.ToString(), key) };
}

/// <summary>
Expand Down Expand Up @@ -222,20 +222,22 @@ private void Open(Stream privateKey, string passPhrase)
switch (keyName)
{
case "RSA":
_key = new RsaKey(decryptedData);
HostKey = new KeyHostAlgorithm("ssh-rsa", _key);
HostKeys = new KeyHostAlgorithm[] {
new KeyHostAlgorithm("rsa-sha2-256", new RsaWithSha256SignatureKey(decryptedData)),
new KeyHostAlgorithm("ssh-rsa", new RsaKey(decryptedData)),
};
break;
case "DSA":
_key = new DsaKey(decryptedData);
HostKey = new KeyHostAlgorithm("ssh-dss", _key);
HostKeys = new KeyHostAlgorithm[] { new KeyHostAlgorithm("ssh-dss", _key) };
break;
case "EC":
_key = new EcdsaKey(decryptedData);
HostKey = new KeyHostAlgorithm(_key.ToString(), _key);
HostKeys = new KeyHostAlgorithm[] { new KeyHostAlgorithm(_key.ToString(), _key) };
break;
case "OPENSSH":
_key = ParseOpenSshV1Key(decryptedData, passPhrase);
HostKey = new KeyHostAlgorithm(_key.ToString(), _key);
HostKeys = new KeyHostAlgorithm[] { new KeyHostAlgorithm(_key.ToString(), _key) };
break;
case "SSH2 ENCRYPTED":
var reader = new SshDataReader(decryptedData);
Expand Down Expand Up @@ -291,7 +293,7 @@ private void Open(Stream privateKey, string passPhrase)
var q = reader.ReadBigIntWithBits(); // p
var p = reader.ReadBigIntWithBits(); // q
_key = new RsaKey(modulus, exponent, d, p, q, inverseQ);
HostKey = new KeyHostAlgorithm("ssh-rsa", _key);
HostKeys = new KeyHostAlgorithm[] { new KeyHostAlgorithm("ssh-rsa", _key) };
}
else if (keyType == "dl-modp{sign{dsa-nist-sha1},dh{plain}}")
{
Expand All @@ -306,7 +308,7 @@ private void Open(Stream privateKey, string passPhrase)
var y = reader.ReadBigIntWithBits();
var x = reader.ReadBigIntWithBits();
_key = new DsaKey(p, q, g, y, x);
HostKey = new KeyHostAlgorithm("ssh-dss", _key);
HostKeys = new KeyHostAlgorithm[] { new KeyHostAlgorithm("ssh-dss", _key) };
}
else
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
using System;
using System.Security.Cryptography;
using Renci.SshNet.Common;
using Renci.SshNet.Security.Cryptography.Ciphers;

namespace Renci.SshNet.Security.Cryptography
{
/// <summary>
/// Implements RSA digital signature algorithm.
/// </summary>
public class RsaSha256DigitalSignature : CipherDigitalSignature, IDisposable
{
private HashAlgorithm _hash;

/// <summary>
/// Initializes a new instance of the <see cref="RsaSha256DigitalSignature"/> class.
/// </summary>
/// <param name="rsaKey">The RSA key.</param>
public RsaSha256DigitalSignature(RsaWithSha256SignatureKey rsaKey)
: base(new ObjectIdentifier(2, 16, 840, 1, 101, 3, 4, 2, 1), new RsaCipher(rsaKey))
{
_hash = SHA256.Create();
}

/// <summary>
/// Hashes the specified input.
/// </summary>
/// <param name="input">The input.</param>
/// <returns>
/// Hashed data.
/// </returns>
protected override byte[] Hash(byte[] input)
{
return _hash.ComputeHash(input);
}

#region IDisposable Members

private bool _isDisposed;

/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}

/// <summary>
/// Releases unmanaged and - optionally - managed resources
/// </summary>
/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
protected virtual void Dispose(bool disposing)
{
if (_isDisposed)
{
return;
}

if (disposing)
{
var hash = _hash;
if (hash != null)
{
hash.Dispose();
_hash = null;
}

_isDisposed = true;
}
}

/// <summary>
/// Releases unmanaged resources and performs other cleanup operations before the
/// <see cref="RsaSha256DigitalSignature"/> is reclaimed by garbage collection.
/// </summary>
~RsaSha256DigitalSignature()
{
Dispose(false);
}

#endregion
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
using System;
using Renci.SshNet.Common;
using Renci.SshNet.Security.Cryptography;

namespace Renci.SshNet.Security
{
/// <summary>
/// Contains RSA private and public key
/// </summary>
public class RsaWithSha256SignatureKey : RsaKey
{
/// <summary>
/// Initializes a new instance of the <see cref="RsaWithSha256SignatureKey"/> class.
/// </summary>
public RsaWithSha256SignatureKey()
{ }

/// <summary>
/// Initializes a new instance of the <see cref="RsaWithSha256SignatureKey"/> class.
/// </summary>
/// <param name="data">DER encoded private key data.</param>
public RsaWithSha256SignatureKey(byte[] data)
: base(data)
{
if (_privateKey.Length != 8)
{
throw new InvalidOperationException("Invalid private key.");
}
}

/// <summary>
/// Initializes a new instance of the <see cref="RsaWithSha256SignatureKey"/> class.
/// </summary>
/// <param name="modulus">The modulus.</param>
/// <param name="exponent">The exponent.</param>
/// <param name="d">The d.</param>
/// <param name="p">The p.</param>
/// <param name="q">The q.</param>
/// <param name="inverseQ">The inverse Q.</param>
public RsaWithSha256SignatureKey(BigInteger modulus, BigInteger exponent, BigInteger d, BigInteger p, BigInteger q,
BigInteger inverseQ) : base(modulus, exponent, d, p, q, inverseQ)
{
}

private RsaSha256DigitalSignature _digitalSignature;

/// <summary>
/// Gets the digital signature.
/// </summary>
protected override DigitalSignature DigitalSignature
{
get
{
_digitalSignature ??= new RsaSha256DigitalSignature(this);
return _digitalSignature;
}
}

/// <summary>
/// Gets the Key String.
/// </summary>
public override string ToString()
{
return "rsa-sha2-256";
}
}
}