from __future__ import print_function
import base64
import hashlib
import struct
from os.path import basename
from cloudmesh_client.common.ConfigDict import Config
# noinspection PyBroadException
[docs]class SSHkey(object):
def __init__(self, file_path=None, keyname=None):
self.__key__ = None
self.read(file_path, keyname)
[docs] def get(self):
return self.__key__
def __str__(self):
return self.__key__['key']
def __repr__(self):
return self.__key__['key']
[docs] def read(self, file_path, keyname=None):
self.__key__ = {}
if file_path is not None:
orig_path = file_path
file_path = Config.path_expand(file_path)
uri = 'file://{}'.format(file_path)
self.__key__ = {
'uri': uri,
'path': orig_path,
'string': open(file_path, "r").read().rstrip()
}
(self.__key__['type'],
self.__key__['key'],
self.__key__['comment']) = self._parse(self.__key__['string'])
self.__key__['fingerprint'] = self._fingerprint(self.__key__['string'])
# Workaround for multiple file entries in cloudmesh.yaml getting same name derived from file name (like id_rsa).
# This caused the dict to have just 1 entry as the name is the key.
# Change tracked in git issue #8
if keyname is None:
name = basename(file_path).replace(".pub", "").replace("id_", "")
else:
name = keyname
self.__key__['name'] = name
self.__key__['comment'] = self.__key__['comment']
self.__key__['source'] = 'ssh'
return self.__key__
@property
def fingerprint(self):
return self.__key__['fingerprint']
@property
def key(self):
return self.__key__['string']
@property
def type(self):
return self.__key__['type']
@property
def comment(self):
return self.__key__['comment']
@classmethod
def _fingerprint(cls, entirekey):
"""returns the fingerprint of a key.
:param entirekey: the key
:type entirekey: string
"""
t, keystring, comment = cls._parse(entirekey)
if keystring is not None:
return cls._key_fingerprint(keystring)
else:
return ''
@classmethod
def _key_fingerprint(cls, key_string):
"""create the fingerprint form just the key.
:param key_string: the key
:type key_string: string
"""
# key = base64.decodestring(key_string)
# fp_plain = hashlib.md5(key).hexdigest()
key_padding = key_string.strip() + '=' * (4 - len(key_string.strip()) % 4)
key = base64.b64decode(key_padding.encode('ascii'))
fp_plain = hashlib.md5(key).hexdigest()
return ':'.join(a + b for a, b in zip(fp_plain[::2], fp_plain[1::2]))
@classmethod
def _parse(cls, keystring):
"""
parse the keystring/keycontent into type,key,comment
:param keystring: the content of a key in string format
"""
# comment section could have a space too
keysegments = keystring.split(" ", 2)
keytype = keysegments[0]
key = None
comment = None
if len(keysegments) > 1:
key = keysegments[1]
if len(keysegments) > 2:
comment = keysegments[2]
return keytype, key, comment
def _validate(self, keytype, key):
"""reads the key string from a file. THIS FUNCTION HAS A BUG.
:param key: either the name of a file that contains the key, or the entire contents of such a file
:param keytype: if 'file' the key is read form the file specified in key.
if 'string' the key is passed as a string in key
"""
keystring = None
if keytype.lower() == "file":
try:
keystring = open(key, "r").read()
except:
return False
elif keytype.lower() == "string":
keystring = key
try:
keytype, key_string, comment = self._parse(keystring)
data = base64.decodestring(key_string)
int_len = 4
str_len = struct.unpack('>I', data[:int_len])[0]
# this should return 7
if data[int_len:int_len + str_len] == keytype:
return True
except Exception as e:
# print(e)
return False
def _keyname_sanitation(self, username, keyname):
keynamenew = "%s_%s" % (
username, keyname.replace('.', '_').replace('@', '_'))
return keynamenew
# unit testing
[docs]def main():
from pprint import pprint
sshkey = SSHkey("~/.ssh/id_rsa.pub")
pprint(sshkey.key)
print("Fingerprint:", sshkey.fingerprint)
pprint(sshkey.__key__)
print("sshkey", sshkey)
print("str", str(sshkey))
print(sshkey.type)
print(sshkey.__key__['key'])
print(sshkey.key)
print(sshkey.comment)
"""
key1 = "ssh-rsa abcdefg comment"
key2 = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDD+NswLi/zjz" + \
"7Vf575eo9iWWku5m4nVSPMgP13JbKCTVKtavAXt8UPZTkYVWi" + \
"USeXRqlf+EZM11U8Mq6C/P/ECJS868rn2KSwFosNPF0OOz8zm" + \
"TvBQShtvBBBVd1kmZePxFGviZbKwe3z3iATLKE8h7pwcupqTi" + \
"n9m3FhQRsGSF7YTFcGXv0ZqxFA2j9+Ix7SVbN5IYxxgwc+mxO" + \
"zYIy1SKEAOPJQFXKkiXxNdLSzGgjkurhPAIns8MNYL9usKMGz" + \
"hgp656onGkSbQHZR3ZHsSsTXWP3SV5ih4QTTFunwB6C0TMQVs" + \
"EGw1P49hhFktb3md+RC4DFP7ZOzfkd9nne2B mycomment"
print(key_validate("string", key1))
print(key_validate("string", key2))
print(key_parse(key1))
print(key_parse("abcdedfg"))
print(key_parse("ssh-rsa somestringhere")[2])
"""
if __name__ == "__main__":
main()