How to get the fingerprint of the server public key

0 votes
asked Dec 12, 2013 by chamila (210 points)
edited Dec 12, 2013

Hello,

We have started using rebex library recently.We received the public key of the remote server and I saved it in the file system. Now what I want use this public key to verify the finger print when I'm connecting to the server. My problem is I'm not sure how we can get the fingerprint of the saved public key using C#. Can you please assist me to solve this?

2 Answers

0 votes
answered Dec 12, 2013 by Jan Sotola (16,580 points)
edited Dec 12, 2013
 
Best answer

It depends on format your public key is saved. Rebex supports the SSH2 public key format (binary or base64-encoded).

To load a public key, just use a constructor of the SshPublicKey class:

// the constructor automatically recognizes between the binary and the base64-encoded format 
var key = new SshPublicKey(@"c:\temp\server.key");
Console.WriteLine(key.Fingerprint);

However, if you intend to use the public key just for server verifying, you don't have to keep the whole public key - keeping the fingerprint only is good enough:

Retrieve the fingerprint when first connected to the server:

client.Connect("test.rebex.net");
client.Login("demo", "password");
// remember the fingerprint for the next usage
File.WriteAllText(@"c:\temp\fingerprint.txt", client.Fingerprint);

Verify the fingerprint when connecting to the known server:

client.Connect("test.rebex.net");
client.Login("demo", "password");
// verify the current connection's fingerprint with the stored one
var storedFingerprint = File.ReadAllText(@"c:\temp\fingerprint.txt");
if (client.Fingerprint != storedFingerprint)
    throw new Exception("Stored fingerprint doesn't match!");
commented Dec 12, 2013 by chamila (210 points)
edited Dec 12, 2013

Hello Jan Thank you for your prompt response. The rebex version which I'm using at the moment doesn't accept key path to the constructor. Instead, it accepts only a byte array which is specified as "Loads a PKCS #8 or SSLeay-format RSA or DSA privarte key from the specified row data". When I pass the public key as a byte array to the constructor, I'm getting following error.

System.InvalidOperationException: Data too long. at gbMKS.29nlwLZ.1QiryS() at gbMKS.29nlwLZ.nq5WYZ() at Rebex.Net.SshPublicKey.1zHJNt(Byte[] , Nullable1& , Nullable1& ) at Rebex.Net.SshPublicKey..ctor(Byte[] data)

Any suggestions for this?

commented Dec 12, 2013 by Jan Sotola (16,580 points)
edited Dec 12, 2013

I see. The load-constructor has been actually added to the most recent version 2013-R3. However, the new constructor just loads the new data into an array and passes it to the old constructor. If it keeps throwing that exception, the key would be in unsupported format or there is some mistake when loading it to the byte array. Could you send us the public key to support@rebex.net for further analysis?

commented Dec 12, 2013 by chamila (210 points)
edited Dec 12, 2013

I have downloaded latest version of the rebex lib and tested it by passing the file path and it worked. But if I pass the byte array like below, it will throw the above exception.

byte[] bytes = File.ReadAllBytes(@"C:\Resources\ssh_public_key.txt"); var key = new SshPublicKey(bytes);

Can you please advise. Unfortunately I can't send you the key file because of our company security policies.

0 votes
answered Dec 12, 2013 by Jan Sotola (16,580 points)
edited Dec 12, 2013

From your code snippet I guess your key file is in the base64-encoded format. You have to parse the key data first and then to decode it to binary array.

I expect your key file looks somehow like this:

---- BEGIN SSH2 PUBLIC KEY ----
Comment: "Saved by Rebex SSH"
AAAAB3NzaC1yc2EAAAADAQABAAABAQDDdumVqjjC8kMbxBlX6F6KZL5Ec3v55D/I
NyupMMDF2L/2hWzDapUUVXlnrFOHPkFExSZkO4UBreQepiQHuiJQboQluwbbSSQi
dTGbApCME4OOYcz+RWBzXM5Pq2+rPIpNJ0Zh5Vy4SW0nKNpfNZ+gib6gIdHrAwHd
EthV/W55lAbGJjS11iw7LS4oLW33r1YdZlvMXn9SHX7vDc+vcVYjuBBHEuGWjhnF
HsvKeUT3Qf2ORokV/Og3opt/4exImX3B49s6V7WDLKF3pbeEbqjfDGcf5UchPJov
ivnKbPOwUCSNDFP9mcJf/ft91Hve2Qn5/EW8yS7BCftiCoVEl9vt
---- END SSH2 PUBLIC KEY ----

If the key is in such format, try the following algorithm to load it:

// load the key in text format
string text = File.ReadAllText(@"c:\temp\public_key.txt");
// unify end-of-lines and remove any extra whitespace on the begin or end of the file
text = text.Replace("\r", "").Trim().Trim('\n').Trim();
// remove the header and footer text
string header = "---- BEGIN SSH2 PUBLIC KEY ----";
string footer = "---- END SSH2 PUBLIC KEY ----";
text = text.Substring(header.Length, text.Length - header.Length - footer.Length - 1);
// remove remaining end-of-lines
text = text.Trim().Trim('\n').Trim();
// remove the optional comment line
if (text.StartsWith("Comment:"))
{
    int posEoln = text.IndexOf('\n');
    text = text.Substring(posEoln + 1);
    text = text.Trim().Trim('\n').Trim();
}
// encode the key from base64
byte[] bytes = Convert.FromBase64String(text);
var key = new SshPublicKey(bytes);
commented Dec 12, 2013 by chamila (210 points)
edited Dec 12, 2013

Thanks Jan, I tried as below and it worked. Instead of passing the byte array I just use a stream. I had to try this out because in our system key file is stored in the database. Thanks lot for your assistance

byte[] bytes = File.ReadAllBytes(@"C:\Resources\ssh_public_key.txt"); MemoryStream stream = new MemoryStream(bytes); SshPublicKey key = new SshPublicKey(stream);

...