Browse Source

Support openssh new format key

pull/126/head
Sheng 5 years ago
parent
commit
ccb40d2bed
  1. 21
      tests/data/test_new_dsa.key
  2. 39
      tests/data/test_new_rsa_password.key
  3. 9
      tests/test_handler.py
  4. 52
      webssh/handler.py

21
tests/data/test_new_dsa.key

@ -0,0 +1,21 @@
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABsQAAAAdzc2gtZH
NzAAAAgQC5Y5rQ1EN+eWQUFv/9K/DLfPgjGC0mwyqvKsKyv6RLpKLc0vi0VDj8lY0WUcuG
CzdYnhIOSa9aB0buGe10gIjU2vAxkhqv1yaR+Zuj3dLDHQk6jpAAgNHciKlQSf1zho/seL
7nehYq/waXfU8/iJuXqywQgqpMLfaHOnIl/tPLGQAAABUArINMjWcrsmEgLmzf6k+sroko
5GkAAACAMQsRQjOtQGQA8/XI7vOWnEMCVntwt1Xi4RsLH5+4GpUMUcm4CvqjfFfSF4CufH
pjlywFhrAC2/ouQIpGJPGToWotk7dt5zWckGX5DscMiRVON7fxdpUMn16IO6DdUctXlWa9
SY+NdfRESKoUCjgH5nlM8k7N2MwCK5phHHkoPu8AAACADgxrRWeNqX3gmZUM1qhrDO0mOH
oHJFrBuvJCdQ6+S1GvjuBI0rNm225+gcaAhia9k/LGk8NwCbWG1FbpesuNaNFt/FxS9LVS
qEaZoXtKuY+CUCn1BfBWF97/u0oMPwanXKIJEAhU81f5TXZM8Ui7OEIyTx1t9qgva+5/gF
cL48kAAAHoLtDYCy7Q2AsAAAAHc3NoLWRzcwAAAIEAuWOa0NRDfnlkFBb//Svwy3z4Ixgt
JsMqryrCsr+kS6Si3NL4tFQ4/JWNFlHLhgs3WJ4SDkmvWgdG7hntdICI1NrwMZIar9cmkf
mbo93Swx0JOo6QAIDR3IipUEn9c4aP7Hi+53oWKv8Gl31PP4ibl6ssEIKqTC32hzpyJf7T
yxkAAAAVAKyDTI1nK7JhIC5s3+pPrK6JKORpAAAAgDELEUIzrUBkAPP1yO7zlpxDAlZ7cL
dV4uEbCx+fuBqVDFHJuAr6o3xX0heArnx6Y5csBYawAtv6LkCKRiTxk6FqLZO3bec1nJBl
+Q7HDIkVTje38XaVDJ9eiDug3VHLV5VmvUmPjXX0REiqFAo4B+Z5TPJOzdjMAiuaYRx5KD
7vAAAAgA4Ma0Vnjal94JmVDNaoawztJjh6ByRawbryQnUOvktRr47gSNKzZttufoHGgIYm
vZPyxpPDcAm1htRW6XrLjWjRbfxcUvS1UqhGmaF7SrmPglAp9QXwVhfe/7tKDD8Gp1yiCR
AIVPNX+U12TPFIuzhCMk8dbfaoL2vuf4BXC+PJAAAAFBVcac1iVzrWVnLglRZRenUhlKLr
AAAADHNoZW5nQHNlcnZlcgECAwQFBgc=
-----END OPENSSH PRIVATE KEY-----

39
tests/data/test_new_rsa_password.key

@ -0,0 +1,39 @@
-----BEGIN OPENSSH PRIVATE KEY-----
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
PR/8/w==
-----END OPENSSH PRIVATE KEY-----

9
tests/test_handler.py

@ -196,6 +196,15 @@ class TestPrivateKey(unittest.TestCase):
password = 'abc123'
self._test_with_encrypted_key(fname, password, paramiko.Ed25519Key)
def test_get_pkey_obj_with_encrypted_new_rsa_key(self):
fname = 'test_new_rsa_password.key'
password = '123456'
self._test_with_encrypted_key(fname, password, paramiko.RSAKey)
def test_get_pkey_obj_with_plain_new_dsa_key(self):
pk = self.get_pk_obj('test_new_dsa.key')
self.assertIsInstance(pk.get_pkey_obj(), paramiko.DSSKey)
def test_parse_name(self):
key = u'-----BEGIN PRIVATE KEY-----'
pk = PrivateKey(key)

52
webssh/handler.py

@ -118,6 +118,7 @@ class PrivateKey(object):
self.password = password
self.check_length()
self.iostr = io.StringIO(privatekey)
self.last_exception = None
def check_length(self):
if len(self.privatekey) > self.max_length:
@ -138,30 +139,49 @@ class PrivateKey(object):
break
return name, len(line_)
def get_pkey_obj(self):
name, length = self.parse_name(self.iostr, self.tag_to_name)
if not name:
raise InvalidValueError('Invalid key {}.'.format(self.filename))
offset = self.iostr.tell() - length
def get_specific_pkey(self, name, offset, password):
self.iostr.seek(offset)
logging.debug('Reset offset to {}.'.format(offset))
logging.info('Parsing {} key'.format(name))
logging.debug('Try parsing it as {} type key'.format(name))
pkeycls = getattr(paramiko, name+'Key')
password = to_bytes(self.password) if self.password else None
pkey = None
try:
return pkeycls.from_private_key(self.iostr, password=password)
pkey = pkeycls.from_private_key(self.iostr, password=password)
except paramiko.PasswordRequiredException:
raise InvalidValueError('Need a passphrase to decrypt the key.')
except paramiko.SSHException as exc:
logging.error(str(exc))
msg = 'Invalid key'
if self.password:
msg += ' or wrong passphrase "{}" for decrypting it.'.format(
self.password)
raise InvalidValueError(msg)
except (paramiko.SSHException, ValueError) as exc:
self.last_exception = exc
logging.debug(str(exc))
return pkey
def get_pkey_obj(self):
logging.info('Parsing private key {!r}'.format(self.filename))
name, length = self.parse_name(self.iostr, self.tag_to_name)
if not name:
raise InvalidValueError('Invalid key {}.'.format(self.filename))
offset = self.iostr.tell() - length
password = to_bytes(self.password) if self.password else None
pkey = self.get_specific_pkey(name, offset, password)
if pkey is None and name == 'Ed25519':
for name in ['RSA', 'ECDSA', 'DSS']:
pkey = self.get_specific_pkey(name, offset, password)
if pkey:
break
if pkey:
return pkey
logging.error(str(self.last_exception))
msg = 'Invalid key'
if self.password:
msg += ' or wrong passphrase "{}" for decrypting it.'.format(
self.password)
raise InvalidValueError(msg)
class MixinHandler(object):

Loading…
Cancel
Save