OLD | NEW |
| (Empty) |
1 # -*- test-case-name: twisted.test.test_sslverify -*- | |
2 # Copyright 2005 Divmod, Inc. See LICENSE file for details | |
3 | |
4 import hashlib | |
5 import itertools | |
6 from OpenSSL import SSL, crypto | |
7 | |
8 from twisted.python import reflect, util | |
9 from twisted.internet.defer import Deferred | |
10 from twisted.internet.error import VerifyError, CertificateError | |
11 | |
12 # Private - shared between all OpenSSLCertificateOptions, counts up to provide | |
13 # a unique session id for each context | |
14 _sessionCounter = itertools.count().next | |
15 | |
16 class _SSLApplicationData(object): | |
17 def __init__(self): | |
18 self.problems = [] | |
19 | |
20 class OpenSSLVerifyError(VerifyError): | |
21 | |
22 _errorCodes = {0: ('X509_V_OK', | |
23 'ok', | |
24 'the operation was successful. >'), | |
25 | |
26 2: ('X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT', | |
27 'unable to get issuer certificate', | |
28 "The issuer certificate could not be found. This " | |
29 "occurs if the issuer certificate of an untrusted " | |
30 "certificate cannot be found."), | |
31 | |
32 3: ('X509_V_ERR_UNABLE_TO_GET_CRL', | |
33 'unable to get certificate CRL', | |
34 "The CRL of a certificate could not be found. " | |
35 "Unused."), | |
36 | |
37 4: ('X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE', | |
38 "unable to decrypt certificate's signature", | |
39 "The certificate signature could not be decrypted. " | |
40 "This means that the actual signature value could not " | |
41 "be determined rather than it not matching the " | |
42 "expected value, this is only meaningful for RSA " | |
43 "keys."), | |
44 | |
45 5: ('X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE', | |
46 "unable to decrypt CRL's signature", | |
47 "The CRL signature could not be decrypted. This " | |
48 "means that the actual signature value could not be " | |
49 "determined rather than it not matching the expected " | |
50 "value. Unused."), | |
51 | |
52 6: ('X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY', | |
53 'unable to decode issuer', | |
54 "Public key the public key in the certificate " | |
55 "SubjectPublicKeyInfo could not be read."), | |
56 | |
57 7: ('X509_V_ERR_CERT_SIGNATURE_FAILURE', | |
58 'certificate signature failure', | |
59 'The signature of the certificate is invalid.'), | |
60 | |
61 8: ('X509_V_ERR_CRL_SIGNATURE_FAILURE', | |
62 'CRL signature failure', | |
63 'The signature of the certificate is invalid. Unused.'), | |
64 | |
65 9: ('X509_V_ERR_CERT_NOT_YET_VALID', | |
66 'certificate is not yet valid', | |
67 "The certificate is not yet valid. The notBefore " | |
68 "date is after the current time."), | |
69 | |
70 10: ('X509_V_ERR_CERT_HAS_EXPIRED', | |
71 'certificate has expired', | |
72 "The certificate has expired. The notAfter date " | |
73 "is before the current time."), | |
74 | |
75 11: ('X509_V_ERR_CRL_NOT_YET_VALID', | |
76 'CRL is not yet valid', | |
77 'The CRL is not yet valid. Unused.'), | |
78 | |
79 12: ('X509_V_ERR_CRL_HAS_EXPIRED', | |
80 'CRL has expired', | |
81 'The CRL has expired. Unused.'), | |
82 | |
83 13: ('X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD', | |
84 "format error in certificate's notBefore field", | |
85 "The certificate's notBefore field contains an " | |
86 "invalid time."), | |
87 | |
88 14: ('X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD', | |
89 "format error in certificate's notAfter field.", | |
90 "The certificate's notAfter field contains an " | |
91 "invalid time."), | |
92 | |
93 15: ('X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD', | |
94 "format error in CRL's lastUpdate field", | |
95 "The CRL lastUpdate field contains an invalid " | |
96 "time. Unused."), | |
97 | |
98 16: ('X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD', | |
99 "format error in CRL's nextUpdate field", | |
100 "The CRL nextUpdate field contains an invalid " | |
101 "time. Unused."), | |
102 | |
103 17: ('X509_V_ERR_OUT_OF_MEM', | |
104 'out of memory', | |
105 'An error occurred trying to allocate memory. ' | |
106 'This should never happen.'), | |
107 | |
108 18: ('X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT', | |
109 'self signed certificate', | |
110 'The passed certificate is self signed and the same ' | |
111 'certificate cannot be found in the list of trusted ' | |
112 'certificates.'), | |
113 | |
114 19: ('X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN', | |
115 'self signed certificate in certificate chain', | |
116 'The certificate chain could be built up using the ' | |
117 'untrusted certificates but the root could not be ' | |
118 'found locally.'), | |
119 | |
120 20: ('X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY', | |
121 'unable to get local issuer certificate', | |
122 'The issuer certificate of a locally looked up ' | |
123 'certificate could not be found. This normally ' | |
124 'means the list of trusted certificates is not ' | |
125 'complete.'), | |
126 | |
127 21: ('X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE', | |
128 'unable to verify the first certificate', | |
129 'No signatures could be verified because the chain ' | |
130 'contains only one certificate and it is not self ' | |
131 'signed.'), | |
132 | |
133 22: ('X509_V_ERR_CERT_CHAIN_TOO_LONG', | |
134 'certificate chain too long', | |
135 'The certificate chain length is greater than the ' | |
136 'supplied maximum depth. Unused.'), | |
137 | |
138 23: ('X509_V_ERR_CERT_REVOKED', | |
139 'certificate revoked', | |
140 'The certificate has been revoked. Unused.'), | |
141 | |
142 24: ('X509_V_ERR_INVALID_CA', | |
143 'invalid CA certificate', | |
144 'A CA certificate is invalid. Either it is not a CA ' | |
145 'or its extensions are not consistent with the ' | |
146 'supplied purpose.'), | |
147 | |
148 25: ('X509_V_ERR_PATH_LENGTH_EXCEEDED', | |
149 'path length constraint exceeded', | |
150 'The basicConstraints pathlength parameter has been ' | |
151 'exceeded.'), | |
152 | |
153 26: ('X509_V_ERR_INVALID_PURPOSE', | |
154 'unsupported certificate purpose', | |
155 'The supplied certificate cannot be used for the ' | |
156 'specified purpose.'), | |
157 | |
158 27: ('X509_V_ERR_CERT_UNTRUSTED', | |
159 'certificate not trusted', | |
160 'The root CA is not marked as trusted for the ' | |
161 'specified purpose.'), | |
162 | |
163 28: ('X509_V_ERR_CERT_REJECTED', | |
164 'certificate rejected', | |
165 'The root CA is marked to reject the specified ' | |
166 'purpose.'), | |
167 | |
168 29: ('X509_V_ERR_SUBJECT_ISSUER_MISMATCH', | |
169 'subject issuer mismatch', | |
170 'The current candidate issuer certificate was ' | |
171 'rejected because its subject name did not match ' | |
172 'the issuer name of the current certificate. Only ' | |
173 'displayed when the issuer_checks option is set.'), | |
174 | |
175 30: ('X509_V_ERR_AKID_SKID_MISMATCH', | |
176 'authority and subject key identifier mismatch', | |
177 'The current candidate issuer certificate was ' | |
178 'rejected because its subject key identifier was ' | |
179 'present and did not match the authority key ' | |
180 'identifier current certificate. Only displayed ' | |
181 'when the issuer_checks option is set.'), | |
182 | |
183 31: ('X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH', | |
184 'authority and issuer serial number mismatch', | |
185 'The current candidate issuer certificate was ' | |
186 'rejected because its issuer name and serial ' | |
187 'number was present and did not match the ' | |
188 'authority key identifier of the current ' | |
189 'certificate. Only displayed when the issuer_checks ' | |
190 'option is set.'), | |
191 | |
192 32: ('X509_V_ERR_KEYUSAGE_NO_CERTSIGN', | |
193 'key usage does not include certificate', | |
194 'Signing the current candidate issuer certificate was ' | |
195 'rejected because its keyUsage extension does not ' | |
196 'permit certificate signing.'), | |
197 | |
198 50: ('X509_V_ERR_APPLICATION_VERIFICATION', | |
199 'application verification failure', | |
200 'an application specific error. Unused.')} | |
201 | |
202 | |
203 def __init__(self, cert, errno, depth): | |
204 VerifyError.__init__(self, cert, errno, depth) | |
205 self.cert = cert | |
206 self.errno = errno | |
207 self.depth = depth | |
208 | |
209 def __repr__(self): | |
210 x = self._errorCodes.get(self.errno) | |
211 if x is not None: | |
212 name, short, long = x | |
213 return 'Peer Certificate Verification Failed: %s (error code: %d)' %
( | |
214 long, self.errno | |
215 ) | |
216 return "Peer Certificate Verification Failed for Unknown Reason" | |
217 | |
218 __str__ = __repr__ | |
219 | |
220 | |
221 _x509names = { | |
222 'CN': 'commonName', | |
223 'commonName': 'commonName', | |
224 | |
225 'O': 'organizationName', | |
226 'organizationName': 'organizationName', | |
227 | |
228 'OU': 'organizationalUnitName', | |
229 'organizationalUnitName': 'organizationalUnitName', | |
230 | |
231 'L': 'localityName', | |
232 'localityName': 'localityName', | |
233 | |
234 'ST': 'stateOrProvinceName', | |
235 'stateOrProvinceName': 'stateOrProvinceName', | |
236 | |
237 'C': 'countryName', | |
238 'countryName': 'countryName', | |
239 | |
240 'emailAddress': 'emailAddress'} | |
241 | |
242 | |
243 class DistinguishedName(dict): | |
244 """ | |
245 Identify and describe an entity. | |
246 | |
247 Distinguished names are used to provide a minimal amount of identifying | |
248 information about a certificate issuer or subject. They are commonly | |
249 created with one or more of the following fields:: | |
250 | |
251 commonName (CN) | |
252 organizationName (O) | |
253 organizationalUnitName (OU) | |
254 localityName (L) | |
255 stateOrProvinceName (ST) | |
256 countryName (C) | |
257 emailAddress | |
258 """ | |
259 __slots__ = () | |
260 | |
261 def __init__(self, **kw): | |
262 for k, v in kw.iteritems(): | |
263 setattr(self, k, v) | |
264 | |
265 | |
266 def _copyFrom(self, x509name): | |
267 d = {} | |
268 for name in _x509names: | |
269 value = getattr(x509name, name, None) | |
270 if value is not None: | |
271 setattr(self, name, value) | |
272 | |
273 | |
274 def _copyInto(self, x509name): | |
275 for k, v in self.iteritems(): | |
276 setattr(x509name, k, v) | |
277 | |
278 | |
279 def __repr__(self): | |
280 return '<DN %s>' % (dict.__repr__(self)[1:-1]) | |
281 | |
282 | |
283 def __getattr__(self, attr): | |
284 try: | |
285 return self[_x509names[attr]] | |
286 except KeyError: | |
287 raise AttributeError(attr) | |
288 | |
289 | |
290 def __setattr__(self, attr, value): | |
291 assert type(attr) is str | |
292 if not attr in _x509names: | |
293 raise AttributeError("%s is not a valid OpenSSL X509 name field" % (
attr,)) | |
294 realAttr = _x509names[attr] | |
295 value = value.encode('ascii') | |
296 assert type(value) is str | |
297 self[realAttr] = value | |
298 | |
299 | |
300 def inspect(self): | |
301 """ | |
302 Return a multi-line, human-readable representation of this DN. | |
303 """ | |
304 l = [] | |
305 lablen = 0 | |
306 def uniqueValues(mapping): | |
307 return dict.fromkeys(mapping.itervalues()).keys() | |
308 for k in uniqueValues(_x509names): | |
309 label = util.nameToLabel(k) | |
310 lablen = max(len(label), lablen) | |
311 v = getattr(self, k, None) | |
312 if v is not None: | |
313 l.append((label, v)) | |
314 lablen += 2 | |
315 for n, (label, attr) in enumerate(l): | |
316 l[n] = (label.rjust(lablen)+': '+ attr) | |
317 return '\n'.join(l) | |
318 | |
319 DN = DistinguishedName | |
320 | |
321 | |
322 class CertBase: | |
323 def __init__(self, original): | |
324 self.original = original | |
325 | |
326 def _copyName(self, suffix): | |
327 dn = DistinguishedName() | |
328 dn._copyFrom(getattr(self.original, 'get_'+suffix)()) | |
329 return dn | |
330 | |
331 def getSubject(self): | |
332 """ | |
333 Retrieve the subject of this certificate. | |
334 | |
335 @rtype: L{DistinguishedName} | |
336 @return: A copy of the subject of this certificate. | |
337 """ | |
338 return self._copyName('subject') | |
339 | |
340 | |
341 | |
342 def problemsFromTransport(tpt): | |
343 """ | |
344 Retrieve the SSL errors associated with the given transport. | |
345 | |
346 @type tpt: L{ISystemHandle} provider wrapper around an SSL connection. | |
347 @rtype: C{list} of L{OpenSSLVerifyError}. | |
348 """ | |
349 return tpt.getHandle().get_context().get_app_data().problems | |
350 | |
351 | |
352 def _handleattrhelper(Class, transport, methodName): | |
353 """ | |
354 (private) Helper for L{Certificate.peerFromTransport} and | |
355 L{Certificate.hostFromTransport} which checks for incompatible handle types | |
356 and null certificates and raises the appropriate exception or returns the | |
357 appropriate certificate object. | |
358 """ | |
359 method = getattr(transport.getHandle(), | |
360 "get_%s_certificate" % (methodName,), None) | |
361 if method is None: | |
362 raise CertificateError( | |
363 "non-TLS transport %r did not have %s certificate" % (transport, met
hodName)) | |
364 cert = method() | |
365 if cert is None: | |
366 raise CertificateError( | |
367 "TLS transport %r did not have %s certificate" % (transport, methodN
ame)) | |
368 return Class(cert) | |
369 | |
370 | |
371 class Certificate(CertBase): | |
372 """ | |
373 An x509 certificate. | |
374 """ | |
375 def __repr__(self): | |
376 return '<%s Subject=%s Issuer=%s>' % (self.__class__.__name__, | |
377 self.getSubject().commonName, | |
378 self.getIssuer().commonName) | |
379 | |
380 def __eq__(self, other): | |
381 if isinstance(other, Certificate): | |
382 return self.dump() == other.dump() | |
383 return False | |
384 | |
385 | |
386 def __ne__(self, other): | |
387 return not self.__eq__(other) | |
388 | |
389 | |
390 def load(Class, requestData, format=crypto.FILETYPE_ASN1, args=()): | |
391 """ | |
392 Load a certificate from an ASN.1- or PEM-format string. | |
393 | |
394 @rtype: C{Class} | |
395 """ | |
396 return Class(crypto.load_certificate(format, requestData), *args) | |
397 load = classmethod(load) | |
398 _load = load | |
399 | |
400 | |
401 def dumpPEM(self): | |
402 """ | |
403 Dump this certificate to a PEM-format data string. | |
404 | |
405 @rtype: C{str} | |
406 """ | |
407 return self.dump(crypto.FILETYPE_PEM) | |
408 | |
409 | |
410 def loadPEM(Class, data): | |
411 """ | |
412 Load a certificate from a PEM-format data string. | |
413 | |
414 @rtype: C{Class} | |
415 """ | |
416 return Class.load(data, crypto.FILETYPE_PEM) | |
417 loadPEM = classmethod(loadPEM) | |
418 | |
419 | |
420 def peerFromTransport(Class, transport): | |
421 """ | |
422 Get the certificate for the remote end of the given transport. | |
423 | |
424 @type: L{ISystemHandle} | |
425 @rtype: C{Class} | |
426 | |
427 @raise: L{CertificateError}, if the given transport does not have a peer | |
428 certificate. | |
429 """ | |
430 return _handleattrhelper(Class, transport, 'peer') | |
431 peerFromTransport = classmethod(peerFromTransport) | |
432 | |
433 | |
434 def hostFromTransport(Class, transport): | |
435 """ | |
436 Get the certificate for the local end of the given transport. | |
437 | |
438 @param transport: an L{ISystemHandle} provider; the transport we will | |
439 | |
440 @rtype: C{Class} | |
441 | |
442 @raise: L{CertificateError}, if the given transport does not have a host | |
443 certificate. | |
444 """ | |
445 return _handleattrhelper(Class, transport, 'host') | |
446 hostFromTransport = classmethod(hostFromTransport) | |
447 | |
448 | |
449 def getPublicKey(self): | |
450 """ | |
451 Get the public key for this certificate. | |
452 | |
453 @rtype: L{PublicKey} | |
454 """ | |
455 return PublicKey(self.original.get_pubkey()) | |
456 | |
457 | |
458 def dump(self, format=crypto.FILETYPE_ASN1): | |
459 return crypto.dump_certificate(format, self.original) | |
460 | |
461 | |
462 def serialNumber(self): | |
463 """ | |
464 Retrieve the serial number of this certificate. | |
465 | |
466 @rtype: C{int} | |
467 """ | |
468 return self.original.get_serial_number() | |
469 | |
470 | |
471 def digest(self, method='md5'): | |
472 """ | |
473 Return a digest hash of this certificate using the specified hash | |
474 algorithm. | |
475 | |
476 @param method: One of C{'md5'} or C{'sha'}. | |
477 @rtype: C{str} | |
478 """ | |
479 return self.original.digest(method) | |
480 | |
481 | |
482 def _inspect(self): | |
483 return '\n'.join(['Certificate For Subject:', | |
484 self.getSubject().inspect(), | |
485 '\nIssuer:', | |
486 self.getIssuer().inspect(), | |
487 '\nSerial Number: %d' % self.serialNumber(), | |
488 'Digest: %s' % self.digest()]) | |
489 | |
490 | |
491 def inspect(self): | |
492 """ | |
493 Return a multi-line, human-readable representation of this | |
494 Certificate, including information about the subject, issuer, and | |
495 public key. | |
496 """ | |
497 return '\n'.join((self._inspect(), self.getPublicKey().inspect())) | |
498 | |
499 | |
500 def getIssuer(self): | |
501 """ | |
502 Retrieve the issuer of this certificate. | |
503 | |
504 @rtype: L{DistinguishedName} | |
505 @return: A copy of the issuer of this certificate. | |
506 """ | |
507 return self._copyName('issuer') | |
508 | |
509 | |
510 def options(self, *authorities): | |
511 raise NotImplementedError('Possible, but doubtful we need this yet') | |
512 | |
513 | |
514 | |
515 class CertificateRequest(CertBase): | |
516 """ | |
517 An x509 certificate request. | |
518 | |
519 Certificate requests are given to certificate authorities to be signed and | |
520 returned resulting in an actual certificate. | |
521 """ | |
522 def load(Class, requestData, requestFormat=crypto.FILETYPE_ASN1): | |
523 req = crypto.load_certificate_request(requestFormat, requestData) | |
524 dn = DistinguishedName() | |
525 dn._copyFrom(req.get_subject()) | |
526 if not req.verify(req.get_pubkey()): | |
527 raise VerifyError("Can't verify that request for %r is self-signed."
% (dn,)) | |
528 return Class(req) | |
529 load = classmethod(load) | |
530 | |
531 | |
532 def dump(self, format=crypto.FILETYPE_ASN1): | |
533 return crypto.dump_certificate_request(format, self.original) | |
534 | |
535 | |
536 | |
537 class PrivateCertificate(Certificate): | |
538 """ | |
539 An x509 certificate and private key. | |
540 """ | |
541 def __repr__(self): | |
542 return Certificate.__repr__(self) + ' with ' + repr(self.privateKey) | |
543 | |
544 | |
545 def _setPrivateKey(self, privateKey): | |
546 if not privateKey.matches(self.getPublicKey()): | |
547 raise VerifyError( | |
548 "Sanity check failed: Your certificate was not properly signed."
) | |
549 self.privateKey = privateKey | |
550 return self | |
551 | |
552 | |
553 def newCertificate(self, newCertData, format=crypto.FILETYPE_ASN1): | |
554 """ | |
555 Create a new L{PrivateCertificate} from the given certificate data and | |
556 this instance's private key. | |
557 """ | |
558 return self.load(newCertData, self.privateKey, format) | |
559 | |
560 | |
561 def load(Class, data, privateKey, format=crypto.FILETYPE_ASN1): | |
562 return Class._load(data, format)._setPrivateKey(privateKey) | |
563 load = classmethod(load) | |
564 | |
565 | |
566 def inspect(self): | |
567 return '\n'.join([Certificate._inspect(self), | |
568 self.privateKey.inspect()]) | |
569 | |
570 | |
571 def dumpPEM(self): | |
572 """ | |
573 Dump both public and private parts of a private certificate to | |
574 PEM-format data. | |
575 """ | |
576 return self.dump(crypto.FILETYPE_PEM) + self.privateKey.dump(crypto.FILE
TYPE_PEM) | |
577 | |
578 | |
579 def loadPEM(Class, data): | |
580 """ | |
581 Load both private and public parts of a private certificate from a | |
582 chunk of PEM-format data. | |
583 """ | |
584 return Class.load(data, KeyPair.load(data, crypto.FILETYPE_PEM), | |
585 crypto.FILETYPE_PEM) | |
586 loadPEM = classmethod(loadPEM) | |
587 | |
588 | |
589 def fromCertificateAndKeyPair(Class, certificateInstance, privateKey): | |
590 privcert = Class(certificateInstance.original) | |
591 return privcert._setPrivateKey(privateKey) | |
592 fromCertificateAndKeyPair = classmethod(fromCertificateAndKeyPair) | |
593 | |
594 | |
595 def options(self, *authorities): | |
596 options = dict(privateKey=self.privateKey.original, | |
597 certificate=self.original) | |
598 if authorities: | |
599 options.update(dict(verify=True, | |
600 requireCertificate=True, | |
601 caCerts=[auth.original for auth in authorities])
) | |
602 return OpenSSLCertificateOptions(**options) | |
603 | |
604 | |
605 def certificateRequest(self, format=crypto.FILETYPE_ASN1, | |
606 digestAlgorithm='md5'): | |
607 return self.privateKey.certificateRequest( | |
608 self.getSubject(), | |
609 format, | |
610 digestAlgorithm) | |
611 | |
612 | |
613 def signCertificateRequest(self, | |
614 requestData, | |
615 verifyDNCallback, | |
616 serialNumber, | |
617 requestFormat=crypto.FILETYPE_ASN1, | |
618 certificateFormat=crypto.FILETYPE_ASN1): | |
619 issuer = self.getSubject() | |
620 return self.privateKey.signCertificateRequest( | |
621 issuer, | |
622 requestData, | |
623 verifyDNCallback, | |
624 serialNumber, | |
625 requestFormat, | |
626 certificateFormat) | |
627 | |
628 | |
629 def signRequestObject(self, certificateRequest, serialNumber, | |
630 secondsToExpiry=60 * 60 * 24 * 365, # One year | |
631 digestAlgorithm='md5'): | |
632 return self.privateKey.signRequestObject(self.getSubject(), | |
633 certificateRequest, | |
634 serialNumber, | |
635 secondsToExpiry, | |
636 digestAlgorithm) | |
637 | |
638 | |
639 class PublicKey: | |
640 def __init__(self, osslpkey): | |
641 self.original = osslpkey | |
642 req1 = crypto.X509Req() | |
643 req1.set_pubkey(osslpkey) | |
644 self._emptyReq = crypto.dump_certificate_request(crypto.FILETYPE_ASN1, r
eq1) | |
645 | |
646 | |
647 def matches(self, otherKey): | |
648 return self._emptyReq == otherKey._emptyReq | |
649 | |
650 | |
651 # XXX This could be a useful method, but sometimes it triggers a segfault, | |
652 # so we'll steer clear for now. | |
653 # def verifyCertificate(self, certificate): | |
654 # """ | |
655 # returns None, or raises a VerifyError exception if the certificate | |
656 # could not be verified. | |
657 # """ | |
658 # if not certificate.original.verify(self.original): | |
659 # raise VerifyError("We didn't sign that certificate.") | |
660 | |
661 def __repr__(self): | |
662 return '<%s %s>' % (self.__class__.__name__, self.keyHash()) | |
663 | |
664 | |
665 def keyHash(self): | |
666 """ | |
667 MD5 hex digest of signature on an empty certificate request with this | |
668 key. | |
669 """ | |
670 return hashlib.md5(self._emptyReq).hexdigest() | |
671 | |
672 | |
673 def inspect(self): | |
674 return 'Public Key with Hash: %s' % (self.keyHash(),) | |
675 | |
676 | |
677 | |
678 class KeyPair(PublicKey): | |
679 | |
680 def load(Class, data, format=crypto.FILETYPE_ASN1): | |
681 return Class(crypto.load_privatekey(format, data)) | |
682 load = classmethod(load) | |
683 | |
684 | |
685 def dump(self, format=crypto.FILETYPE_ASN1): | |
686 return crypto.dump_privatekey(format, self.original) | |
687 | |
688 | |
689 def __getstate__(self): | |
690 return self.dump() | |
691 | |
692 | |
693 def __setstate__(self, state): | |
694 self.__init__(crypto.load_privatekey(crypto.FILETYPE_ASN1, state)) | |
695 | |
696 | |
697 def inspect(self): | |
698 t = self.original.type() | |
699 if t == crypto.TYPE_RSA: | |
700 ts = 'RSA' | |
701 elif t == crypto.TYPE_DSA: | |
702 ts = 'DSA' | |
703 else: | |
704 ts = '(Unknown Type!)' | |
705 L = (self.original.bits(), ts, self.keyHash()) | |
706 return '%s-bit %s Key Pair with Hash: %s' % L | |
707 | |
708 | |
709 def generate(Class, kind=crypto.TYPE_RSA, size=1024): | |
710 pkey = crypto.PKey() | |
711 pkey.generate_key(kind, size) | |
712 return Class(pkey) | |
713 | |
714 | |
715 def newCertificate(self, newCertData, format=crypto.FILETYPE_ASN1): | |
716 return PrivateCertificate.load(newCertData, self, format) | |
717 generate = classmethod(generate) | |
718 | |
719 | |
720 def requestObject(self, distinguishedName, digestAlgorithm='md5'): | |
721 req = crypto.X509Req() | |
722 req.set_pubkey(self.original) | |
723 distinguishedName._copyInto(req.get_subject()) | |
724 req.sign(self.original, digestAlgorithm) | |
725 return CertificateRequest(req) | |
726 | |
727 | |
728 def certificateRequest(self, distinguishedName, | |
729 format=crypto.FILETYPE_ASN1, | |
730 digestAlgorithm='md5'): | |
731 """Create a certificate request signed with this key. | |
732 | |
733 @return: a string, formatted according to the 'format' argument. | |
734 """ | |
735 return self.requestObject(distinguishedName, digestAlgorithm).dump(forma
t) | |
736 | |
737 | |
738 def signCertificateRequest(self, | |
739 issuerDistinguishedName, | |
740 requestData, | |
741 verifyDNCallback, | |
742 serialNumber, | |
743 requestFormat=crypto.FILETYPE_ASN1, | |
744 certificateFormat=crypto.FILETYPE_ASN1, | |
745 secondsToExpiry=60 * 60 * 24 * 365, # One year | |
746 digestAlgorithm='md5'): | |
747 """ | |
748 Given a blob of certificate request data and a certificate authority's | |
749 DistinguishedName, return a blob of signed certificate data. | |
750 | |
751 If verifyDNCallback returns a Deferred, I will return a Deferred which | |
752 fires the data when that Deferred has completed. | |
753 """ | |
754 hlreq = CertificateRequest.load(requestData, requestFormat) | |
755 | |
756 dn = hlreq.getSubject() | |
757 vval = verifyDNCallback(dn) | |
758 | |
759 def verified(value): | |
760 if not value: | |
761 raise VerifyError("DN callback %r rejected request DN %r" % (ver
ifyDNCallback, dn)) | |
762 return self.signRequestObject(issuerDistinguishedName, hlreq, | |
763 serialNumber, secondsToExpiry, digestA
lgorithm).dump(certificateFormat) | |
764 | |
765 if isinstance(vval, Deferred): | |
766 return vval.addCallback(verified) | |
767 else: | |
768 return verified(vval) | |
769 | |
770 | |
771 def signRequestObject(self, | |
772 issuerDistinguishedName, | |
773 requestObject, | |
774 serialNumber, | |
775 secondsToExpiry=60 * 60 * 24 * 365, # One year | |
776 digestAlgorithm='md5'): | |
777 """ | |
778 Sign a CertificateRequest instance, returning a Certificate instance. | |
779 """ | |
780 req = requestObject.original | |
781 dn = requestObject.getSubject() | |
782 cert = crypto.X509() | |
783 issuerDistinguishedName._copyInto(cert.get_issuer()) | |
784 cert.set_subject(req.get_subject()) | |
785 cert.set_pubkey(req.get_pubkey()) | |
786 cert.gmtime_adj_notBefore(0) | |
787 cert.gmtime_adj_notAfter(secondsToExpiry) | |
788 cert.set_serial_number(serialNumber) | |
789 cert.sign(self.original, digestAlgorithm) | |
790 return Certificate(cert) | |
791 | |
792 | |
793 def selfSignedCert(self, serialNumber, **kw): | |
794 dn = DN(**kw) | |
795 return PrivateCertificate.fromCertificateAndKeyPair( | |
796 self.signRequestObject(dn, self.requestObject(dn), serialNumber), | |
797 self) | |
798 | |
799 | |
800 | |
801 class OpenSSLCertificateOptions(object): | |
802 """ | |
803 A factory for SSL context objects for both SSL servers and clients. | |
804 """ | |
805 | |
806 _context = None | |
807 # Older versions of PyOpenSSL didn't provide OP_ALL. Fudge it here, just in
case. | |
808 _OP_ALL = getattr(SSL, 'OP_ALL', 0x0000FFFF) | |
809 | |
810 method = SSL.TLSv1_METHOD | |
811 | |
812 def __init__(self, | |
813 privateKey=None, | |
814 certificate=None, | |
815 method=None, | |
816 verify=False, | |
817 caCerts=None, | |
818 verifyDepth=9, | |
819 requireCertificate=True, | |
820 verifyOnce=True, | |
821 enableSingleUseKeys=True, | |
822 enableSessions=True, | |
823 fixBrokenPeers=False): | |
824 """ | |
825 Create an OpenSSL context SSL connection context factory. | |
826 | |
827 @param privateKey: A PKey object holding the private key. | |
828 | |
829 @param certificate: An X509 object holding the certificate. | |
830 | |
831 @param method: The SSL protocol to use, one of SSLv23_METHOD, | |
832 SSLv2_METHOD, SSLv3_METHOD, TLSv1_METHOD. Defaults to TLSv1_METHOD. | |
833 | |
834 @param verify: If True, verify certificates received from the peer and | |
835 fail the handshake if verification fails. Otherwise, allow anonymous | |
836 sessions and sessions with certificates which fail validation. By | |
837 default this is False. | |
838 | |
839 @param caCerts: List of certificate authority certificates to | |
840 send to the client when requesting a certificate. Only used if verify | |
841 is True, and if verify is True, either this must be specified or | |
842 caCertsFile must be given. Since verify is False by default, | |
843 this is None by default. | |
844 | |
845 @param verifyDepth: Depth in certificate chain down to which to verify. | |
846 If unspecified, use the underlying default (9). | |
847 | |
848 @param requireCertificate: If True, do not allow anonymous sessions. | |
849 | |
850 @param verifyOnce: If True, do not re-verify the certificate | |
851 on session resumption. | |
852 | |
853 @param enableSingleUseKeys: If True, generate a new key whenever | |
854 ephemeral DH parameters are used to prevent small subgroup attacks. | |
855 | |
856 @param enableSessions: If True, set a session ID on each context. This | |
857 allows a shortened handshake to be used when a known client reconnects. | |
858 | |
859 @param fixBrokenPeers: If True, enable various non-spec protocol fixes | |
860 for broken SSL implementations. This should be entirely safe, | |
861 according to the OpenSSL documentation, but YMMV. This option is now | |
862 off by default, because it causes problems with connections between | |
863 peers using OpenSSL 0.9.8a. | |
864 """ | |
865 | |
866 assert (privateKey is None) == (certificate is None), "Specify neither o
r both of privateKey and certificate" | |
867 self.privateKey = privateKey | |
868 self.certificate = certificate | |
869 if method is not None: | |
870 self.method = method | |
871 | |
872 self.verify = verify | |
873 assert ((verify and caCerts) or | |
874 (not verify)), "Specify client CA certificate information if and
only if enabling certificate verification" | |
875 | |
876 self.caCerts = caCerts | |
877 self.verifyDepth = verifyDepth | |
878 self.requireCertificate = requireCertificate | |
879 self.verifyOnce = verifyOnce | |
880 self.enableSingleUseKeys = enableSingleUseKeys | |
881 self.enableSessions = enableSessions | |
882 self.fixBrokenPeers = fixBrokenPeers | |
883 | |
884 | |
885 def __getstate__(self): | |
886 d = self.__dict__.copy() | |
887 try: | |
888 del d['_context'] | |
889 except KeyError: | |
890 pass | |
891 return d | |
892 | |
893 | |
894 def __setstate__(self, state): | |
895 self.__dict__ = state | |
896 | |
897 | |
898 def getContext(self): | |
899 """Return a SSL.Context object. | |
900 """ | |
901 if self._context is None: | |
902 self._context = self._makeContext() | |
903 return self._context | |
904 | |
905 | |
906 def _makeContext(self): | |
907 ctx = SSL.Context(self.method) | |
908 ctx.set_app_data(_SSLApplicationData()) | |
909 | |
910 if self.certificate is not None and self.privateKey is not None: | |
911 ctx.use_certificate(self.certificate) | |
912 ctx.use_privatekey(self.privateKey) | |
913 # Sanity check | |
914 ctx.check_privatekey() | |
915 | |
916 verifyFlags = SSL.VERIFY_NONE | |
917 if self.verify: | |
918 verifyFlags = SSL.VERIFY_PEER | |
919 if self.requireCertificate: | |
920 verifyFlags |= SSL.VERIFY_FAIL_IF_NO_PEER_CERT | |
921 if self.verifyOnce: | |
922 verifyFlags |= SSL.VERIFY_CLIENT_ONCE | |
923 if self.caCerts: | |
924 store = ctx.get_cert_store() | |
925 for cert in self.caCerts: | |
926 store.add_cert(cert) | |
927 | |
928 def _trackVerificationProblems(conn,cert,errno,depth,preverify_ok): | |
929 # retcode is the answer OpenSSL's default verifier would have | |
930 # given, had we allowed it to run. | |
931 if not preverify_ok: | |
932 ctx.get_app_data().problems.append(OpenSSLVerifyError(cert, errn
o, depth)) | |
933 return preverify_ok | |
934 ctx.set_verify(verifyFlags, _trackVerificationProblems) | |
935 | |
936 if self.verifyDepth is not None: | |
937 ctx.set_verify_depth(self.verifyDepth) | |
938 | |
939 if self.enableSingleUseKeys: | |
940 ctx.set_options(SSL.OP_SINGLE_DH_USE) | |
941 | |
942 if self.fixBrokenPeers: | |
943 ctx.set_options(self._OP_ALL) | |
944 | |
945 if self.enableSessions: | |
946 sessionName = hashlib.md5("%s-%d" % (reflect.qual(self.__class__), _
sessionCounter())).hexdigest() | |
947 ctx.set_session_id(sessionName) | |
948 | |
949 return ctx | |
OLD | NEW |