Python Forum
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
paramiko.ed25519key
#1
Hello,
Python newbie alert!
I would appreciate a helping hand to get my first py script working.
The goal is to write a script that uploads a file to an sftp site after verifying the host ed25519 fingerprint.
I think the issue is that my keydata is not being decoded successfully?...?
I am unfamiliar with ed25519 keys but the key length i got from FileZilla is 44 characters. I tried to research on what the normal Ed25519 length is and found another source saying they should be 43 chars. So i removed the trailing = but that gave the error saying
..., line 546, in decodingbytes
return binascii.a2b_base64(s)
binascii.Error: Incorrect Padding

if a leave the trailing = i get the following error
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xb8 in position 0: invalid start byte

My code below - based from here - https://stackoverflow.com/questions/4681...ingerprint
import pysftp
import sys
import paramiko
from paramiko.py3compat import decodebytes

path = './folder/' + sys.argv[1]    #hard-coded
localpath = sys.argv[1]  

host = "1.2.3.4"                    	#hard-coded
password = "passwd"               		#hard-coded
username = "user"                		#hard-coded

keydata = b"""RLh...kP+Ido=""" #obtained using filezilla, connected successfully then used lock icon on the bottom right to display fingerprints: SHA256: RLh...kP+Ido=
key = paramiko.ed25519key.Ed25519Key(data=decodebytes(keydata))
cnopts = pysftp.CnOpts()
cnopts.hostkeys.add('1.2.3.4', 'ssh-ed25519', key)


with pysftp.Connection(host, username=username, password=password, cnopts=cnopts) as sftp:
    sftp.put(localpath, path)

print("Upload done")
Would really appreciate any help on this
Reply
#2
As a preface, let me just say that I'm not sure what's going on, how it's supposed to work, or why it's acting the way that it is. What follows is just a few things that stand out to me, to hopefully help you track down what else you can try.

But first, when sharing error messages, please include the entire traceback. Which files are throwing the errors, for example, is very useful info.

Your comment says this: SHA256: RLh...kP+Ido=. SHA256 is a one-way hash, and CANNOT be decoded/reversed/etc. But a sha has doesn't have "+" or "=" in them (it'd be all alphanumeric), but those things ARE in a base64 encoded string. So that might be what decodebytes is doing, to try to get the sha hash maybe?

But paramiko is open source, so we don't have to guess: https://github.com/paramiko/paramiko/blo...at.py#L118
Quote:
decodebytes = base64.decodebytes
encodebytes = base64.encodebytes
So decodebytes is literally just base64 decoding, with nothing else happening.

Which means to me that your key isn't valid base64, for whatever reason. Here's an example of valid base64 encoded text:
>>> import base64
>>> x=base64.encodebytes(b"foo bar")
>>> x
b'Zm9vIGJhcg==\n'
>>> y = x.strip()
>>> y
b'Zm9vIGJhcg=='
>>> base64.decodebytes(y)
b'foo bar'
>>> base64.decodebytes(x)
b'foo bar'
You can see that I tried stripping off the trailing whitespace, to see if that would matter (it did not). The = is absolutely necessary, though, as it pads the string to a multiple of 4 characters.
Reply
#3
Hi Nilamo,
Firstly thanks for your reply.
I was not entirely sure what hashes e.g. SHA256 are but after a weekend of research i think i get the concept.
You were spot on with all your assumptions
My hash is shown below.
keydata = b"""RLhdtbhVbFXUeqZh6jm0t5ToI6b4IGbac8R2EkP+Ido="""
Which i am thinking it not the the correct or entire hash being displayed. Guess I will have to use OpenSSH or else not compare the host finger print since it is verifying on an internal. I was just trying to do the 'fingerprint' authentication as a fun exercise.
Thanks for the ideas though, you have given me multiple new ideas to try! :)
Much appreciated.
Reply
#4
A sha256 hex is 64 characters long.
What you have looks like base64 encoded text. So, let's go fishing...
>>> import base64
>>> keydata = b"""RLhdtbhVbFXUeqZh6jm0t5ToI6b4IGbac8R2EkP+Ido="""
>>> decoded = base64.b64decode(keydata)
>>> decoded
b'D\xb8]\xb5\xb8UlU\xd4z\xa6a\xea9\xb4\xb7\x94\xe8#\xa6\xf8 f\xdas\xc4v\x12C\xfe!\xda'
>>> hexhash = decoded.hex()
>>> hexhash
'44b85db5b8556c55d47aa661ea39b4b794e823a6f82066da73c4761243fe21da'
>>> len(hexhash)
64
So maybe if you use the bytes.hex() method (as shown), paramiko can understand what you're passing? ie, try this: key = paramiko.ed25519key.Ed25519Key(data=decodebytes(keydata).hex())
Reply
#5
Hi,
Sorry for the late reply!
I tried what you suggested but i get the error below
C:\Users\Comp1\Documents\Work\Old>Host_FingerPrint_SFTP.py Yay.txt
Traceback (most recent call last):
  File "C:\Users\Comp1\Documents\Work\Old\Host_FingerPrint_SFTP.py",
 line 16, in <module>
    key = paramiko.ed25519key.Ed25519Key(data=decodebytes(keydata).hex())
  File "C:\Users\Comp1\AppData\Local\Programs\Python\Python37-32\lib\site-p
ackages\paramiko\ed25519key.py", line 66, in __init__
    msg = Message(data)
  File "C:\Users\Comp1\AppData\Local\Programs\Python\Python37-32\lib\site-p
ackages\paramiko\message.py", line 52, in __init__
    self.packet = BytesIO(content)
TypeError: a bytes-like object is required, not 'str'
using the following code

import pysftp
import sys
import paramiko
from paramiko.py3compat import decodebytes
import base64
 
path = './folder/' + sys.argv[1]    #hard-coded
localpath = sys.argv[1]  
 
remote_path = "./work/" 				    #hard-coded
host = "1.2.3.4"                    	#hard-coded
password = "password"               #hard-coded
username = "username"                			#hard-coded
 
keydata = b"""RLhdtbhVbFXUeqZh6jm0t5ToI6b4IGbac8R2EkP+Ido=""" #obtained using filezilla, connected successfully then used lock icon on the bottom right to display fingerprints: SHA256: RLh...kP+Ido=
key = paramiko.ed25519key.Ed25519Key(data=decodebytes(keydata).hex())
cnopts = pysftp.CnOpts()
cnopts.hostkeys.add('1.2.3.4', 'ssh-ed25519', key)
 
 
with pysftp.Connection(host, username=username, password=password, cnopts=cnopts) as sftp:
    sftp.put(localpath, path)
 
print("Upload done")
is there a library that i need apart from base64?
Reply
#6
That's just a semantics issue, of the library being particular about what you give it. Calling .encode() on the hex string will convert it to a bytes string. They'll have the same content, but it'll be a "bytes" to make the library happy.
>>> import base64
>>> key = 'RLhdtbhVbFXUeqZh6jm0t5ToI6b4IGbac8R2EkP+Ido='
>>> text = base64.b64decode(key).hex()
>>> text
'44b85db5b8556c55d47aa661ea39b4b794e823a6f82066da73c4761243fe21da'
>>> text.encode()
b'44b85db5b8556c55d47aa661ea39b4b794e823a6f82066da73c4761243fe21da'
Reply
#7
HI Nilamo,
Thanks for sticking with me.

I am moving forward, but not quite there yet.
C:\Users\Comp1\Documents\Work\Old>Host_FingerPrint_SFTP.py Yay.txt
Traceback (most recent call last):
  File "C:\Users\Comp1\Documents\Work\Old\Host_FingerPrint_SFTP.py",
 line 17, in <module>
    key = paramiko.ed25519key.Ed25519Key(data=text.encode())
  File "C:\Users\Comp1\AppData\Local\Programs\Python\Python37-32\lib\site-p
ackages\paramiko\ed25519key.py", line 71, in __init__
    cert_type="[email protected]",
  File "C:\Users\Comp1\AppData\Local\Programs\Python\Python37-32\lib\site-p
ackages\paramiko\pkey.py", line 416, in _check_type_and_load_cert
    raise SSHException(err.format(self.__class__.__name__, type_))
paramiko.ssh_exception.SSHException: Invalid key (class: Ed25519Key, data type:
5db5b8556c55d47aa661ea39b4b794e823a6f82066da73c4761243fe21da
This makes me think that the key i copied from File Zilla is wrong (either i copied it wrong which i have checked) or (else it is not the real key...?)

Sorry,
the code i used below is
import pysftp
import sys
import paramiko
from paramiko.py3compat import decodebytes
import base64
 
path = './folder/' + sys.argv[1]    #hard-coded
localpath = sys.argv[1]  
 
remote_path = "./work/" 				    #hard-coded
host = "1.2.3.4"                    	#hard-coded
password = "password"               #hard-coded
username = "username"                			#hard-coded
 
keydata = b"""RLhdtbhVbFXUeqZh6jm0t5ToI6b4IGbac8R2EkP+Ido=""" #obtained using filezilla, connected successfully then used lock icon on the bottom right to display fingerprints: SHA256: RLh...kP+Ido=
text = base64.b64decode(keydata).hex()
key = paramiko.ed25519key.Ed25519Key(data=text.encode())
cnopts = pysftp.CnOpts()
cnopts.hostkeys.add('1.2.3.4', 'ssh-ed25519', key)
 
 
with pysftp.Connection(host, username=username, password=password, cnopts=cnopts) as sftp:
    sftp.put(localpath, path)
 
print("Upload done")
Reply
#8
(Nov-30-2018, 08:31 PM)cverm Wrote: The goal is to write a script that uploads a file to an sftp site after verifying the host ed25519 fingerprint.

I've pretty much run out of ideas. What's the purpose of the ed25519 fingerprint? What does verifying it gain you, and is using ftp without this fingerprint acceptable?
Reply
#9
Hi Nilamo,
It is acceptable to do the connection without fingerprinting, the host fingerprinting was the icing on the cake.
The rest of the script is working and the ftp is internal, i just wanted to learn how to verify the host.
But it is in no way deal breaker.
I really appreciate your efforts in helping me solve this
Thanks again
C
Reply
#10
I wish I could help better, but I really don't know as much as I should about ssh/fingerprints/etc. If you're interested in learning, I'd suggest asking the paramiko community, as they'd be more focused on the topic. The best way to reach them would probably be either irc or the mailing list:
https://github.com/paramiko/paramiko#bugs--support Wrote:Bugs & Support
Bug Reports: Github
Mailing List: [email protected] (see the LibreList website for usage details).
IRC: #paramiko on Freenode

That said, if you do get it working, I'd love to see what the working code ends up looking like :)
Reply


Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020