Package pulp :: Package server :: Package auth :: Module certificate
[hide private]
[frames] | no frames]

Source Code for Module pulp.server.auth.certificate

  1  #!/usr/bin/python 
  2  # 
  3  # Copyright (c) 2010 Red Hat, Inc. 
  4  # 
  5  # This software is licensed to you under the GNU General Public License, 
  6  # version 2 (GPLv2). There is NO WARRANTY for this software, express or 
  7  # implied, including the implied warranties of MERCHANTABILITY or FITNESS 
  8  # FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2 
  9  # along with this software; if not, see 
 10  # http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. 
 11  # 
 12  # Red Hat trademarks are not licensed under GPLv2. No permission is 
 13  # granted to use or replicate Red Hat trademarks that are incorporated 
 14  # in this software or its documentation. 
 15  # 
 16   
 17  """ 
 18  Contains classes for working with x.509 certificates. 
 19  The backing implementation is M2Crypto.X509 which has insufficient 
 20  support for custom v3 extensions.  It is not intended to be a 
 21  replacement of full wrapper but instead and extension. 
 22  """ 
 23   
 24  import os 
 25  import re 
 26  from datetime import datetime as dt 
 27  from datetime import tzinfo, timedelta 
 28   
 29  from M2Crypto import X509 
30 31 32 -class Certificate(object):
33 """ 34 Represents and x.509 certificate. 35 @ivar x509: The M2Crypto.X509 backing object. 36 @type x509: L{X509} 37 @ivar __ext: A dictionary of extensions L{OID}:value 38 @type __ext: L{Extensions} 39 """ 40
41 - def __init__(self, content=None):
42 """ 43 @param content: The (optional) PEM encoded content. 44 @type content: str 45 """ 46 self.update(content)
47
48 - def update(self, content):
49 if content: 50 x509 = X509.load_cert_string(content) 51 else: 52 x509 = X509.X509() 53 self.__ext = Extensions(x509) 54 self.x509 = x509
55
56 - def serialNumber(self):
57 """ 58 Get the serial number 59 @return: The x.509 serial number 60 @rtype: str 61 """ 62 return self.x509.get_serial_number()
63
64 - def subject(self):
65 """ 66 Get the certificate subject. 67 note: Missing NID mapping for UID added to patch openssl. 68 @return: A dictionary of subject fields. 69 @rtype: dict 70 """ 71 d = {} 72 subject = self.x509.get_subject() 73 subject.nid['UID'] = 458 74 for key, nid in subject.nid.items(): 75 entry = subject.get_entries_by_nid(nid) 76 if len(entry): 77 asn1 = entry[0].get_data() 78 d[key] = str(asn1) 79 continue 80 return d
81
82 - def validRange(self):
83 """ 84 Get the I{valid} date range. 85 @return: The valid date range. 86 @rtype: L{DateRange} 87 """ 88 return DateRange(self.x509)
89
90 - def valid(self):
91 """ 92 Get whether the certificate is valid based on date. 93 @return: True if valid. 94 @rtype: boolean 95 """ 96 return self.validRange().hasNow()
97
98 - def bogus(self):
99 """ 100 Get whether the certificate contains bogus 101 data or is otherwise unsuitable. The certificate 102 may be valid but still be considered bogus. 103 @return: List of reasons if bogus 104 @rtype: list 105 """ 106 return []
107
108 - def extensions(self):
109 """ 110 Get custom extensions. 111 @return: An extensions object. 112 @rtype: L{Extensions} 113 """ 114 return self.__ext
115
116 - def read(self, path):
117 """ 118 Read a certificate file. 119 @param path: The path to a .pem file. 120 @type path: str 121 @return: A certificate 122 @rtype: L{Certificate} 123 """ 124 f = open(path) 125 content = f.read() 126 try: 127 self.update(content) 128 finally: 129 f.close() 130 self.path = path
131
132 - def write(self, path):
133 """ 134 Write the certificate. 135 @param path: The path to the .pem file. 136 @type path: str 137 @return: self 138 """ 139 f = open(path, 'w') 140 f.write(self.toPEM()) 141 self.path = path 142 f.close() 143 return self
144
145 - def delete(self):
146 """ 147 Delete the file associated with this certificate. 148 """ 149 if hasattr(self, 'path'): 150 os.unlink(self.path) 151 else: 152 raise Exception, 'no path, not deleted'
153
154 - def toPEM(self):
155 """ 156 Get PEM representation of the certificate. 157 @return: A PEM string 158 @rtype: str 159 """ 160 return self.x509.as_pem()
161
162 - def __str__(self):
163 return self.x509.as_text()
164
165 - def __repr__(self):
166 sn = self.serialNumber() 167 path = None 168 if hasattr(self, 'path'): 169 path = self.path 170 return '[sn: %d, path: "%s"]' % (sn, path)
171
172 - def __cmp__(self, other):
173 range = self.validRange() 174 exp1 = range.end() 175 range = other.validRange() 176 exp2 = range.end() 177 if exp1 < exp2: 178 return -1 179 if exp1 > exp2: 180 return 1 181 return 0
182
183 184 -class Key(object):
185 """ 186 The (private|public) key. 187 @ivar content: The PEM encoded key. 188 @type content: str 189 """ 190 191 @classmethod
192 - def read(cls, path):
193 """ 194 Read the key. 195 @param path: The path to the .pem file. 196 @type path: str 197 """ 198 f = open(path) 199 content = f.read() 200 f.close() 201 key = Key(content) 202 key.path = path 203 return key
204
205 - def __init__(self, content):
206 """ 207 @param content: The PEM encoded key. 208 @type content: str 209 """ 210 self.content = content
211
212 - def write(self, path):
213 """ 214 Write the key. 215 @param path: The path to the .pem file. 216 @type path: str 217 @return: self 218 """ 219 f = open(path, 'w') 220 f.write(self.content) 221 self.path = path 222 f.close() 223 return self
224
225 - def delete(self):
226 """ 227 Delete the file associated with this key. 228 """ 229 if hasattr(self, 'path'): 230 os.unlink(self.path) 231 else: 232 raise Exception, 'no path, not deleted'
233
234 - def __str__(self):
235 return self.content
236
237 238 -class DateRange:
239 """ 240 Date range object converts between ASN1 and python 241 datetime objects. 242 @ivar x509: A certificate. 243 @type x509: X509 244 """ 245 246 ASN1_FORMAT = '%b %d %H:%M:%S %Y %Z' 247
248 - def __init__(self, x509):
249 """ 250 @param x509: A certificate. 251 @type x509: X509 252 """ 253 self.range = \ 254 (str(x509.get_not_before()), 255 str(x509.get_not_after()))
256
257 - def begin(self):
258 """ 259 Get range beginning. 260 @return: The beginning date in UTC. 261 @rtype: L{datetime.datetime} 262 """ 263 return self.__parse(self.range[0])
264
265 - def end(self):
266 """ 267 Get range end. 268 @return: The end date in UTC. 269 @rtype: L{datetime.datetime} 270 """ 271 return self.__parse(self.range[1])
272
273 - def hasNow(self):
274 """ 275 Get whether the certificate is valid based on date. 276 @return: True if valid. 277 @rtype: boolean 278 """ 279 gmt = dt.utcnow() 280 gmt = gmt.replace(tzinfo=GMT()) 281 return ( gmt >= self.begin() and gmt <= self.end() )
282
283 - def __parse(self, asn1):
284 try: 285 d = dt.strptime(asn1, self.ASN1_FORMAT) 286 except: 287 d = dt(year=2000, month=1, day=1) 288 return d.replace(tzinfo=GMT())
289
290 - def __str__(self):
291 return '\n\t%s\n\t%s' % self.range
292
293 294 -class GMT(tzinfo):
295 """GMT""" 296
297 - def utcoffset(self, dt):
298 return timedelta(seconds=0)
299
300 - def tzname(self, dt):
301 return 'GMT'
302
303 - def dst(self, dt):
304 return 0
305
306 307 -class Extensions(dict):
308 """ 309 Represents x.509 (v3) I{custom} extensions. 310 """ 311 312 pattern = re.compile('([0-9]+\.)+[0-9]+:') 313
314 - def __init__(self, x509):
315 """ 316 @param x509: A certificate object. 317 @type x509: L{X509} 318 """ 319 if isinstance(x509, dict): 320 self.update(x509) 321 else: 322 self.__parse(x509)
323
324 - def ltrim(self, n):
325 """ 326 Left trim I{n} parts. 327 @param n: The number of parts to trim. 328 @type n: int 329 @return: The trimmed OID 330 @rtype: L{Extensions} 331 """ 332 d = {} 333 for oid,v in self.items(): 334 d[oid.ltrim(n)] = v 335 return Extensions(d)
336
337 - def get(self, oid, default=None):
338 """ 339 Get the value of an extension by I{oid}. 340 Note: The I{oid} may contain (*) wildcards. 341 @param oid: An OID that may contain (*) wildcards. 342 @type oid: str|L{OID} 343 @return: The value of the first extension matched. 344 @rtype: str 345 """ 346 ext = self.find(oid, 1) 347 if ext: 348 return ext[0][1] 349 else: 350 return default
351
352 - def find(self, oid, limit=0):
353 """ 354 Find all extensions matching the I{oid}. 355 Note: The I{oid} may contain (*) wildcards. 356 @param oid: An OID that may contain (*) wildcards. 357 @type oid: str|L{OID} 358 @param limit: Limit the number returned, 0=unlimited 359 @type limit: int 360 @return: A list of matching items. 361 @rtype: (OID, value) 362 @see: OID.match() 363 """ 364 ext = [] 365 if isinstance(oid, str): 366 oid = OID(oid) 367 keyset = sorted(self.keys()) 368 for k in keyset: 369 v = self[k] 370 if k.match(oid): 371 ext.append((k, v)) 372 if limit and len(ext) == limit: 373 break 374 return ext
375
376 - def branch(self, root):
377 """ 378 Find a subtree by matching the oid. 379 @param root: An OID that may contain (*) wildcards. 380 @type root: str|L{OID} 381 @return: A subtree. 382 @rtype: L{Extensions} 383 """ 384 d = {} 385 if isinstance(root, str): 386 root = OID(root) 387 if root[-1]: 388 root = root.append('') 389 ln = len(root)-1 390 for oid,v in self.find(root): 391 trimmed = oid.ltrim(ln) 392 d[trimmed] = v 393 return Extensions(d)
394
395 - def __ext(self, x509):
396 # get extensions substring 397 text = x509.as_text() 398 start = text.find('extensions:') 399 end = text.rfind('Signature Algorithm:') 400 text = text[start:end] 401 text = text.replace('.\n', '..') 402 return [s.strip() for s in text.split('\n')]
403
404 - def __parse(self, x509):
405 # parse the extensions section 406 oid = None 407 for entry in self.__ext(x509): 408 if oid is not None: 409 self[oid] = entry[2:] 410 oid = None 411 continue 412 m = self.pattern.match(entry) 413 if m is None: 414 continue 415 oid = OID(entry[:-1])
416
417 - def __str__(self):
418 s = [] 419 for item in self.items(): 420 s.append('%s = "%s"' % item) 421 return '\n'.join(s)
422
423 424 -class OID(object):
425 """ 426 The Object Identifier object. 427 @ivar part: The oid parts. 428 @type part: [str,] 429 @cvar WILDCARD: The wildcard character. 430 @type WILDCARD: str 431 """ 432 433 WILDCARD = '*' 434 435 @classmethod
436 - def join(cls, *oid):
437 return '.'.join(oid)
438 439 @classmethod
440 - def split(cls, s):
441 """ 442 Split an OID string. 443 @param s: An OID string Eg: (1.2.3) 444 @type s: str 445 @return: A list of OID parts. 446 @rtype: [str,] 447 """ 448 return s.split('.')
449
450 - def __init__(self, oid):
451 """ 452 @param oid: The OID value. 453 @type oid: str|[str,] 454 """ 455 if isinstance(oid, str): 456 self.part = self.split(oid) 457 else: 458 self.part = oid
459
460 - def parent(self):
461 """ 462 Get the parent OID. 463 @return: The parent OID. 464 @rtype: L{OID} 465 """ 466 p = self.part[:-1] 467 if p: 468 return OID(p)
469
470 - def ltrim(self, n):
471 """ 472 Left trim I{n} parts. 473 @param n: The number of parts to trim. 474 @type n: int 475 @return: The trimmed OID 476 @rtype: L{OID} 477 """ 478 return OID(self.part[n:])
479
480 - def rtrim(self, n):
481 """ 482 Right trim I{n} parts. 483 @param n: The number of parts to trim. 484 @type n: int 485 @return: The trimmed OID 486 @rtype: L{OID} 487 """ 488 return OID(self.part[:-n])
489
490 - def append(self, oid):
491 """ 492 Append the specified OID fragment. 493 @param oid: An OID fragment. 494 @type oid: str|L{OID} 495 @return: The concatenated OID. 496 @rtype: L{OID} 497 """ 498 if isinstance(oid, str): 499 oid = OID(oid) 500 part = self.part + oid.part 501 return OID(part)
502
503 - def match(self, oid):
504 """ 505 Match the specified OID considering wildcards. 506 Patterns: 507 - 1.4.5.6.74 (not wildcarded) 508 - .5.6.74 (match on only last 4) 509 - 5.6.74. (match only first 4) 510 - 1.4.*.6.* (wildcard pattern) 511 @param oid: An OID string or object. 512 @type oid: str|L{OID} 513 @return: True if matched 514 """ 515 i = 0 516 if isinstance(oid, str): 517 oid = OID(oid) 518 try: 519 if not oid[0]: 520 oid = OID(oid[1:]) 521 parts = self.part[-len(oid):] 522 elif not oid[-1]: 523 oid = OID(oid[:-1]) 524 parts = self.part[:len(oid)] 525 else: 526 parts = self.part 527 if len(parts) != len(oid): 528 raise Exception() 529 for x in parts: 530 if ( x == oid[i] or oid[i] == self.WILDCARD ): 531 i += 1 532 else: 533 raise Exception() 534 except: 535 return False 536 return True
537
538 - def __len__(self):
539 return len(self.part)
540
541 - def __getitem__(self, index):
542 return self.part[index]
543
544 - def __repr__(self):
545 return str(self)
546
547 - def __hash__(self):
548 return hash(str(self))
549
550 - def __eq__(self, other):
551 return ( str(self) == str(other) )
552
553 - def __str__(self):
554 return '.'.join(self.part)
555
556 557 -class RedhatCertificate(Certificate):
558 """ 559 Represents a Red Hat certificate. 560 @cvar REDHAT: The Red Hat base OID. 561 @type REDHAT: str 562 """ 563 564 REDHAT = '1.3.6.1.4.1.2312.9' 565
566 - def update(self, content):
567 Certificate.update(self, content) 568 redhat = OID(self.REDHAT) 569 n = len(redhat) 570 self.__redhat = self.extensions().ltrim(n)
571
572 - def redhat(self):
573 """ 574 Get the extension subtree for the B{redhat} namespace. 575 @return: The extensions with the RH namespace trimmed. 576 @rtype: L{Extension} 577 """ 578 try: 579 return self.__redhat 580 except: 581 return self.extensions()
582
583 - def bogus(self):
584 bogus = Certificate.bogus(self) 585 if self.serialNumber() < 1: 586 bogus.append('Serial Number must be > 0') 587 cn = self.subject().get('CN') 588 if not cn: 589 bogus.append('Common Name (%s) not-valid' % cn) 590 return bogus
591
592 593 -class ProductCertificate(RedhatCertificate):
594 """ 595 Represents a Red Hat product/entitlement certificate. 596 It is OID schema aware and provides methods to 597 get product information. 598 """ 599
600 - def getProduct(self):
601 """ 602 Get the product defined in the certificate. 603 @return: A product object. 604 @rtype: L{Product} 605 """ 606 rhns = self.redhat() 607 products = rhns.find('1.*.1', 1) 608 if products: 609 p = products[0] 610 oid = p[0] 611 root = oid.rtrim(1) 612 hash = oid[1] 613 ext = rhns.branch(root) 614 return Product(hash, ext)
615
616 - def getProducts(self):
617 """ 618 Get a list products defined in the certificate. 619 @return: A list of product objects. 620 @rtype: [L{Product},..] 621 """ 622 lst = [] 623 rhns = self.redhat() 624 for p in rhns.find('1.*.1'): 625 oid = p[0] 626 root = oid.rtrim(1) 627 hash = oid[1] 628 ext = rhns.branch(root) 629 lst.append(Product(hash, ext)) 630 return lst
631
632 - def bogus(self):
633 bogus = RedhatCertificate.bogus(self) 634 if self.getProduct() is None: 635 bogus.append('No product information') 636 return bogus
637
638 - def __str__(self):
639 s = [] 640 s.append('RAW:') 641 s.append('===================================') 642 s.append(Certificate.__str__(self)) 643 s.append('MODEL:') 644 s.append('===================================') 645 s.append('Serial#: %s' % self.serialNumber()) 646 s.append('Subject (CN): %s' % self.subject().get('CN')) 647 s.append('Valid: [%s] %s\n' % (self.valid(), self.validRange())) 648 for p in self.getProducts(): 649 s.append(str(p)) 650 return '\n'.join(s)
651
652 653 -class EntitlementCertificate(ProductCertificate):
654 """ 655 Represents an entitlement certificate. 656 """ 657
658 - def getOrder(self):
659 """ 660 Get the B{order} object defined in the certificate. 661 @return: An order object. 662 @rtype: L{Order} 663 """ 664 rhns = self.redhat() 665 order = rhns.find('4.1', 1) 666 if order: 667 p = order[0] 668 oid = p[0] 669 root = oid.rtrim(1) 670 ext = rhns.branch(root) 671 return Order(ext)
672
673 - def getEntitlements(self):
674 """ 675 Get the B{all} entitlements defined in the certificate. 676 @return: A list of entitlement object. 677 @rtype: [L{Entitlement},..] 678 """ 679 return self.getContentEntitlements() \ 680 + self.getRoleEntitlements()
681
682 - def getContentEntitlements(self):
683 """ 684 Get the B{content} entitlements defined in the certificate. 685 @return: A list of entitlement object. 686 @rtype: [L{Content},..] 687 """ 688 lst = [] 689 rhns = self.redhat() 690 entitlements = rhns.find('2.*.1.1') 691 for ent in entitlements: 692 oid = ent[0] 693 root = oid.rtrim(1) 694 ext = rhns.branch(root) 695 lst.append(Content(ext)) 696 return lst
697
698 - def getRoleEntitlements(self):
699 """ 700 Get the B{role} entitlements defined in the certificate. 701 @return: A list of entitlement object. 702 @rtype: [L{Role},..] 703 """ 704 lst = [] 705 rhns = self.redhat() 706 entitlements = rhns.find('3.*.1') 707 for ent in entitlements: 708 oid = ent[0] 709 root = oid.rtrim(1) 710 ext = rhns.branch(root) 711 lst.append(Role(ext)) 712 return lst
713
714 - def bogus(self):
715 bogus = ProductCertificate.bogus(self) 716 if self.getOrder() is None: 717 bogus.append('No order infomation') 718 return bogus
719
720 - def __str__(self):
721 s = [] 722 order = self.getOrder() 723 s.append(ProductCertificate.__str__(self)) 724 for ent in self.getEntitlements(): 725 s.append(str(ent)) 726 s.append(str(order)) 727 return '\n'.join(s)
728
729 730 -class Order:
731
732 - def __init__(self, ext):
733 self.ext = ext
734
735 - def getName(self):
736 return self.ext.get('1')
737
738 - def getNumber(self):
739 return self.ext.get('2')
740
741 - def getSku(self):
742 return self.ext.get('3')
743
744 - def getSubscription(self):
745 return self.ext.get('4')
746
747 - def getQuantity(self):
748 return self.ext.get('5')
749
750 - def getStart(self):
751 return self.ext.get('6')
752
753 - def getEnd(self):
754 return self.ext.get('7')
755
756 - def getSubtype(self):
757 return self.ext.get('8')
758
759 - def getVirtLimit(self):
760 return self.ext.get('9')
761
762 - def getSocketLimit(self):
763 return self.ext.get('10')
764
765 - def getOptionCode(self):
766 return self.ext.get('11')
767
768 - def getContract(self):
769 return self.ext.get('12')
770
771 - def __str__(self):
772 s = [] 773 s.append('Order {') 774 s.append('\tName .......... = %s' % self.getName()) 775 s.append('\tNumber ........ = %s' % self.getNumber()) 776 s.append('\tSKU ........... = %s' % self.getSku()) 777 s.append('\tSubscription .. = %s' % self.getSubscription()) 778 s.append('\tQuantity ...... = %s' % self.getQuantity()) 779 s.append('\tStart (Ent) ... = %s' % self.getStart()) 780 s.append('\tEnd (Ent) ..... = %s' % self.getEnd()) 781 s.append('\tSubtype ....... = %s' % self.getSubtype()) 782 s.append('\tVirt Limit .... = %s' % self.getVirtLimit()) 783 s.append('\tSocket Limit .. = %s' % self.getSocketLimit()) 784 s.append('\tOption Code ... = %s' % self.getOptionCode()) 785 s.append('\tContract ...... = %s' % self.getContract()) 786 s.append('}') 787 return '\n'.join(s)
788
789 790 -class Product:
791
792 - def __init__(self, hash, ext):
793 self.hash = hash 794 self.ext = ext
795
796 - def getHash(self):
797 return self.hash
798
799 - def getName(self):
800 return self.ext.get('1')
801
802 - def getVariant(self):
803 return self.ext.get('2')
804
805 - def getArch(self):
806 return self.ext.get('3')
807
808 - def getVersion(self):
809 return self.ext.get('4')
810
811 - def __eq__(self, rhs):
812 return ( self.getHash() == rhs.getHash() )
813
814 - def __str__(self):
815 s = [] 816 s.append('Product {') 817 s.append('\tHash ......... = %s' % self.getHash()) 818 s.append('\tName ......... = %s' % self.getName()) 819 s.append('\tVariant ...... = %s' % self.getVariant()) 820 s.append('\tArchitecture . = %s' % self.getArch()) 821 s.append('\tVersion ...... = %s' % self.getVersion()) 822 s.append('}') 823 return '\n'.join(s)
824
825 - def __repr__(self):
826 return str(self)
827
828 829 -class Entitlement:
830
831 - def __init__(self, ext):
832 self.ext = ext
833
834 835 -class Content(Entitlement):
836
837 - def getName(self):
838 return self.ext.get('1')
839
840 - def getLabel(self):
841 return self.ext.get('2')
842
843 - def getQuantity(self):
844 return self.ext.get('3')
845
846 - def getFlexQuantity(self):
847 return self.ext.get('4')
848
849 - def getVendor(self):
850 return self.ext.get('5')
851
852 - def getUrl(self):
853 return self.ext.get('6')
854
855 - def getGpg(self):
856 return self.ext.get('7')
857
858 - def getEnabled(self):
859 return self.ext.get('8')
860
861 - def __eq__(self, rhs):
862 return ( self.getLabel() == rhs.getLabel() )
863
864 - def __str__(self):
865 s = [] 866 s.append('Entitlement (content) {') 867 s.append('\tName ........ = %s' % self.getName()) 868 s.append('\tLabel ....... = %s' % self.getLabel()) 869 s.append('\tQuantity .... = %s' % self.getQuantity()) 870 s.append('\tFlex Quantity = %s' % self.getFlexQuantity()) 871 s.append('\tVendor ...... = %s' % self.getVendor()) 872 s.append('\tURL ......... = %s' % self.getUrl()) 873 s.append('\tGPG Key ..... = %s' % self.getGpg()) 874 s.append('\tEnabled ..... = %s' % self.getEnabled()) 875 s.append('}') 876 return '\n'.join(s)
877
878 - def __repr__(self):
879 return str(self)
880
881 882 -class Role(Entitlement):
883
884 - def getName(self):
885 return self.ext.get('1')
886
887 - def getDescription(self):
888 return self.ext.get('2')
889
890 - def __eq__(self, rhs):
891 return ( self.getName() == rhs.getName() )
892
893 - def __str__(self):
894 s = [] 895 s.append('Entitlement (role) {') 896 s.append('\tName ......... = %s' % self.getName()) 897 s.append('\tDescription .. = %s' % self.getDescription()) 898 s.append('}') 899 return '\n'.join(s)
900
901 - def __repr__(self):
902 return str(self)
903
904 905 -class Bundle(object):
906 907 KEY_PATTERN = re.compile( 908 '-----BEGIN.+KEY-----\n.+\n-----END.+KEY-----', 909 re.DOTALL) 910 CERT_PATTERN = re.compile( 911 '-----BEGIN CERTIFICATE-----\n.+\n-----END CERTIFICATE-----', 912 re.DOTALL) 913 914 @classmethod
915 - def split(cls, pem):
916 m = cls.KEY_PATTERN.search(pem) 917 if m is None: 918 raise Exception, 'Key not found.' 919 key = m.group(0) 920 m = cls.CERT_PATTERN.search(pem) 921 if m is None: 922 raise Exception, 'Certificate not found.' 923 cert = m.group(0) 924 return (key, cert)
925
926 - def __init__(self, key=None, cert=None):
927 self.key = key 928 self.cert = cert
929
930 - def read(self, path):
931 f = open(path) 932 try: 933 b = self.split(f.read()) 934 self.key = b[0] 935 self.cert = b[1] 936 self.path = path 937 finally: 938 f.close() 939 return self
940
941 - def write(self, path):
942 f = open(path, 'w') 943 try: 944 f.write(str(self)) 945 self.path = path 946 finally: 947 f.close() 948 return self
949
950 - def __str__(self):
951 s = [] 952 s.append(self.key) 953 s.append(self.cert) 954 s.append('') 955 return '\n'.join(s)
956 957 958 import sys 959 if __name__ == '__main__': 960 for path in sys.argv[1:]: 961 print path 962 pc = EntitlementCertificate() 963 pc.read(path) 964 print pc 965