Changeset 8089


Ignore:
Timestamp:
14/08/12 11:22:35 (8 years ago)
Author:
pjkersha
Message:
Location:
trunk/CertificateAuthority/ca
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/CertificateAuthority/ca/__init__.py

    r8087 r8089  
     1"""ca package contains Certificate Authority classes 
     2""" 
     3__author__ = "P J Kershaw" 
     4__date__ = "10/08/12" 
     5__copyright__ = "(C) 2012 Science and Technology Facilities Council" 
     6__license__ = "BSD - see LICENSE file in top-level directory" 
     7__contact__ = "Philip.Kershaw@stfc.ac.uk" 
     8__revision__ = "$Id: $" 
     9from ConfigParser import ConfigParser, SafeConfigParser 
     10from os import path 
     11import logging 
     12log = logging.getLogger(__name__) 
    113 
    214from OpenSSL import crypto 
     15 
    316 
    417class Utils(object): 
     
    6174 
    6275 
     76class CertificateAuthorityCSRError(Exception): 
     77    """Error with input certificate signing request""" 
     78     
     79     
    6380class CertificateAuthority(object): 
    6481    """Provide basic functionality for a Certificate Authority""" 
    6582    certificate_version2 = 1 
    6683    certificate_version3 = 2 
     84     
     85    __slots__ = ('__cert', '__key', '__serial_num', '__min_key_nbits') 
    6786     
    6887    def __init__(self): 
     
    7190        self.__key = None 
    7291        self.__serial_num = 0L 
     92        self.__min_key_nbits = 1024 
     93 
     94 
     95    @classmethod 
     96    def from_config(cls, cfg, **kw): 
     97        '''Alternative constructor makes object from config file settings 
     98        @type cfg: basestring / ConfigParser derived type 
     99        @param cfg: configuration file path or ConfigParser type object 
     100        @rtype: ndg.saml.saml2.binding.soap.client.SOAPBinding or derived type 
     101        @return: new instance of this class 
     102        ''' 
     103        obj = cls() 
     104        obj.parse_config(cfg, **kw) 
     105         
     106        return obj 
     107 
     108    def parse_config(self, cfg, prefix='', section='DEFAULT'): 
     109        '''Read config file settings 
     110        @type cfg: basestring /ConfigParser derived type 
     111        @param cfg: configuration file path or ConfigParser type object 
     112        @type prefix: basestring 
     113        @param prefix: prefix for option names e.g. "attributeQuery." 
     114        @type section: baestring 
     115        @param section: configuration file section from which to extract 
     116        parameters. 
     117        '''   
     118        if isinstance(cfg, basestring): 
     119            config_file_path = path.expandvars(cfg) 
     120            here_dir = path.dirname(config_file_path) 
     121            _cfg = SafeConfigParser(defaults=dict(here=here_dir)) 
     122            _cfg.optionxform = str 
     123 
     124            _cfg.read(config_file_path) 
     125             
     126        elif isinstance(cfg, ConfigParser): 
     127            _cfg = cfg    
     128        else: 
     129            raise AttributeError('Expecting basestring or ConfigParser type ' 
     130                                 'for "cfg" attribute; got %r type' % type(cfg)) 
     131         
     132        # Get items for this section as a dictionary so that parseKeywords can 
     133        # used to update the object 
     134        kw = dict(_cfg.items(section)) 
     135        if 'prefix' not in kw and prefix: 
     136            kw['prefix'] = prefix 
     137             
     138        self.parse_keywords(**kw) 
     139         
     140    def parse_keywords(self, prefix='', **kw): 
     141        """Update object from input keywords 
     142         
     143        @type prefix: basestring 
     144        @param prefix: if a prefix is given, only update self from kw items  
     145        where keyword starts with this prefix 
     146        @type kw: dict 
     147        @param kw: items corresponding to class instance variables to  
     148        update.  Keyword names must match their equivalent class instance  
     149        variable names.  However, they may prefixed with <prefix> 
     150        """ 
     151        prefixLen = len(prefix) 
     152        for optName, val in kw.items(): 
     153            if prefix: 
     154                # Filter attributes based on prefix 
     155                if optName.startswith(prefix): 
     156                    setattr(self, optName[prefixLen:], val) 
     157            else: 
     158                # No prefix set - attempt to set all attributes    
     159                setattr(self, optName, val) 
     160                 
     161    @classmethod 
     162    def from_keywords(cls, prefix='', **kw): 
     163        """Create a new instance initialising instance variables from the  
     164        keyword inputs 
     165        @type prefix: basestring 
     166        @param prefix: if a prefix is given, only update self from kw items  
     167        where keyword starts with this prefix 
     168        @type kw: dict 
     169        @param kw: items corresponding to class instance variables to  
     170        update.  Keyword names must match their equivalent class instance  
     171        variable names.  However, they may prefixed with <prefix> 
     172        @return: new instance of this class 
     173        @rtype: ndg.saml.saml2.binding.soap.client.SOAPBinding or derived type 
     174        """ 
     175        obj = cls() 
     176        obj.from_keywords(prefix=prefix, **kw) 
     177         
     178        return obj 
    73179 
    74180    @property 
     
    97203         
    98204    @property 
    99     def serial_num(self): 
     205    def serial_num_counter(self): 
    100206        """Certificate serial number""" 
    101207        return self.__serial_num 
    102208 
    103     @serial_num.setter 
    104     def serial_num(self, value): 
     209    @serial_num_counter.setter 
     210    def serial_num_counter(self, value): 
    105211        if not isinstance(value, (long, int)): 
    106             raise TypeError('Expecting int or long type for "serial_num" ' 
     212            raise TypeError('Expecting int or long type for "serial_num_counter" ' 
    107213                            'got %r type' % type(value)) 
    108214        self.__serial_num = long(value) 
    109  
     215         
     216    @property 
     217    def min_key_nbits(self): 
     218        """Minimum number of bits required for key in certificate request""" 
     219        return self.__min_key_nbits 
     220 
     221    @min_key_nbits.setter 
     222    def min_key_nbits(self, value): 
     223        if not isinstance(value, (long, int)): 
     224            raise TypeError('Expecting int or long type for "min_key_nbits" ' 
     225                            'got %r type' % type(value)) 
     226        self.__min_key_nbits = long(value) 
     227         
    110228    @classmethod 
    111229    def from_files(cls, cert_filepath, key_filepath, key_file_passwd=None): 
     
    148266        @return: The signed certificate in an X.509 object 
    149267        """ 
     268         
     269        # Check number of bits in key 
     270        pkey = req.get_pubkey() 
     271        pkey_nbits = pkey.bits() 
     272        if pkey_nbits < self.min_key_nbits: 
     273            raise CertificateAuthorityCSRError('Certificate signing request ' 
     274                                               'must use a key with at least ' 
     275                                               '%d bits, input request has a ' 
     276                                               'key with %d bits' % pkey_nbits) 
     277             
    150278        cert = crypto.X509() 
    151         cert.set_serial_number(self.serial_num) 
     279        cert.set_serial_number(self.serial_num_counter) 
    152280         
    153281        cert.gmtime_adj_notBefore(not_before_ndays) 
     
    166294            basic_constraints = 'CA:false' 
    167295             
    168         # Add basic contraints as first element of extensions tuple 
     296        # Add basic constraints as first element of extensions tuple 
    169297        extensions = (crypto.X509Extension('basicConstraints',  
    170298                                           True,  
     
    182310         
    183311        # Serial number is a counter 
    184         self.serial_num += 1 
     312        self.serial_num_counter += 1 
     313         
     314        if log.isEnabledFor(logging.INFO): 
     315            dn = ''.join(["/%s=%s" % (k, v)  
     316                          for k,v in cert.get_subject().get_components()]) 
     317             
     318            log.info('Issuing certificate with subject %s', dn) 
    185319         
    186320        return cert  
  • trunk/CertificateAuthority/ca/test/test_ca.py

    r8087 r8089  
    44@author: philipkershaw 
    55''' 
     6import logging 
     7logging.basicConfig(level=logging.DEBUG) 
     8 
    69from os import path 
    710import unittest 
     
    1922class CertificateAuthorityTestCase(unittest.TestCase): 
    2023    """Test certificate authority class""" 
    21     ca_cert_filepath = path.join(this_dir, 'cacert.pem') 
    22     ca_key_filepath = path.join(this_dir, 'cakey.pem') 
     24    ca_cert_filepath = path.join(this_dir, 'myca.crt') 
     25    ca_key_filepath = path.join(this_dir, 'myca.key') 
    2326    ca_key_file_passwd = 'ndgtestca' 
    2427     
Note: See TracChangeset for help on using the changeset viewer.