| 1 |
|
|---|
| 2 |
|
|---|
| 3 |
|
|---|
| 4 |
|
|---|
| 5 |
""" |
|---|
| 6 |
Handling of RSA and DSA keys. |
|---|
| 7 |
|
|---|
| 8 |
Maintainer: U{Paul Swartz} |
|---|
| 9 |
""" |
|---|
| 10 |
|
|---|
| 11 |
|
|---|
| 12 |
import base64 |
|---|
| 13 |
import warnings |
|---|
| 14 |
import itertools |
|---|
| 15 |
|
|---|
| 16 |
|
|---|
| 17 |
from Crypto.Cipher import DES3 |
|---|
| 18 |
from Crypto.PublicKey import RSA, DSA |
|---|
| 19 |
from Crypto import Util |
|---|
| 20 |
from pyasn1.type import univ |
|---|
| 21 |
from pyasn1.codec.ber import decoder as berDecoder |
|---|
| 22 |
from pyasn1.codec.ber import encoder as berEncoder |
|---|
| 23 |
|
|---|
| 24 |
|
|---|
| 25 |
from twisted.python import randbytes |
|---|
| 26 |
from twisted.python.hashlib import md5, sha1 |
|---|
| 27 |
|
|---|
| 28 |
|
|---|
| 29 |
from twisted.conch.ssh import common, sexpy |
|---|
| 30 |
|
|---|
| 31 |
|
|---|
| 32 |
class BadKeyError(Exception): |
|---|
| 33 |
""" |
|---|
| 34 |
Raised when a key isn't what we expected from it. |
|---|
| 35 |
|
|---|
| 36 |
XXX: we really need to check for bad keys |
|---|
| 37 |
""" |
|---|
| 38 |
|
|---|
| 39 |
class EncryptedKeyError(Exception): |
|---|
| 40 |
""" |
|---|
| 41 |
Raised when an encrypted key is presented to fromString/fromFile without |
|---|
| 42 |
a password. |
|---|
| 43 |
""" |
|---|
| 44 |
|
|---|
| 45 |
class Key(object): |
|---|
| 46 |
""" |
|---|
| 47 |
An object representing a key. A key can be either a public or |
|---|
| 48 |
private key. A public key can verify a signature; a private key can |
|---|
| 49 |
create or verify a signature. To generate a string that can be stored |
|---|
| 50 |
on disk, use the toString method. If you have a private key, but want |
|---|
| 51 |
the string representation of the public key, use Key.public().toString(). |
|---|
| 52 |
|
|---|
| 53 |
@ivar keyObject: The C{Crypto.PublicKey.pubkey.pubkey} object that |
|---|
| 54 |
operations are performed with. |
|---|
| 55 |
""" |
|---|
| 56 |
|
|---|
| 57 |
def fromFile(Class, filename, type=None, passphrase=None): |
|---|
| 58 |
""" |
|---|
| 59 |
Return a Key object corresponding to the data in filename. type |
|---|
| 60 |
and passphrase function as they do in fromString. |
|---|
| 61 |
""" |
|---|
| 62 |
return Class.fromString(file(filename, 'rb').read(), type, passphrase) |
|---|
| 63 |
fromFile = classmethod(fromFile) |
|---|
| 64 |
|
|---|
| 65 |
def fromString(Class, data, type=None, passphrase=None): |
|---|
| 66 |
""" |
|---|
| 67 |
Return a Key object corresponding to the string data. |
|---|
| 68 |
type is optionally the type of string, matching a _fromString_* |
|---|
| 69 |
method. Otherwise, the _guessStringType() classmethod will be used |
|---|
| 70 |
to guess a type. If the key is encrypted, passphrase is used as |
|---|
| 71 |
the decryption key. |
|---|
| 72 |
|
|---|
| 73 |
@type data: C{str} |
|---|
| 74 |
@type type: C{None}/C{str} |
|---|
| 75 |
@type passphrase: C{None}/C{str} |
|---|
| 76 |
@rtype: C{Key} |
|---|
| 77 |
""" |
|---|
| 78 |
if type is None: |
|---|
| 79 |
type = Class._guessStringType(data) |
|---|
| 80 |
if type is None: |
|---|
| 81 |
raise BadKeyError('cannot guess the type of %r' % data) |
|---|
| 82 |
method = getattr(Class, '_fromString_%s' % type.upper(), None) |
|---|
| 83 |
if method is None: |
|---|
| 84 |
raise BadKeyError('no _fromString method for %s' % type) |
|---|
| 85 |
if method.func_code.co_argcount == 2: |
|---|
| 86 |
if passphrase: |
|---|
| 87 |
raise BadKeyError('key not encrypted') |
|---|
| 88 |
return method(data) |
|---|
| 89 |
else: |
|---|
| 90 |
return method(data, passphrase) |
|---|
| 91 |
fromString = classmethod(fromString) |
|---|
| 92 |
|
|---|
| 93 |
def _fromString_BLOB(Class, blob): |
|---|
| 94 |
""" |
|---|
| 95 |
Return a public key object corresponding to this public key blob. |
|---|
| 96 |
The format of a RSA public key blob is:: |
|---|
| 97 |
string 'ssh-rsa' |
|---|
| 98 |
integer e |
|---|
| 99 |
integer n |
|---|
| 100 |
|
|---|
| 101 |
The format of a DSA public key blob is:: |
|---|
| 102 |
string 'ssh-dss' |
|---|
| 103 |
integer p |
|---|
| 104 |
integer q |
|---|
| 105 |
integer g |
|---|
| 106 |
integer y |
|---|
| 107 |
|
|---|
| 108 |
@type blob: C{str} |
|---|
| 109 |
@return: a C{Crypto.PublicKey.pubkey.pubkey} object |
|---|
| 110 |
@raises BadKeyError: if the key type (the first string) is unknown. |
|---|
| 111 |
""" |
|---|
| 112 |
keyType, rest = common.getNS(blob) |
|---|
| 113 |
if keyType == 'ssh-rsa': |
|---|
| 114 |
e, n, rest = common.getMP(rest, 2) |
|---|
| 115 |
return Class(RSA.construct((n, e))) |
|---|
| 116 |
elif keyType == 'ssh-dss': |
|---|
| 117 |
p, q, g, y, rest = common.getMP(rest, 4) |
|---|
| 118 |
return Class(DSA.construct((y, g, p, q))) |
|---|
| 119 |
else: |
|---|
| 120 |
raise BadKeyError('unknown blob type: %s' % keyType) |
|---|
| 121 |
_fromString_BLOB = classmethod(_fromString_BLOB) |
|---|
| 122 |
|
|---|
| 123 |
def _fromString_PRIVATE_BLOB(Class, blob): |
|---|
| 124 |
""" |
|---|
| 125 |
Return a private key object corresponding to this private key blob. |
|---|
| 126 |
The blob formats are as follows: |
|---|
| 127 |
|
|---|
| 128 |
RSA keys:: |
|---|
| 129 |
string 'ssh-rsa' |
|---|
| 130 |
integer n |
|---|
| 131 |
integer e |
|---|
| 132 |
integer d |
|---|
| 133 |
integer u |
|---|
| 134 |
integer p |
|---|
| 135 |
integer q |
|---|
| 136 |
|
|---|
| 137 |
DSA keys:: |
|---|
| 138 |
string 'ssh-dss' |
|---|
| 139 |
integer p |
|---|
| 140 |
integer q |
|---|
| 141 |
integer g |
|---|
| 142 |
integer y |
|---|
| 143 |
integer x |
|---|
| 144 |
|
|---|
| 145 |
@type blob: C{str} |
|---|
| 146 |
@return: a C{Crypto.PublicKey.pubkey.pubkey} object |
|---|
| 147 |
@raises BadKeyError: if the key type (the first string) is unknown. |
|---|
| 148 |
""" |
|---|
| 149 |
keyType, rest = common.getNS(blob) |
|---|
| 150 |
|
|---|
| 151 |
if keyType == 'ssh-rsa': |
|---|
| 152 |
n, e, d, u, p, q, rest = common.getMP(rest, 6) |
|---|
| 153 |
rsakey = Class(RSA.construct((n, e, d, p, q, u))) |
|---|
| 154 |
return rsakey |
|---|
| 155 |
elif keyType == 'ssh-dss': |
|---|
| 156 |
p, q, g, y, x, rest = common.getMP(rest, 5) |
|---|
| 157 |
dsakey = Class(DSA.construct((y, g, p, q, x))) |
|---|
| 158 |
return dsakey |
|---|
| 159 |
else: |
|---|
| 160 |
raise BadKeyError('unknown blob type: %s' % keyType) |
|---|
| 161 |
_fromString_PRIVATE_BLOB = classmethod(_fromString_PRIVATE_BLOB) |
|---|
| 162 |
|
|---|
| 163 |
def _fromString_PUBLIC_OPENSSH(Class, data): |
|---|
| 164 |
""" |
|---|
| 165 |
Return a public key object corresponding to this OpenSSH public key |
|---|
| 166 |
string. The format of an OpenSSH public key string is:: |
|---|
| 167 |
<key type> <base64-encoded public key blob> |
|---|
| 168 |
|
|---|
| 169 |
@type data: C{str} |
|---|
| 170 |
@return: A {Crypto.PublicKey.pubkey.pubkey} object |
|---|
| 171 |
@raises BadKeyError: if the blob type is unknown. |
|---|
| 172 |
""" |
|---|
| 173 |
blob = base64.decodestring(data.split()[1]) |
|---|
| 174 |
return Class._fromString_BLOB(blob) |
|---|
| 175 |
_fromString_PUBLIC_OPENSSH = classmethod(_fromString_PUBLIC_OPENSSH) |
|---|
| 176 |
|
|---|
| 177 |
def _fromString_PRIVATE_OPENSSH(Class, data, passphrase): |
|---|
| 178 |
""" |
|---|
| 179 |
Return a private key object corresponding to this OpenSSH private key |
|---|
| 180 |
string. If the key is encrypted, passphrase MUST be provided. |
|---|
| 181 |
Providing a passphrase for an unencrypted key is an error. |
|---|
| 182 |
|
|---|
| 183 |
The format of an OpenSSH private key string is:: |
|---|
| 184 |
-----BEGIN <key type> PRIVATE KEY----- |
|---|
| 185 |
[Proc-Type: 4,ENCRYPTED |
|---|
| 186 |
DEK-Info: DES-EDE3-CBC,<initialization value>] |
|---|
| 187 |
<base64-encoded ASN.1 structure> |
|---|
| 188 |
------END <key type> PRIVATE KEY------ |
|---|
| 189 |
|
|---|
| 190 |
The ASN.1 structure of a RSA key is:: |
|---|
| 191 |
(0, n, e, d, p, q) |
|---|
| 192 |
|
|---|
| 193 |
The ASN.1 structure of a DSA key is:: |
|---|
| 194 |
(0, p, q, g, y, x) |
|---|
| 195 |
|
|---|
| 196 |
@type data: C{str} |
|---|
| 197 |
@type passphrase: C{str} |
|---|
| 198 |
@return: a C{Crypto.PublicKey.pubkey.pubkey} object |
|---|
| 199 |
@raises BadKeyError: if |
|---|
| 200 |
* a passphrase is provided for an unencrypted key |
|---|
| 201 |
* a passphrase is not provided for an encrypted key |
|---|
| 202 |
* the ASN.1 encoding is incorrect |
|---|
| 203 |
""" |
|---|
| 204 |
lines = [x + '\n' for x in data.split('\n')] |
|---|
| 205 |
kind = lines[0][11:14] |
|---|
| 206 |
if lines[1].startswith('Proc-Type: 4,ENCRYPTED'): |
|---|
| 207 |
ivdata = lines[2].split(',')[1][:-1] |
|---|
| 208 |
iv = ''.join([chr(int(ivdata[i:i + 2], 16)) for i in range(0, |
|---|
| 209 |
len(ivdata), 2)]) |
|---|
| 210 |
if not passphrase: |
|---|
| 211 |
raise EncryptedKeyError('encrypted key with no passphrase') |
|---|
| 212 |
ba = md5(passphrase + iv).digest() |
|---|
| 213 |
bb = md5(ba + passphrase + iv).digest() |
|---|
| 214 |
decKey = (ba + bb)[:24] |
|---|
| 215 |
b64Data = base64.decodestring(''.join(lines[3:-1])) |
|---|
| 216 |
keyData = DES3.new(decKey, DES3.MODE_CBC, iv).decrypt(b64Data) |
|---|
| 217 |
removeLen = ord(keyData[-1]) |
|---|
| 218 |
keyData = keyData[:-removeLen] |
|---|
| 219 |
else: |
|---|
| 220 |
b64Data = ''.join(lines[1:-1]) |
|---|
| 221 |
keyData = base64.decodestring(b64Data) |
|---|
| 222 |
try: |
|---|
| 223 |
decodedKey = berDecoder.decode(keyData)[0] |
|---|
| 224 |
except Exception, e: |
|---|
| 225 |
raise BadKeyError, 'something wrong with decode' |
|---|
| 226 |
if kind == 'RSA': |
|---|
| 227 |
if len(decodedKey) == 2: |
|---|
| 228 |
decodedKey = decodedKey[0] |
|---|
| 229 |
if len(decodedKey) < 6: |
|---|
| 230 |
raise BadKeyError('RSA key failed to decode properly') |
|---|
| 231 |
n, e, d, p, q = [long(value) for value in decodedKey[1:6]] |
|---|
| 232 |
if p > q: |
|---|
| 233 |
p, q = q, p |
|---|
| 234 |
return Class(RSA.construct((n, e, d, p, q))) |
|---|
| 235 |
elif kind == 'DSA': |
|---|
| 236 |
p, q, g, y, x = [long(value) for value in decodedKey[1: 6]] |
|---|
| 237 |
if len(decodedKey) < 6: |
|---|
| 238 |
raise BadKeyError('DSA key failed to decode properly') |
|---|
| 239 |
return Class(DSA.construct((y, g, p, q, x))) |
|---|
| 240 |
_fromString_PRIVATE_OPENSSH = classmethod(_fromString_PRIVATE_OPENSSH) |
|---|
| 241 |
|
|---|
| 242 |
def _fromString_PUBLIC_LSH(Class, data): |
|---|
| 243 |
""" |
|---|
| 244 |
Return a public key corresponding to this LSH public key string. |
|---|
| 245 |
The LSH public key string format is:: |
|---|
| 246 |
<s-expression: ('public-key', (<key type>, (<name, <value>)+))> |
|---|
| 247 |
|
|---|
| 248 |
The names for a RSA (key type 'rsa-pkcs1-sha1') key are: n, e. |
|---|
| 249 |
The names for a DSA (key type 'dsa') key are: y, g, p, q. |
|---|
| 250 |
|
|---|
| 251 |
@type data: C{str} |
|---|
| 252 |
@return: a C{Crypto.PublicKey.pubkey.pubkey} object |
|---|
| 253 |
@raises BadKeyError: if the key type is unknown |
|---|
| 254 |
""" |
|---|
| 255 |
sexp = sexpy.parse(base64.decodestring(data[1:-1])) |
|---|
| 256 |
assert sexp[0] == 'public-key' |
|---|
| 257 |
kd = {} |
|---|
| 258 |
for name, data in sexp[1][1:]: |
|---|
| 259 |
kd[name] = common.getMP(common.NS(data))[0] |
|---|
| 260 |
if sexp[1][0] == 'dsa': |
|---|
| 261 |
return Class(DSA.construct((kd['y'], kd['g'], kd['p'], kd['q']))) |
|---|
| 262 |
elif sexp[1][0] == 'rsa-pkcs1-sha1': |
|---|
| 263 |
return Class(RSA.construct((kd['n'], kd['e']))) |
|---|
| 264 |
else: |
|---|
| 265 |
raise BadKeyError('unknown lsh key type %s' % sexp[1][0]) |
|---|
| 266 |
_fromString_PUBLIC_LSH = classmethod(_fromString_PUBLIC_LSH) |
|---|
| 267 |
|
|---|
| 268 |
def _fromString_PRIVATE_LSH(Class, data): |
|---|
| 269 |
""" |
|---|
| 270 |
Return a private key corresponding to this LSH private key string. |
|---|
| 271 |
The LSH private key string format is:: |
|---|
| 272 |
<s-expression: ('private-key', (<key type>, (<name>, <value>)+))> |
|---|
| 273 |
|
|---|
| 274 |
The names for a RSA (key type 'rsa-pkcs1-sha1') key are: n, e, d, p, q. |
|---|
| 275 |
The names for a DSA (key type 'dsa') key are: y, g, p, q, x. |
|---|
| 276 |
|
|---|
| 277 |
@type data: C{str} |
|---|
| 278 |
@return: a {Crypto.PublicKey.pubkey.pubkey} object |
|---|
| 279 |
@raises BadKeyError: if the key type is unknown |
|---|
| 280 |
""" |
|---|
| 281 |
sexp = sexpy.parse(data) |
|---|
| 282 |
assert sexp[0] == 'private-key' |
|---|
| 283 |
kd = {} |
|---|
| 284 |
for name, data in sexp[1][1:]: |
|---|
| 285 |
kd[name] = common.getMP(common.NS(data))[0] |
|---|
| 286 |
if sexp[1][0] == 'dsa': |
|---|
| 287 |
assert len(kd) == 5, len(kd) |
|---|
| 288 |
return Class(DSA.construct((kd['y'], kd['g'], kd['p'], |
|---|
| 289 |
kd['q'], kd['x']))) |
|---|
| 290 |
elif sexp[1][0] == 'rsa-pkcs1': |
|---|
| 291 |
assert len(kd) == 8, len(kd) |
|---|
| 292 |
if kd['p'] > kd['q']: |
|---|
| 293 |
kd['p'], kd['q'] = kd['q'], kd['p'] |
|---|
| 294 |
return Class(RSA.construct((kd['n'], kd['e'], kd['d'], |
|---|
| 295 |
kd['p'], kd['q']))) |
|---|
| 296 |
else: |
|---|
| 297 |
raise BadKeyError('unknown lsh key type %s' % sexp[1][0]) |
|---|
| 298 |
_fromString_PRIVATE_LSH = classmethod(_fromString_PRIVATE_LSH) |
|---|
| 299 |
|
|---|
| 300 |
def _fromString_AGENTV3(Class, data): |
|---|
| 301 |
""" |
|---|
| 302 |
Return a private key object corresponsing to the Secure Shell Key |
|---|
| 303 |
Agent v3 format. |
|---|
| 304 |
|
|---|
| 305 |
The SSH Key Agent v3 format for a RSA key is:: |
|---|
| 306 |
string 'ssh-rsa' |
|---|
| 307 |
integer e |
|---|
| 308 |
integer d |
|---|
| 309 |
integer n |
|---|
| 310 |
integer u |
|---|
| 311 |
integer p |
|---|
| 312 |
integer q |
|---|
| 313 |
|
|---|
| 314 |
The SSH Key Agent v3 format for a DSA key is:: |
|---|
| 315 |
string 'ssh-dss' |
|---|
| 316 |
integer p |
|---|
| 317 |
integer q |
|---|
| 318 |
integer g |
|---|
| 319 |
integer y |
|---|
| 320 |
integer x |
|---|
| 321 |
|
|---|
| 322 |
@type data: C{str} |
|---|
| 323 |
@return: a C{Crypto.PublicKey.pubkey.pubkey} object |
|---|
| 324 |
@raises BadKeyError: if the key type (the first string) is unknown |
|---|
| 325 |
""" |
|---|
| 326 |
keyType, data = common.getNS(data) |
|---|
| 327 |
if keyType == 'ssh-dss': |
|---|
| 328 |
p, data = common.getMP(data) |
|---|
| 329 |
q, data = common.getMP(data) |
|---|
| 330 |
g, data = common.getMP(data) |
|---|
| 331 |
y, data = common.getMP(data) |
|---|
| 332 |
x, data = common.getMP(data) |
|---|
| 333 |
return Class(DSA.construct((y,g,p,q,x))) |
|---|
| 334 |
elif keyType == 'ssh-rsa': |
|---|
| 335 |
e, data = common.getMP(data) |
|---|
| 336 |
d, data = common.getMP(data) |
|---|
| 337 |
n, data = common.getMP(data) |
|---|
| 338 |
u, data = common.getMP(data) |
|---|
| 339 |
p, data = common.getMP(data) |
|---|
| 340 |
q, data = common.getMP(data) |
|---|
| 341 |
return Class(RSA.construct((n,e,d,p,q,u))) |
|---|
| 342 |
else: |
|---|
| 343 |
raise BadKeyError("unknown key type %s" % keyType) |
|---|
| 344 |
_fromString_AGENTV3 = classmethod(_fromString_AGENTV3) |
|---|
| 345 |
|
|---|
| 346 |
def _guessStringType(Class, data): |
|---|
| 347 |
""" |
|---|
| 348 |
Guess the type of key in data. The types map to _fromString_* |
|---|
| 349 |
methods. |
|---|
| 350 |
""" |
|---|
| 351 |
if data.startswith('ssh-'): |
|---|
| 352 |
return 'public_openssh' |
|---|
| 353 |
elif data.startswith('-----BEGIN'): |
|---|
| 354 |
return 'private_openssh' |
|---|
| 355 |
elif data.startswith('{'): |
|---|
| 356 |
return 'public_lsh' |
|---|
| 357 |
elif data.startswith('('): |
|---|
| 358 |
return 'private_lsh' |
|---|
| 359 |
elif data.startswith('\x00\x00\x00\x07ssh-'): |
|---|
| 360 |
ignored, rest = common.getNS(data) |
|---|
| 361 |
count = 0 |
|---|
| 362 |
while rest: |
|---|
| 363 |
count += 1 |
|---|
| 364 |
ignored, rest = common.getMP(rest) |
|---|
| 365 |
if count > 4: |
|---|
| 366 |
return 'agentv3' |
|---|
| 367 |
else: |
|---|
| 368 |
return 'blob' |
|---|
| 369 |
_guessStringType = classmethod(_guessStringType) |
|---|
| 370 |
|
|---|
| 371 |
def __init__(self, keyObject): |
|---|
| 372 |
""" |
|---|
| 373 |
Initialize a PublicKey with a C{Crypto.PublicKey.pubkey.pubkey} |
|---|
| 374 |
object. |
|---|
| 375 |
|
|---|
| 376 |
@type keyObject: C{Crypto.PublicKey.pubkey.pubkey} |
|---|
| 377 |
""" |
|---|
| 378 |
self.keyObject = keyObject |
|---|
| 379 |
|
|---|
| 380 |
def __eq__(self, other): |
|---|
| 381 |
""" |
|---|
| 382 |
Return True if other represents an object with the same key. |
|---|
| 383 |
""" |
|---|
| 384 |
if type(self) == type(other): |
|---|
| 385 |
return self.type() == other.type() and self.data() == other.data() |
|---|
| 386 |
else: |
|---|
| 387 |
return NotImplemented |
|---|
| 388 |
|
|---|
| 389 |
def __ne__(self, other): |
|---|
| 390 |
""" |
|---|
| 391 |
Return True if other represents anything other than this key. |
|---|
| 392 |
""" |
|---|
| 393 |
result = self.__eq__(other) |
|---|
| 394 |
if result == NotImplemented: |
|---|
| 395 |
return result |
|---|
| 396 |
return not result |
|---|
| 397 |
|
|---|
| 398 |
def __repr__(self): |
|---|
| 399 |
""" |
|---|
| 400 |
Return a pretty representation of this object. |
|---|
| 401 |
""" |
|---|
| 402 |
lines = ['<%s %s (%s bits)' % (self.type(), |
|---|
| 403 |
self.isPublic() and 'Public Key' or 'Private Key', |
|---|
| 404 |
self.keyObject.size())] |
|---|
| 405 |
for k, v in self.data().items(): |
|---|
| 406 |
lines.append('attr %s:' % k) |
|---|
| 407 |
by = common.MP(v)[4:] |
|---|
| 408 |
while by: |
|---|
| 409 |
m = by[:15] |
|---|
| 410 |
by = by[15:] |
|---|
| 411 |
o = '' |
|---|
| 412 |
for c in m: |
|---|
| 413 |
o = o + '%02x:' % ord(c) |
|---|
| 414 |
if len(m) < 15: |
|---|
| 415 |
o = o[:-1] |
|---|
| 416 |
lines.append('\t' + o) |
|---|
| 417 |
lines[-1] = lines[-1] + '>' |
|---|
| 418 |
return '\n'.join(lines) |
|---|
| 419 |
|
|---|
| 420 |
def isPublic(self): |
|---|
| 421 |
""" |
|---|
| 422 |
Returns True if this Key is a public key. |
|---|
| 423 |
""" |
|---|
| 424 |
return not self.keyObject.has_private() |
|---|
| 425 |
|
|---|
| 426 |
def public(self): |
|---|
| 427 |
""" |
|---|
| 428 |
Returns a version of this key containing only the public key data. |
|---|
| 429 |
If this is a public key, this may or may not be the same object |
|---|
| 430 |
as self. |
|---|
| 431 |
""" |
|---|
| 432 |
return Key(self.keyObject.publickey()) |
|---|
| 433 |
|
|---|
| 434 |
|
|---|
| 435 |
def fingerprint(self): |
|---|
| 436 |
""" |
|---|
| 437 |
Get the user presentation of the fingerprint of this L{Key}. As |
|---|
| 438 |
described by U{RFC 4716 section |
|---|
| 439 |
4<http://tools.ietf.org/html/rfc4716#section-4>}:: |
|---|
| 440 |
|
|---|
| 441 |
The fingerprint of a public key consists of the output of the MD5 |
|---|
| 442 |
message-digest algorithm [RFC1321]. The input to the algorithm is |
|---|
| 443 |
the public key data as specified by [RFC4253]. (...) The output |
|---|
| 444 |
of the (MD5) algorithm is presented to the user as a sequence of 16 |
|---|
| 445 |
octets printed as hexadecimal with lowercase letters and separated |
|---|
| 446 |
by colons. |
|---|
| 447 |
|
|---|
| 448 |
@since: 8.2 |
|---|
| 449 |
|
|---|
| 450 |
@return: the user presentation of this L{Key}'s fingerprint, as a |
|---|
| 451 |
string. |
|---|
| 452 |
|
|---|
| 453 |
@rtype: L{str} |
|---|
| 454 |
""" |
|---|
| 455 |
return ':'.join([x.encode('hex') for x in md5(self.blob()).digest()]) |
|---|
| 456 |
|
|---|
| 457 |
|
|---|
| 458 |
def type(self): |
|---|
| 459 |
""" |
|---|
| 460 |
Return the type of the object we wrap. Currently this can only be |
|---|
| 461 |
'RSA' or 'DSA'. |
|---|
| 462 |
""" |
|---|
| 463 |
|
|---|
| 464 |
klass = str(self.keyObject.__class__) |
|---|
| 465 |
if klass.startswith('Crypto.PublicKey'): |
|---|
| 466 |
type = klass.split('.')[2] |
|---|
| 467 |
else: |
|---|
| 468 |
raise RuntimeError('unknown type of object: %r' % self.keyObject) |
|---|
| 469 |
if type in ('RSA', 'DSA'): |
|---|
| 470 |
return type |
|---|
| 471 |
else: |
|---|
| 472 |
raise RuntimeError('unknown type of key: %s' % type) |
|---|
| 473 |
|
|---|
| 474 |
def sshType(self): |
|---|
| 475 |
""" |
|---|
| 476 |
Return the type of the object we wrap as defined in the ssh protocol. |
|---|
| 477 |
Currently this can only be 'ssh-rsa' or 'ssh-dss'. |
|---|
| 478 |
""" |
|---|
| 479 |
return {'RSA':'ssh-rsa', 'DSA':'ssh-dss'}[self.type()] |
|---|
| 480 |
|
|---|
| 481 |
def data(self): |
|---|
| 482 |
""" |
|---|
| 483 |
Return the values of the public key as a dictionary. |
|---|
| 484 |
|
|---|
| 485 |
@rtype: C{dict} |
|---|
| 486 |
""" |
|---|
| 487 |
keyData = {} |
|---|
| 488 |
for name in self.keyObject.keydata: |
|---|
| 489 |
value = getattr(self.keyObject, name, None) |
|---|
| 490 |
if value is not None: |
|---|
| 491 |
keyData[name] = value |
|---|
| 492 |
return keyData |
|---|
| 493 |
|
|---|
| 494 |
def blob(self): |
|---|
| 495 |
""" |
|---|
| 496 |
Return the public key blob for this key. The blob is the |
|---|
| 497 |
over-the-wire format for public keys: |
|---|
| 498 |
|
|---|
| 499 |
RSA keys:: |
|---|
| 500 |
string 'ssh-rsa' |
|---|
| 501 |
integer e |
|---|
| 502 |
integer n |
|---|
| 503 |
|
|---|
| 504 |
DSA keys:: |
|---|
| 505 |
string 'ssh-dss' |
|---|
| 506 |
integer p |
|---|
| 507 |
integer q |
|---|
| 508 |
integer g |
|---|
| 509 |
integer y |
|---|
| 510 |
|
|---|
| 511 |
@rtype: C{str} |
|---|
| 512 |
""" |
|---|
| 513 |
type = self.type() |
|---|
| 514 |
data = self.data() |
|---|
| 515 |
if type == 'RSA': |
|---|
| 516 |
return (common.NS('ssh-rsa') + common.MP(data['e']) + |
|---|
| 517 |
common.MP(data['n'])) |
|---|
| 518 |
elif type == 'DSA': |
|---|
| 519 |
return (common.NS('ssh-dss') + common.MP(data['p']) + |
|---|
| 520 |
common.MP(data['q']) + common.MP(data['g']) + |
|---|
| 521 |
common.MP(data['y'])) |
|---|
| 522 |
|
|---|
| 523 |
def privateBlob(self): |
|---|
| 524 |
""" |
|---|
| 525 |
Return the private key blob for this key. The blob is the |
|---|
| 526 |
over-the-wire format for private keys: |
|---|
| 527 |
|
|---|
| 528 |
RSA keys:: |
|---|
| 529 |
string 'ssh-rsa' |
|---|
| 530 |
integer n |
|---|
| 531 |
integer e |
|---|
| 532 |
integer d |
|---|
| 533 |
integer u |
|---|
| 534 |
integer p |
|---|
| 535 |
integer q |
|---|
| 536 |
|
|---|
| 537 |
DSA keys:: |
|---|
| 538 |
string 'ssh-dss' |
|---|
| 539 |
integer p |
|---|
| 540 |
integer q |
|---|
| 541 |
integer g |
|---|
| 542 |
integer y |
|---|
| 543 |
integer x |
|---|
| 544 |
""" |
|---|
| 545 |
type = self.type() |
|---|
| 546 |
data = self.data() |
|---|
| 547 |
if type == 'RSA': |
|---|
| 548 |
return (common.NS('ssh-rsa') + common.MP(data['n']) + |
|---|
| 549 |
common.MP(data['e']) + common.MP(data['d']) + |
|---|
| 550 |
common.MP(data['u']) + common.MP(data['p']) + |
|---|
| 551 |
common.MP(data['q'])) |
|---|
| 552 |
elif type == 'DSA': |
|---|
| 553 |
return (common.NS('ssh-dss') + common.MP(data['p']) + |
|---|
| 554 |
common.MP(data['q']) + common.MP(data['g']) + |
|---|
| 555 |
common.MP(data['y']) + common.MP(data['x'])) |
|---|
| 556 |
|
|---|
| 557 |
def toString(self, type, extra=None): |
|---|
| 558 |
""" |
|---|
| 559 |
Create a string representation of this key. If the key is a private |
|---|
| 560 |
key and you want the represenation of its public key, use |
|---|
| 561 |
C{key.public().toString()}. type maps to a _toString_* method. |
|---|
| 562 |
|
|---|
| 563 |
@param type: The type of string to emit. Currently supported values |
|---|
| 564 |
are C{'OPENSSH'}, C{'LSH'}, and C{'AGENTV3'}. |
|---|
| 565 |
@type type: L{str} |
|---|
| 566 |
|
|---|
| 567 |
@param extra: Any extra data supported by the selected format which |
|---|
| 568 |
is not part of the key itself. For public OpenSSH keys, this is |
|---|
| 569 |
a comment. For private OpenSSH keys, this is a passphrase to |
|---|
| 570 |
encrypt with. |
|---|
| 571 |
@type extra: L{str} or L{NoneType} |
|---|
| 572 |
|
|---|
| 573 |
@rtype: L{str} |
|---|
| 574 |
""" |
|---|
| 575 |
method = getattr(self, '_toString_%s' % type.upper(), None) |
|---|
| 576 |
if method is None: |
|---|
| 577 |
raise BadKeyError('unknown type: %s' % type) |
|---|
| 578 |
if method.func_code.co_argcount == 2: |
|---|
| 579 |
return method(extra) |
|---|
| 580 |
else: |
|---|
| 581 |
return method() |
|---|
| 582 |
|
|---|
| 583 |
def _toString_OPENSSH(self, extra): |
|---|
| 584 |
""" |
|---|
| 585 |
Return a public or private OpenSSH string. See |
|---|
| 586 |
_fromString_PUBLIC_OPENSSH and _fromString_PRIVATE_OPENSSH for the |
|---|
| 587 |
string formats. If extra is present, it represents a comment for a |
|---|
| 588 |
public key, or a passphrase for a private key. |
|---|
| 589 |
|
|---|
| 590 |
@type extra: C{str} |
|---|
| 591 |
@rtype: C{str} |
|---|
| 592 |
""" |
|---|
| 593 |
data = self.data() |
|---|
| 594 |
if self.isPublic(): |
|---|
| 595 |
b64Data = base64.encodestring(self.blob()).replace('\n', '') |
|---|
| 596 |
if not extra: |
|---|
| 597 |
extra = '' |
|---|
| 598 |
return ('%s %s %s' % (self.sshType(), b64Data, extra)).strip() |
|---|
| 599 |
else: |
|---|
| 600 |
lines = ['-----BEGIN %s PRIVATE KEY-----' % self.type()] |
|---|
| 601 |
if self.type() == 'RSA': |
|---|
| 602 |
p, q = data['p'], data['q'] |
|---|
| 603 |
objData = (0, data['n'], data['e'], data['d'], q, p, |
|---|
| 604 |
data['d'] % (q - 1), data['d'] % (p - 1), |
|---|
| 605 |
data['u']) |
|---|
| 606 |
else: |
|---|
| 607 |
objData = (0, data['p'], data['q'], data['g'], data['y'], |
|---|
| 608 |
data['x']) |
|---|
| 609 |
asn1Sequence = univ.Sequence() |
|---|
| 610 |
for index, value in itertools.izip(itertools.count(), objData): |
|---|
| 611 |
asn1Sequence.setComponentByPosition(index, univ.Integer(value)) |
|---|
| 612 |
asn1Data = berEncoder.encode(asn1Sequence) |
|---|
| 613 |
if extra: |
|---|
| 614 |
iv = randbytes.secureRandom(8) |
|---|
| 615 |
hexiv = ''.join(['%02X' % ord(x) for x in iv]) |
|---|
| 616 |
lines.append('Proc-Type: 4,ENCRYPTED') |
|---|
| 617 |
lines.append('DEK-Info: DES-EDE3-CBC,%s\n' % hexiv) |
|---|
| 618 |
ba = md5(extra + iv).digest() |
|---|
| 619 |
bb = md5(ba + extra + iv).digest() |
|---|
| 620 |
encKey = (ba + bb)[:24] |
|---|
| 621 |
padLen = 8 - (len(asn1Data) % 8) |
|---|
| 622 |
asn1Data += (chr(padLen) * padLen) |
|---|
| 623 |
asn1Data = DES3.new(encKey, DES3.MODE_CBC, |
|---|
| 624 |
iv).encrypt(asn1Data) |
|---|
| 625 |
b64Data = base64.encodestring(asn1Data).replace('\n', '') |
|---|
| 626 |
lines += [b64Data[i:i + 64] for i in range(0, len(b64Data), 64)] |
|---|
| 627 |
lines.append('-----END %s PRIVATE KEY-----' % self.type()) |
|---|
| 628 |
return '\n'.join(lines) |
|---|
| 629 |
|
|---|
| 630 |
def _toString_LSH(self): |
|---|
| 631 |
""" |
|---|
| 632 |
Return a public or private LSH key. See _fromString_PUBLIC_LSH and |
|---|
| 633 |
_fromString_PRIVATE_LSH for the key formats. |
|---|
| 634 |
|
|---|
| 635 |
@rtype: C{str} |
|---|
| 636 |
""" |
|---|
| 637 |
data = self.data() |
|---|
| 638 |
if self.isPublic(): |
|---|
| 639 |
if self.type() == 'RSA': |
|---|
| 640 |
keyData = sexpy.pack([['public-key', ['rsa-pkcs1-sha1', |
|---|
| 641 |
['n', common.MP(data['n'])[4:]], |
|---|
| 642 |
['e', common.MP(data['e'])[4:]]]]]) |
|---|
| 643 |
elif self.type() == 'DSA': |
|---|
| 644 |
keyData = sexpy.pack([['public-key', ['dsa', |
|---|
| 645 |
['p', common.MP(data['p'])[4:]], |
|---|
| 646 |
['q', common.MP(data['q'])[4:]], |
|---|
| 647 |
['g', common.MP(data['g'])[4:]], |
|---|
| 648 |
['y', common.MP(data['y'])[4:]]]]]) |
|---|
| 649 |
return '{' + base64.encodestring(keyData).replace('\n', '') + '}' |
|---|
| 650 |
else: |
|---|
| 651 |
if self.type() == 'RSA': |
|---|
| 652 |
p, q = data['p'], data['q'] |
|---|
| 653 |
return sexpy.pack([['private-key', ['rsa-pkcs1', |
|---|
| 654 |
['n', common.MP(data['n'])[4:]], |
|---|
| 655 |
['e', common.MP(data['e'])[4:]], |
|---|
| 656 |
['d', common.MP(data['d'])[4:]], |
|---|
| 657 |
['p', common.MP(q)[4:]], |
|---|
| 658 |
['q', common.MP(p)[4:]], |
|---|
| 659 |
['a', common.MP(data['d'] % (q - 1))[4:]], |
|---|
| 660 |
['b', common.MP(data['d'] % (p - 1))[4:]], |
|---|
| 661 |
['c', common.MP(data['u'])[4:]]]]]) |
|---|
| 662 |
elif self.type() == 'DSA': |
|---|
| 663 |
return sexpy.pack([['private-key', ['dsa', |
|---|
| 664 |
['p', common.MP(data['p'])[4:]], |
|---|
| 665 |
['q', common.MP(data['q'])[4:]], |
|---|
| 666 |
['g', common.MP(data['g'])[4:]], |
|---|
| 667 |
['y', common.MP(data['y'])[4:]], |
|---|
| 668 |
['x', common.MP(data['x'])[4:]]]]]) |
|---|
| 669 |
|
|---|
| 670 |
def _toString_AGENTV3(self): |
|---|
| 671 |
""" |
|---|
| 672 |
Return a private Secure Shell Agent v3 key. See |
|---|
| 673 |
_fromString_AGENTV3 for the key format. |
|---|
| 674 |
|
|---|
| 675 |
@rtype: C{str} |
|---|
| 676 |
""" |
|---|
| 677 |
data = self.data() |
|---|
| 678 |
if not self.isPublic(): |
|---|
| 679 |
if self.type() == 'RSA': |
|---|
| 680 |
values = (data['e'], data['d'], data['n'], data['u'], |
|---|
| 681 |
data['p'], data['q']) |
|---|
| 682 |
elif self.type() == 'DSA': |
|---|
| 683 |
values = (data['p'], data['q'], data['g'], data['y'], |
|---|
| 684 |
data['x']) |
|---|
| 685 |
return common.NS(self.sshType()) + ''.join(map(common.MP, values)) |
|---|
| 686 |
|
|---|
| 687 |
|
|---|
| 688 |
def sign(self, data): |
|---|
| 689 |
""" |
|---|
| 690 |
Returns a signature with this Key. |
|---|
| 691 |
|
|---|
| 692 |
@type data: C{str} |
|---|
| 693 |
@rtype: C{str} |
|---|
| 694 |
""" |
|---|
| 695 |
if self.type() == 'RSA': |
|---|
| 696 |
digest = pkcs1Digest(data, self.keyObject.size()/8) |
|---|
| 697 |
signature = self.keyObject.sign(digest, '')[0] |
|---|
| 698 |
ret = common.NS(Util.number.long_to_bytes(signature)) |
|---|
| 699 |
elif self.type() == 'DSA': |
|---|
| 700 |
digest = sha1(data).digest() |
|---|
| 701 |
randomBytes = randbytes.secureRandom(19) |
|---|
| 702 |
sig = self.keyObject.sign(digest, randomBytes) |
|---|
| 703 |
|
|---|
| 704 |
|
|---|
| 705 |
|
|---|
| 706 |
|
|---|
| 707 |
ret = common.NS(Util.number.long_to_bytes(sig[0], 20) + |
|---|
| 708 |
Util.number.long_to_bytes(sig[1], 20)) |
|---|
| 709 |
return common.NS(self.sshType()) + ret |
|---|
| 710 |
|
|---|
| 711 |
def verify(self, signature, data): |
|---|
| 712 |
""" |
|---|
| 713 |
Returns true if the signature for data is valid for this Key. |
|---|
| 714 |
|
|---|
| 715 |
@type signature: C{str} |
|---|
| 716 |
@type data: C{str} |
|---|
| 717 |
@rtype: C{bool} |
|---|
| 718 |
""" |
|---|
| 719 |
signatureType, signature = common.getNS(signature) |
|---|
| 720 |
if signatureType != self.sshType(): |
|---|
| 721 |
return False |
|---|
| 722 |
if self.type() == 'RSA': |
|---|
| 723 |
numbers = common.getMP(signature) |
|---|
| 724 |
digest = pkcs1Digest(data, self.keyObject.size() / 8) |
|---|
| 725 |
elif self.type() == 'DSA': |
|---|
| 726 |
signature = common.getNS(signature)[0] |
|---|
| 727 |
numbers = [Util.number.bytes_to_long(n) for n in signature[:20], |
|---|
| 728 |
signature[20:]] |
|---|
| 729 |
digest = sha1(data).digest() |
|---|
| 730 |
return self.keyObject.verify(digest, numbers) |
|---|
| 731 |
|
|---|
| 732 |
def getPublicKeyString(filename=None, line=0, data=''): |
|---|
| 733 |
""" |
|---|
| 734 |
Return a public key string suitable for being sent over the wire. |
|---|
| 735 |
Takes a filename or data of a public key. Currently handles OpenSSH |
|---|
| 736 |
and LSH keys. |
|---|
| 737 |
|
|---|
| 738 |
This function has been deprecated since Twisted Conch 0.9. Use |
|---|
| 739 |
Key.fromString() instead. |
|---|
| 740 |
|
|---|
| 741 |
@type filename: C{str} |
|---|
| 742 |
@type line: C{int} |
|---|
| 743 |
@type data: C{str} |
|---|
| 744 |
@rtype: C{str} |
|---|
| 745 |
""" |
|---|
| 746 |
warnings.warn("getPublicKeyString is deprecated since Twisted Conch 0.9." |
|---|
| 747 |
" Use Key.fromString().", |
|---|
| 748 |
DeprecationWarning, stacklevel=2) |
|---|
| 749 |
if filename and data: |
|---|
| 750 |
raise BadKeyError("either filename or data, not both") |
|---|
| 751 |
if filename: |
|---|
| 752 |
lines = open(filename).readlines() |
|---|
| 753 |
data = lines[line] |
|---|
| 754 |
return Key.fromString(data).blob() |
|---|
| 755 |
|
|---|
| 756 |
def makePublicKeyString(obj, comment='', kind='openssh'): |
|---|
| 757 |
""" |
|---|
| 758 |
Return an public key given a C{Crypto.PublicKey.pubkey.pubkey} |
|---|
| 759 |
object. |
|---|
| 760 |
kind is one of ('openssh', 'lsh') |
|---|
| 761 |
|
|---|
| 762 |
This function is deprecated since Twisted Conch 0.9. Instead use |
|---|
| 763 |
Key(obj).toString(). |
|---|
| 764 |
|
|---|
| 765 |
@type obj: C{Crypto.PublicKey.pubkey.pubkey} |
|---|
| 766 |
@type comment: C{str} |
|---|
| 767 |
@type kind: C{str} |
|---|
| 768 |
@rtype: C{str} |
|---|
| 769 |
""" |
|---|
| 770 |
warnings.warn("makePublicKeyString is deprecated since Twisted Conch 0.9." |
|---|
| 771 |
" Use Key(obj).toString().", |
|---|
| 772 |
DeprecationWarning, stacklevel=2) |
|---|
| 773 |
return Key(obj).public().toString(kind, comment) |
|---|
| 774 |
|
|---|
| 775 |
def getPublicKeyObject(data): |
|---|
| 776 |
""" |
|---|
| 777 |
Return a C{Crypto.PublicKey.pubkey.pubkey} corresponding to the SSHv2 |
|---|
| 778 |
public key data. data is in the over-the-wire public key format. |
|---|
| 779 |
|
|---|
| 780 |
This function is deprecated since Twisted Conch 0.9. Instead, use |
|---|
| 781 |
Key.fromString(). |
|---|
| 782 |
|
|---|
| 783 |
@type data: C{str} |
|---|
| 784 |
@rtype: C{Crypto.PublicKey.pubkey.pubkey} |
|---|
| 785 |
""" |
|---|
| 786 |
warnings.warn("getPublicKeyObject is deprecated since Twisted Conch 0.9." |
|---|
| 787 |
" Use Key.fromString().", |
|---|
| 788 |
DeprecationWarning, stacklevel=2) |
|---|
| 789 |
return Key.fromString(data).keyObject |
|---|
| 790 |
|
|---|
| 791 |
def getPrivateKeyObject(filename=None, data='', passphrase=''): |
|---|
| 792 |
""" |
|---|
| 793 |
Return a C{Crypto.PublicKey.pubkey.pubkey} object corresponding to the |
|---|
| 794 |
private key file/data. If the private key is encrypted, passphrase B{must} |
|---|
| 795 |
be specified, other wise a L{BadKeyError} will be raised. |
|---|
| 796 |
|
|---|
| 797 |
This method is deprecated since Twisted Conch 0.9. Instead, use |
|---|
| 798 |
the fromString or fromFile classmethods of Key. |
|---|
| 799 |
|
|---|
| 800 |
@type filename: C{str} |
|---|
| 801 |
@type data: C{str} |
|---|
| 802 |
@type passphrase: C{str} |
|---|
| 803 |
@rtype: C{Crypto.PublicKey.pubkey.pubkey} |
|---|
| 804 |
@raises BadKeyError: if the key is invalid or a passphrase is not specified |
|---|
| 805 |
""" |
|---|
| 806 |
warnings.warn("getPrivateKeyObject is deprecated since Twisted Conch 0.9." |
|---|
| 807 |
" Use Key.fromString().", |
|---|
| 808 |
DeprecationWarning, stacklevel=2) |
|---|
| 809 |
if filename and data: |
|---|
| 810 |
raise BadKeyError("either filename or data, not both") |
|---|
| 811 |
if filename: |
|---|
| 812 |
return Key.fromFile(filename, passphrase=passphrase).keyObject |
|---|
| 813 |
else: |
|---|
| 814 |
return Key.fromString(data, passphrase=passphrase).keyObject |
|---|
| 815 |
|
|---|
| 816 |
def makePrivateKeyString(obj, passphrase=None, kind='openssh'): |
|---|
| 817 |
""" |
|---|
| 818 |
Return an OpenSSH-style private key for a |
|---|
| 819 |
C{Crypto.PublicKey.pubkey.pubkey} object. If passphrase is given, encrypt |
|---|
| 820 |
the private key with it. |
|---|
| 821 |
kind is one of ('openssh', 'lsh', 'agentv3') |
|---|
| 822 |
|
|---|
| 823 |
This function is deprecated since Twisted Conch 0.9. Instead use |
|---|
| 824 |
Key(obj).toString(). |
|---|
| 825 |
|
|---|
| 826 |
@type obj: C{Crypto.PublicKey.pubkey.pubkey} |
|---|
| 827 |
@type passphrase: C{str}/C{None} |
|---|
| 828 |
@type kind: C{str} |
|---|
| 829 |
@rtype: C{str} |
|---|
| 830 |
""" |
|---|
| 831 |
warnings.warn("makePrivateKeyString is deprecated since Twisted Conch 0.9." |
|---|
| 832 |
" Use Key(obj).toString().", |
|---|
| 833 |
DeprecationWarning, stacklevel=2) |
|---|
| 834 |
return Key(obj).toString(kind, passphrase) |
|---|
| 835 |
|
|---|
| 836 |
def makePublicKeyBlob(obj): |
|---|
| 837 |
""" |
|---|
| 838 |
Make a public key blob from a C{Crypto.PublicKey.pubkey.pubkey}. |
|---|
| 839 |
|
|---|
| 840 |
This function is deprecated since Twisted Conch 0.9. Use |
|---|
| 841 |
Key().blob() instead. |
|---|
| 842 |
""" |
|---|
| 843 |
warnings.warn("makePublicKeyBlob is deprecated since Twisted Conch 0.9." |
|---|
| 844 |
" Use Key(obj).blob().", |
|---|
| 845 |
DeprecationWarning, stacklevel=2) |
|---|
| 846 |
return Key(obj).blob() |
|---|
| 847 |
|
|---|
| 848 |
def objectType(obj): |
|---|
| 849 |
""" |
|---|
| 850 |
Return the SSH key type corresponding to a C{Crypto.PublicKey.pubkey.pubkey} |
|---|
| 851 |
object. |
|---|
| 852 |
|
|---|
| 853 |
@type obj: C{Crypto.PublicKey.pubkey.pubkey} |
|---|
| 854 |
@rtype: C{str} |
|---|
| 855 |
""" |
|---|
| 856 |
keyDataMapping = { |
|---|
| 857 |
('n', 'e', 'd', 'p', 'q'): 'ssh-rsa', |
|---|
| 858 |
('n', 'e', 'd', 'p', 'q', 'u'): 'ssh-rsa', |
|---|
| 859 |
('y', 'g', 'p', 'q', 'x'): 'ssh-dss' |
|---|
| 860 |
} |
|---|
| 861 |
try: |
|---|
| 862 |
return keyDataMapping[tuple(obj.keydata)] |
|---|
| 863 |
except (KeyError, AttributeError): |
|---|
| 864 |
raise BadKeyError("invalid key object", obj) |
|---|
| 865 |
|
|---|
| 866 |
def pkcs1Pad(data, messageLength): |
|---|
| 867 |
""" |
|---|
| 868 |
Pad out data to messageLength according to the PKCS#1 standard. |
|---|
| 869 |
@type data: C{str} |
|---|
| 870 |
@type messageLength: C{int} |
|---|
| 871 |
""" |
|---|
| 872 |
lenPad = messageLength - 2 - len(data) |
|---|
| 873 |
return '\x01' + ('\xff' * lenPad) + '\x00' + data |
|---|
| 874 |
|
|---|
| 875 |
def pkcs1Digest(data, messageLength): |
|---|
| 876 |
""" |
|---|
| 877 |
Create a message digest using the SHA1 hash algorithm according to the |
|---|
| 878 |
PKCS#1 standard. |
|---|
| 879 |
@type data: C{str} |
|---|
| 880 |
@type messageLength: C{str} |
|---|
| 881 |
""" |
|---|
| 882 |
digest = sha1(data).digest() |
|---|
| 883 |
return pkcs1Pad(ID_SHA1+digest, messageLength) |
|---|
| 884 |
|
|---|
| 885 |
def lenSig(obj): |
|---|
| 886 |
""" |
|---|
| 887 |
Return the length of the signature in bytes for a key object. |
|---|
| 888 |
|
|---|
| 889 |
@type obj: C{Crypto.PublicKey.pubkey.pubkey} |
|---|
| 890 |
@rtype: C{long} |
|---|
| 891 |
""" |
|---|
| 892 |
return obj.size()/8 |
|---|
| 893 |
|
|---|
| 894 |
def signData(obj, data): |
|---|
| 895 |
""" |
|---|
| 896 |
Sign the data with the given C{Crypto.PublicKey.pubkey.pubkey} object. |
|---|
| 897 |
|
|---|
| 898 |
This method is deprecated since Twisted Conch 0.9. Instead use |
|---|
| 899 |
Key().sign(). |
|---|
| 900 |
|
|---|
| 901 |
@type obj: C{Crypto.PublicKey.pubkey.pubkey} |
|---|
| 902 |
@type data: C{str} |
|---|
| 903 |
@rtype: C{str} |
|---|
| 904 |
""" |
|---|
| 905 |
warnings.warn("signData is deprecated since Twisted Conch 0.9." |
|---|
| 906 |
" Use Key(obj).sign(data).", |
|---|
| 907 |
DeprecationWarning, stacklevel=2) |
|---|
| 908 |
return Key(obj).sign(data) |
|---|
| 909 |
|
|---|
| 910 |
def verifySignature(obj, sig, data): |
|---|
| 911 |
""" |
|---|
| 912 |
Verify that the signature for the data is valid. |
|---|
| 913 |
|
|---|
| 914 |
This method is deprecated since Twisted Conch 0.9. Use |
|---|
| 915 |
Key().verify(). |
|---|
| 916 |
|
|---|
| 917 |
@type obj: C{Crypto.PublicKey.pubkey.pubkey} |
|---|
| 918 |
@type sig: C{str} |
|---|
| 919 |
@type data: C{str} |
|---|
| 920 |
@rtype: C{bool} |
|---|
| 921 |
""" |
|---|
| 922 |
warnings.warn("verifySignature is deprecated since Twisted Conch 0.9." |
|---|
| 923 |
" Use Key(obj).verify(signature, data).", |
|---|
| 924 |
DeprecationWarning, stacklevel=2) |
|---|
| 925 |
return Key(obj).verify(sig, data) |
|---|
| 926 |
|
|---|
| 927 |
def printKey(obj): |
|---|
| 928 |
""" |
|---|
| 929 |
Pretty print a C{Crypto.PublicKey.pubkey.pubkey} object. |
|---|
| 930 |
|
|---|
| 931 |
This function is deprecated since Twisted Conch 0.9. Use |
|---|
| 932 |
repr(Key()). |
|---|
| 933 |
|
|---|
| 934 |
@type obj: C{Crypto.PublicKey.pubkey.pubkey} |
|---|
| 935 |
""" |
|---|
| 936 |
warnings.warn("printKey is deprecated since Twisted Conch 0.9." |
|---|
| 937 |
" Use repr(Key(obj)).", |
|---|
| 938 |
DeprecationWarning, stacklevel=2) |
|---|
| 939 |
return repr(Key(obj))[1:-1] |
|---|
| 940 |
|
|---|
| 941 |
ID_SHA1 = '\x30\x21\x30\x09\x06\x05\x2b\x0e\x03\x02\x1a\x05\x00\x04\x14' |
|---|