sshnet / SSH.NET

SSH.NET is a Secure Shell (SSH) library for .NET, optimized for parallelism.
http://sshnet.github.io/SSH.NET/
MIT License
3.96k stars 932 forks source link

Can't connect (SCP, SSH) in Xamarin Android #807

Closed stealthrabbi closed 1 month ago

stealthrabbi commented 3 years ago

Calling Connect() with an SSH or SCP client (and likely others) causes this exception. Does this library not work in Xamarin Android?

I don't see where in this library ti's throwing NotImplementedException, so I'm guessing something missing in Mono / .NET Standard?

2021-04-15 12:29:05.030 16384-16427/? E/MySessionStarter: Failed uploading file: System.NotImplementedException: The method or operation is not implemented.
      at Renci.SshNet.Session.WaitOnHandle (System.Threading.WaitHandle waitHandle, System.TimeSpan timeout) [0x00041] in <c7169cd606324a41b931acc40c516428>:0 
      at Renci.SshNet.Session.WaitOnHandle (System.Threading.WaitHandle waitHandle) [0x0000d] in <c7169cd606324a41b931acc40c516428>:0 
      at Renci.SshNet.Session.Connect () [0x0017b] in <c7169cd606324a41b931acc40c516428>:0 
      at Renci.SshNet.BaseClient.CreateAndConnectSession () [0x00053] in <c7169cd606324a41b931acc40c516428>:0 
      at Renci.SshNet.BaseClient.Connect () [0x0001f] in <c7169cd606324a41b931acc40c516428>:0 
    ....
drwtsn32x commented 3 years ago

Did you solve this yet? What version of mono are you using?

stealthrabbi commented 3 years ago

Not solved. I don't know what version of Mono I'm using. I believe it was roughly the latest Xamarin.

drwtsn32x commented 3 years ago

When you get a chance, if you could check the mono version that'd be great....

$ mono --version

I'm exploring possibly the same issue with a different product that uses the SSH.NET library. Wondering if the mono version is the culprit.

stealthrabbi commented 3 years ago

I don't explicitly have mono installed -- that's not needed for Xamarin work. The Mono.Android assembly that VS is using for the Xamarin Android project is in a folder named C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\Common7\IDE\ReferenceAssemblies\Microsoft\Framework\MonoAndroid\v9.0, but the actual DLL has a Product version of 11.0.2.0;

drwtsn32x commented 3 years ago

Ok, on the other project I'm working on we discovered that mono doesn't support ECDSA keys. SSH.NET library will throw the NotImplemented exception when it's running via mono. (It works fine when using native .NET on Windows though.)

So you might be able to "solve" your problem by not using an ECDSA key...

stealthrabbi commented 3 years ago

What you say about the keys may be right, but I think my issue is happening way before a connection. It's related to Session.WaitOnHandle being not implemented.

ts678 commented 3 years ago

It's related to Session.WaitOnHandle being not implemented.

It seems Session.WaitOnHandle is in https://github.com/sshnet/SSH.NET/blob/develop/src/Renci.SshNet/Session.cs You can see comments there about the receive thread. I'm not an SSH.NET developer. It'd be nice if they can help.

It might be waiting at the below for the receive thread to either receive, or find an exception, and it got exception:

https://github.com/sshnet/SSH.NET/blob/f072c5f1a1bb607797a85cd43cf498ec541af3ab/src/Renci.SshNet/Session.cs#L852-L852

I think my issue is happening way before a connection

The stack trace names suggest that it's working on connect, but it likely died before SSH connection was finished. The behavior we see with certain SFTP servers such as Bitvise (but not OpenSSH, at least so far) is a failure after a

Server: Elliptic Curve Diffie-Hellman Key Exchange Reply, New Keys Message Code: Elliptic Curve Diffie-Hellman Key Exchange Reply (31) is received, containing KEX host key (type: ecdsa-sha2-nistp384) I think the received protocol is RFC 5656 ECDH Key Exchange here.

Processing of that server SSH_MSG_KEX_ECDH_REPLY might be here:

https://github.com/sshnet/SSH.NET/blob/f072c5f1a1bb607797a85cd43cf498ec541af3ab/src/Renci.SshNet/Session.cs#L1298-L1302

and at some point the key from the server might hit here:

https://github.com/sshnet/SSH.NET/blob/a5bd08d655bb6a3c762306472cec354556dca3a3/src/Renci.SshNet/Security/Cryptography/EcdsaKey.cs#L238-L242

ECDsa Class and ECDsaCng Class get involved, and so maybe it hits the NotImplementedException around here.

NotImplementedException has been in ECDsaCng.cs a long time, maybe added here, then later exposed through Support newer SSH Ciphers and MACs #53. If you're still on the 2016 release, ignore all this. It applies to 2020's.

Any SSH.NET developer comments? Could these very welcome new capabilities be exposing an old gap in mono? Please forgive any mistakes in the details. I don't have a debugger, or know the protocol or the code all that well.

ts678 commented 3 years ago

I'm noticing the 2017 mono change did a similar NotImplementedException to RsaCng.cs just below ECDsaCng.cs

drwtsn32x commented 3 years ago

(but not OpenSSH, at least so far)

I was able to reproduce with an OpenSSH server by using an ECDSA key.

Joannick commented 3 years ago

Hi friend, so did you found a solution about this? I've seen an other solution is Rebex SFTP Nugget but licence is 350$. Thanks

stealthrabbi commented 3 years ago

My solution was in Xamarin, to create a binding to the JSCH java library and use that in my xamarin android proj.

ts678 commented 3 years ago

A workaround is try new ECDsaCng() to see if it's implemented. If exception occurs, go through HostKeyAlgorithms.Keys, and HostKeyAlgorithms.Remove(key) the ones beginning with ecdsa (inspired by Limit what algorithms or ciphers are used #730).

TechnoFairyGirl commented 3 years ago

ECDSA is simply not implemented on Mono. The only real fix would be to use a different implementation, such as the one provided by Bouncy Castle.

You can work around the issue by removing the ECDSA algorithms from connectionInfo.HostKeyAlgorithms if System.Security.Cryptography.ECDsaCng is unavailable.

try { using (var ecdsa = new System.Security.Cryptography.ECDsaCng()) ; }
catch (NotImplementedException)
{
    var algsToRemove = connectionInfo.HostKeyAlgorithms.Keys.Where(algName => algName.StartsWith("ecdsa")).ToArray();
    foreach (var algName in algsToRemove) connectionInfo.HostKeyAlgorithms.Remove(algName);
}

If you get Renci.SshNet.Common.SshConnectionException: 'An established connection was aborted by the server.' on connect after adding the code above, then the server doesn't have any mutually agreeable host key algorithms now that you've removed the ECDSA ones. This can be solved on the server by adding ssh-rsa to HostKeyAlgorithms in sshd_config (or the equivalent for whatever SSH server you have).

Rob-Hague commented 1 month ago

Should be fixed by #1461