def _get_dss_key_from_string(dsskeystr=None, password=None): from paramiko.dsskey import DSSKey, SSHException, BER, BERException, randpool, util from paramiko.pkey import base64, PasswordRequiredException class MyDSSKey(DSSKey): def __init__(self): pass self = MyDSSKey() tag = 'DSA' lines = dsskeystr.split('\n') # adapted from PKey._read_private_key_file() while True: start = 0 while (start < len(lines)) and (lines[start].strip() != '-----BEGIN ' + tag + ' PRIVATE KEY-----'): start += 1 if start >= len(lines): raise SSHException('not a valid ' + tag + ' private key file') # parse any headers first headers = {} start += 1 while start < len(lines): l = lines[start].split(': ') if len(l) == 1: break headers[l[0].lower()] = l[1].strip() start += 1 # find end end = start while (lines[end].strip() != '-----END ' + tag + ' PRIVATE KEY-----') and (end < len(lines)): end += 1 # if we trudged to the end of the file, just try to cope. try: data = base64.decodestring(''.join(lines[start:end])) except base64.binascii.Error, e: raise SSHException('base64 decoding error: ' + str(e)) if not headers.has_key('proc-type'): # unencryped: done break # encrypted keyfile: will need a password if headers['proc-type'] != '4,ENCRYPTED': raise SSHException('Unknown private key structure "%s"' % headers['proc-type']) try: encryption_type, saltstr = headers['dek-info'].split(',') except: raise SSHException('Can\'t parse DEK-info in private key file') if not self._CIPHER_TABLE.has_key(encryption_type): raise SSHException('Unknown private key cipher "%s"' % encryption_type) # if no password was passed in, raise an exception pointing out that we need one if password is None: raise PasswordRequiredException('Private key file is encrypted') cipher = self._CIPHER_TABLE[encryption_type]['cipher'] keysize = self._CIPHER_TABLE[encryption_type]['keysize'] mode = self._CIPHER_TABLE[encryption_type]['mode'] salt = util.unhexify(saltstr) key = util.generate_key_bytes(MD5, salt, password, keysize) data = cipher.new(key, mode, salt).decrypt(data) break
def _get_dss_key_as_string(dsskey=None, password=None): # patching paramiko to get a string instead of a file self = dsskey from paramiko.dsskey import SSHException, BER, BERException, randpool, util from paramiko.pkey import base64 # adapted from DSSKey.write_private_key_file() if self.x is None: raise SSHException('Not enough key information') keylist = [ 0, self.p, self.q, self.g, self.y, self.x ] try: b = BER() b.encode(keylist) except BERException: raise SSHException('Unable to create ber encoding of key') f = [] # adapted from PKey._write_private_key_file() tag, data, password = '******', str(b), password f.append('-----BEGIN %s PRIVATE KEY-----\n' % tag) if password is not None: # since we only support one cipher here, use it cipher_name = self._CIPHER_TABLE.keys()[0] cipher = self._CIPHER_TABLE[cipher_name]['cipher'] keysize = self._CIPHER_TABLE[cipher_name]['keysize'] blocksize = self._CIPHER_TABLE[cipher_name]['blocksize'] mode = self._CIPHER_TABLE[cipher_name]['mode'] salt = randpool.get_bytes(8) key = util.generate_key_bytes(MD5, salt, password, keysize) if len(data) % blocksize != 0: n = blocksize - len(data) % blocksize #data += randpool.get_bytes(n) # that would make more sense ^, but it confuses openssh. data += '\0' * n data = cipher.new(key, mode, salt).encrypt(data) f.append('Proc-Type: 4,ENCRYPTED\n') f.append('DEK-Info: %s,%s\n' % (cipher_name, util.hexify(salt))) f.append('\n') s = base64.encodestring(data) # re-wrap to 64-char lines s = ''.join(s.split('\n')) s = '\n'.join([s[i : i+64] for i in range(0, len(s), 64)]) f.append(s) f.append('\n') f.append('-----END %s PRIVATE KEY-----\n' % tag) return ''.join(f)