root/trunk/twisted/conch/scripts/ckeygen.py

Revision 32921, 6.7 KB (checked in by teratorn, 7 months ago)

Merge make-zshcomp-dynamic-3078-6: New tab-completion system
Author: teratorn
Reviewer: glyph, exarkun
Fixes: #3078

Deprecates t.p.zshcomp in favor of a tab-completion system in t.p.usage - completion matches are generated dynamically at tab-press time.

Line 
1# -*- test-case-name: twisted.conch.test.test_ckeygen -*-
2# Copyright (c) Twisted Matrix Laboratories.
3# See LICENSE for details.
4
5"""
6Implementation module for the `ckeygen` command.
7"""
8
9import sys, os, getpass, socket
10if getpass.getpass == getpass.unix_getpass:
11    try:
12        import termios # hack around broken termios
13        termios.tcgetattr, termios.tcsetattr
14    except (ImportError, AttributeError):
15        sys.modules['termios'] = None
16        reload(getpass)
17
18from twisted.conch.ssh import keys
19from twisted.python import filepath, log, usage, randbytes
20
21
22class GeneralOptions(usage.Options):
23    synopsis = """Usage:    ckeygen [options]
24 """
25
26    longdesc = "ckeygen manipulates public/private keys in various ways."
27
28    optParameters = [['bits', 'b', 1024, 'Number of bits in the key to create.'],
29                     ['filename', 'f', None, 'Filename of the key file.'],
30                     ['type', 't', None, 'Specify type of key to create.'],
31                     ['comment', 'C', None, 'Provide new comment.'],
32                     ['newpass', 'N', None, 'Provide new passphrase.'],
33                     ['pass', 'P', None, 'Provide old passphrase']]
34
35    optFlags = [['fingerprint', 'l', 'Show fingerprint of key file.'],
36                ['changepass', 'p', 'Change passphrase of private key file.'],
37                ['quiet', 'q', 'Quiet.'],
38                ['showpub', 'y', 'Read private key file and print public key.']]
39
40    compData = usage.Completions(
41        optActions={"type": usage.CompleteList(["rsa", "dsa"])})
42
43def run():
44    options = GeneralOptions()
45    try:
46        options.parseOptions(sys.argv[1:])
47    except usage.UsageError, u:
48        print 'ERROR: %s' % u
49        options.opt_help()
50        sys.exit(1)
51    log.discardLogs()
52    log.deferr = handleError # HACK
53    if options['type']:
54        if options['type'] == 'rsa':
55            generateRSAkey(options)
56        elif options['type'] == 'dsa':
57            generateDSAkey(options)
58        else:
59            sys.exit('Key type was %s, must be one of: rsa, dsa' % options['type'])
60    elif options['fingerprint']:
61        printFingerprint(options)
62    elif options['changepass']:
63        changePassPhrase(options)
64    elif options['showpub']:
65        displayPublicKey(options)
66    else:
67        options.opt_help()
68        sys.exit(1)
69
70def handleError():
71    from twisted.python import failure
72    global exitStatus
73    exitStatus = 2
74    log.err(failure.Failure())
75    reactor.stop()
76    raise
77
78def generateRSAkey(options):
79    from Crypto.PublicKey import RSA
80    print 'Generating public/private rsa key pair.'
81    key = RSA.generate(int(options['bits']), randbytes.secureRandom)
82    _saveKey(key, options)
83
84def generateDSAkey(options):
85    from Crypto.PublicKey import DSA
86    print 'Generating public/private dsa key pair.'
87    key = DSA.generate(int(options['bits']), randbytes.secureRandom)
88    _saveKey(key, options)
89
90
91def printFingerprint(options):
92    if not options['filename']:
93        filename = os.path.expanduser('~/.ssh/id_rsa')
94        options['filename'] = raw_input('Enter file in which the key is (%s): ' % filename)
95    if os.path.exists(options['filename']+'.pub'):
96        options['filename'] += '.pub'
97    try:
98        key = keys.Key.fromFile(options['filename'])
99        obj = key.keyObject
100        string = key.blob()
101        print '%s %s %s' % (
102            obj.size() + 1,
103            key.fingerprint(),
104            os.path.basename(options['filename']))
105    except:
106        sys.exit('bad key')
107
108
109def changePassPhrase(options):
110    if not options['filename']:
111        filename = os.path.expanduser('~/.ssh/id_rsa')
112        options['filename'] = raw_input('Enter file in which the key is (%s): ' % filename)
113    try:
114        key = keys.Key.fromFile(options['filename']).keyObject
115    except keys.BadKeyError, e:
116        if e.args[0] != 'encrypted key with no passphrase':
117            raise
118        else:
119            if not options['pass']:
120                options['pass'] = getpass.getpass('Enter old passphrase: ')
121            key = keys.Key.fromFile(
122                options['filename'], passphrase = options['pass']).keyObject
123    if not options['newpass']:
124        while 1:
125            p1 = getpass.getpass('Enter new passphrase (empty for no passphrase): ')
126            p2 = getpass.getpass('Enter same passphrase again: ')
127            if p1 == p2:
128                break
129            print 'Passphrases do not match.  Try again.'
130        options['newpass'] = p1
131    open(options['filename'], 'w').write(
132        keys.Key(key).toString(passphrase=options['newpass']))
133    print 'Your identification has been saved with the new passphrase.'
134
135
136def displayPublicKey(options):
137    if not options['filename']:
138        filename = os.path.expanduser('~/.ssh/id_rsa')
139        options['filename'] = raw_input('Enter file in which the key is (%s): ' % filename)
140    try:
141        key = keys.Key.fromFile(options['filename']).keyObject
142    except keys.BadKeyError, e:
143        if e.args[0] != 'encrypted key with no passphrase':
144            raise
145        else:
146            if not options['pass']:
147                options['pass'] = getpass.getpass('Enter passphrase: ')
148            key = keys.Key.fromFile(
149                options['filename'], passphrase = options['pass']).keyObject
150    print keys.Key(key).public().toString()
151
152
153def _saveKey(key, options):
154    if not options['filename']:
155        kind = keys.objectType(key)
156        kind = {'ssh-rsa':'rsa','ssh-dss':'dsa'}[kind]
157        filename = os.path.expanduser('~/.ssh/id_%s'%kind)
158        options['filename'] = raw_input('Enter file in which to save the key (%s): '%filename).strip() or filename
159    if os.path.exists(options['filename']):
160        print '%s already exists.' % options['filename']
161        yn = raw_input('Overwrite (y/n)? ')
162        if yn[0].lower() != 'y':
163            sys.exit()
164    if not options['pass']:
165        while 1:
166            p1 = getpass.getpass('Enter passphrase (empty for no passphrase): ')
167            p2 = getpass.getpass('Enter same passphrase again: ')
168            if p1 == p2:
169                break
170            print 'Passphrases do not match.  Try again.'
171        options['pass'] = p1
172
173    keyObj = keys.Key(key)
174    comment = '%s@%s' % (getpass.getuser(), socket.gethostname())
175
176    filepath.FilePath(options['filename']).setContent(
177        keyObj.toString('openssh', options['pass']))
178    os.chmod(options['filename'], 33152)
179
180    filepath.FilePath(options['filename'] + '.pub').setContent(
181        keyObj.public().toString('openssh', comment))
182
183    print 'Your identification has been saved in %s' % options['filename']
184    print 'Your public key has been saved in %s.pub' % options['filename']
185    print 'The key fingerprint is:'
186    print keyObj.fingerprint()
187
188if __name__ == '__main__':
189    run()
Note: See TracBrowser for help on using the browser.