pyca-20031118/0040755000076400001440000000000007756443113012252 5ustar michaeluserspyca-20031118/bin/0040755000076400001440000000000007756443113013022 5ustar michaeluserspyca-20031118/bin/certs2ldap.py0100755000076400001440000003407507475754042015454 0ustar michaelusers#!/usr/bin/python """ certs2ldap.py - Upload all EE certificates on LDAP server (c) by Michael Stroeder, michael@stroeder.com """ __version__ = '0.6.6' import sys, string, os, getopt ldap_attrtype = { 'ST':'st', 'Email':'mail', 'emailAddress':'mail', 'E':'mail', 'L':'l', 'O':'o', 'OU':'ou', 'CN':'cn', } def findoption(options,paramname): for i in options: if i[0]==paramname: return i return () def PrintUsage(ErrorMsg='',ErrorCode=1): script_name = string.split(sys.argv[0],os.sep)[-1] sys.stderr.write("""*** %s *** (C) by Michael Stroeder, 1999 usage: %s [options] Options: -h or --help Print out this message --config=[pathname] Pathname of OpenSSL configuration file. You may also use env variable OPENSSL_CONF. Default: /etc/openssl/openssl.cnf --pycalib=[directory] Specify directory containing the pyCA modules You may also use env variable PYCALIB. Default: /usr/local/pyca/pylib --host=[LDAP host] Specify an alternate host:port on which the ldap server is running. Default: localhost:389 --basedn=[searchbase] Use searchbase as the starting point for the search instead of the default. Default: emtpy string --binddn=[binddn] Use binddn to bind to the LDAP directory. Default: cn=root[,searchbase] --bindpasswd=[password] Use password to bind to the LDAP directory. For security reasons it is better to set this with the env variable LDAP_PASSWD if you really have to provide the password in a non-interactive script. Default: emtpy string --filtertemplate=[Python dict string] A Python string used as template for searching the LDAP entries of certificate owners. E.g. (&(cn=%%(CN)s)(mail=%%(Email)s)) Default: (mail=%%(Email)s) --certdnfilter=[regex] Specify a filter as comma separated list of regular expressions for DNs of the certificates which should be sent to the LDAP host. E.g. C=DE,CN=.*,Email=.*@domain.my Default: Email=.* --objectclasses=[objectClass] Add objectclass: [objectClass] to the entry. Might be a comma-separated list for specifying multiple object classes. --replace Replace existing userCertificate;binary attributes --create Create LDAP entries if no entry for a user certificate was found. --dntemplate=[Python dict string] A Python string used as template for the distinguished name of LDAP entries to be created. E.g. cn=%%(CN)s+mail=%%(Email)s,ou=Testing,dc=stroeder,dc=com """ % (script_name,script_name)) if ErrorMsg: sys.stderr.write('Error: %s\n' % ErrorMsg) sys.exit(ErrorCode) script_name=sys.argv[0] try: options,args=getopt.getopt( sys.argv[1:],'h', [ 'help', 'config=', 'pycalib=', 'host=', 'basedn=', 'binddn=', 'bindpasswd=', 'filtertemplate=', 'certdnfilter=', 'objectclasses=', 'replace', 'create', 'dntemplate=' ] ) except getopt.error,e: PrintUsage(str(e)) if findoption(options,'-h')!=() or findoption(options,'--help')!=(): PrintUsage() if findoption(options,'--config')!=(): opensslcnfname = findoption(options,'--config')[1] else: opensslcnfname = os.environ.get('OPENSSL_CONF','/etc/openssl/openssl.cnf') if not os.path.isfile(opensslcnfname): PrintUsage('Config file %s not found.' % (opensslcnfname)) sys.exit(1) if findoption(options,'--pycalib')!=(): pycalib = findoption(options,'--pycalib')[1] else: pycalib = os.environ.get('PYCALIB','/usr/local/pyca/pylib') if not os.path.exists(pycalib) or not os.path.isdir(pycalib): PrintUsage('Directory %s with pyCA modules not found!' % (pycalib)) sys.path.append(pycalib) try: import ldap except ImportError: PrintUsage('python-ldap module not found.' % (pycalib)) sys.exit(1) try: import openssl,charset except ImportError: PrintUsage('pyCA modules not found in directory %s.' % (pycalib)) sys.exit(1) # Read the configuration file if os.path.isfile('%s.pickle' % (opensslcnfname)): # Try to read OpenSSL's config file from a pickled copy f=open('%s.pickle' % (opensslcnfname),'rb') try: # first try to use the faster cPickle module from cPickle import load except ImportError: from pickle import load opensslcnf=load(f) f.close() else: # Parse OpenSSL's config file from source opensslcnf=openssl.cnf.OpenSSLConfigClass(opensslcnfname) delete_reason = {openssl.db.DB_TYPE_EXP:'expired',openssl.db.DB_TYPE_REV:'revoked'} pyca_section = opensslcnf.data.get('pyca',{}) openssl.bin_filename = pyca_section.get('OpenSSLExec','/usr/local/ssl/bin/openssl') if not os.path.isfile(openssl.bin_filename): sys.stderr.write('Did not find OpenSSL executable %s.\n' % (openssl.bin_filename)) sys.exit(1) if findoption(options,'--host')!=(): ldap_host = findoption(options,'--host')[1] else: ldap_host = 'localhost:389' if findoption(options,'--basedn')!=(): basedn = findoption(options,'--basedn')[1] else: basedn = '' if findoption(options,'--binddn')!=(): binddn = findoption(options,'--binddn')[1] else: if basedn: binddn = 'cn=root,%s' % basedn else: binddn = 'cn=root' if findoption(options,'--bindpasswd')!=(): bindpasswd = findoption(options,'--bindpasswd')[1] else: if os.environ.has_key('LDAP_PASSWD'): bindpasswd = os.environ.get['LDAP_PASSWD'] else: from getpass import getpass sys.stdout.write('Enter password for bind DN "%s".\n' % (binddn)) bindpasswd = getpass() if findoption(options,'--filtertemplate')!=(): filtertemplate = findoption(options,'--filtertemplate')[1] else: filtertemplate = r'(mail=%(Email)s)' if findoption(options,'--objectclasses')!=(): objectclasses = map( string.strip, map( None,string.split(findoption(options,'--objectclasses')[1],',') ) ) else: objectclasses = None replace = findoption(options,'--replace')!=() if findoption(options,'--certdnfilter')!=(): certdnfilterlist = string.split(findoption(options,'--certdnfilter')[1],',') certdnfilter = {} for i in certdnfilterlist: attr,filter = string.split(i,'=',1) if filter: certdnfilter[attr]=filter else: certdnfilter = {'Email':'.*'} create = findoption(options,'--create')!=() if findoption(options,'--dntemplate')!=(): dntemplate = findoption(options,'--dntemplate')[1] else: dntemplate = r'mail=%(Email)s,'+basedn print repr(dntemplate) # FIX ME!!! # This should be surrounded by a nice try: except: clause # which catches specific exceptions and outputs # nicer error messages. l = ldap.open(ldap_host) l.bind_s(binddn,bindpasswd,ldap.AUTH_SIMPLE) ca_names = opensslcnf.sectionkeys.get('ca',[]) old_db_filenames = [] for ca_name in ca_names: sys.stdout.write('*** Processing %s ***\n\n' % (ca_name)) ca = opensslcnf.getcadata(ca_name) # Ist der Zertifikattyp 'S/MIME for client use' ? if ca.isclientcert() and \ not ca.database in old_db_filenames and \ os.path.isfile(ca.database): old_db_filenames.append(ca.database) # Anfrage starten certs_found = openssl.db.GetEntriesbyDN(ca.database,certdnfilter,casesensitive=1,onlyvalid=0) for cert_entry in certs_found: certdn = charset.asn12iso(cert_entry[openssl.db.DB_name]) certdndict = openssl.db.SplitDN(charset.iso2utf(certdn)) ldap_filter = filtertemplate % certdndict try: ldap_result = l.search_s( basedn, ldap.SCOPE_SUBTREE, ldap_filter, ['objectclass','userCertificate;binary','userSMIMECertificate;binary'], 0 ) except ldap.NO_SUCH_OBJECT: sys.stdout.write('Certificate subject "%s" not found with filter "%s".\n' % (certdn,ldap_filter)) ldap_result=[] except: exc_obj,exc_value,exc_traceback = sys.exc_info() sys.stderr.write('Unexpected error during searching with filter "%s":\n%s\n' % (ldap_filter,exc_value)) sys.exit(1) if ldap_result: # Read certificate data certfilename = os.path.join(ca.certs,'%s.pem' % (cert_entry[openssl.db.DB_serial])) cert = openssl.cert.X509CertificateClass(certfilename) local_cert = cert.readcertfile('der') for entry in ldap_result: ldap_dn = entry[0] old_objectclasses = {} for oc in entry[1].get('objectClass',entry[1].get('objectclass',[])): old_objectclasses[string.lower(oc)] = None existing_usercert_attrtype = None for a in [ 'userCertificate;binary','userCertificate', 'usercertificate;binary','usercertificate', ]: if entry[1].has_key(a): existing_usercert_attrtype = a break old_usercertificate_attr = {} if existing_usercert_attrtype!=None: for ldap_cert in entry[1][existing_usercert_attrtype]: old_usercertificate_attr[ldap_cert] = None ldap_modlist = [] if cert_entry[openssl.db.DB_type]==openssl.db.DB_TYPE_VAL: if existing_usercert_attrtype is None: # Add new certificate attribute ldap_modlist.append((ldap.MOD_ADD,'userCertificate;binary',[local_cert])) sys.stdout.write('Adding new certificate attribute usercertificate;binary with certificate serial %s of LDAP entry "%s".\n' % (cert_entry[openssl.db.DB_serial],charset.utf2iso(ldap_dn))) elif replace: # Replace existing certificate attribute ldap_modlist.append((ldap.MOD_DELETE,existing_usercert_attrtype,None)) ldap_modlist.append((ldap.MOD_ADD,existing_usercert_attrtype,[local_cert])) sys.stdout.write('Replacing attribute %s of entry %s with certificate serial %s.\n' % ( existing_usercert_attrtype, charset.utf2iso(ldap_dn), cert_entry[openssl.db.DB_serial] ) ) elif not old_usercertificate_attr.has_key(local_cert): # Add new certificate attribute value ldap_modlist.append((ldap.MOD_DELETE,existing_usercert_attrtype,None)) ldap_modlist.append((ldap.MOD_ADD,existing_usercert_attrtype,old_usercertificate_attr.keys()+[local_cert])) sys.stdout.write( 'Adding certificate with certificate serial %s to existing attribute %s of LDAP entry "%s".\n' % ( cert_entry[openssl.db.DB_serial], existing_usercert_attrtype, charset.utf2iso(ldap_dn) ) ) else: sys.stdout.write('Leaving attribute %s of entry %s untouched.\n' % ( existing_usercert_attrtype, charset.utf2iso(ldap_dn) ) ) if ldap_modlist and objectclasses: # New object classes were specified at command-line # => add to modify list if necessary new_objectclasses = [] for oc in objectclasses: if not old_objectclasses.has_key(string.lower(oc)): new_objectclasses.append(oc) if new_objectclasses: ldap_modlist.append((ldap.MOD_ADD,'objectClass',new_objectclasses)) elif (cert_entry[openssl.db.DB_type]==openssl.db.DB_TYPE_EXP) or \ (cert_entry[openssl.db.DB_type]==openssl.db.DB_TYPE_REV): sys.stdout.write('Certificate (serial %s) %s.\n' % (cert_entry[openssl.db.DB_serial],delete_reason[cert_entry[openssl.db.DB_type]])) if old_usercertificate_attr.has_key(local_cert): del old_usercertificate_attr[local_cert] sys.stdout.write('Deleting certificate with certificate serial %s from attribute usercertificate;binary of LDAP entry "%s".\n' % (cert_entry[openssl.db.DB_serial],charset.utf2iso(ldap_dn))) ldap_modlist.append((ldap.MOD_REPLACE,existing_usercert_attrtype,old_usercertificate_attr.keys())) if ldap_modlist and objectclasses: new_objectclasses = [] for oc in objectclasses: if old_objectclasses.has_key(string.lower(oc)): new_objectclasses.append(oc) if new_objectclasses: ldap_modlist.append((ldap.MOD_DELETE,'objectClass',new_objectclasses)) # Do modifications on directory if modlist is not empty if ldap_modlist: try: l.modify_s(ldap_dn,ldap_modlist) except ldap.NO_SUCH_OBJECT: sys.stderr.write('No such object "%s": Probably a parent entry is missing.\n' % ( charset.utf2iso(ldap_dn) ) ) except ldap.INSUFFICIENT_ACCESS,e: sys.stderr.write('You are not allowed to modify entry "%s": %s.\n' % ( charset.utf2iso(ldap_dn),str(e) ) ) except ldap.LDAPError,e: sys.stderr.write('LDAPError: %s.\n' % str(e)) else: if cert_entry[openssl.db.DB_type]==openssl.db.DB_TYPE_VAL: if create: # Read certificate data certfilename = os.path.join(ca.certs,'%s.pem' % (cert_entry[openssl.db.DB_serial])) cert = openssl.cert.X509CertificateClass(certfilename) local_cert = cert.readcertfile('der') ldap_dn = dntemplate % certdndict ldap_modlist = [ ('objectClass',['person','organizationalPerson','inetOrgPerson']), ('userCertificate;binary',[local_cert]), ('sn',['-']) ] for k in certdndict.keys(): try: ldap_modlist.append((ldap_attrtype[k],certdndict[k])) except KeyError: pass try: # print ldap_modlist l.add_s(ldap_dn,ldap_modlist) except ldap.NO_SUCH_OBJECT: sys.stderr.write('No such object "%s": Probably a parent entry is missing.\n' % ( charset.utf2iso(ldap_dn) ) ) except ldap.INSUFFICIENT_ACCESS,e: sys.stderr.write('You are not allowed to add entry "%s": %s.\n' % ( charset.utf2iso(ldap_dn),str(e) ) ) except ldap.LDAPError,e: sys.stderr.write('LDAPError: %s.\n' % str(e)) else: sys.stdout.write('Added new entry "%s" for certificate serial %s.\n' % (charset.utf2iso(ldap_dn),cert_entry[openssl.db.DB_serial])) else: sys.stderr.write('No entry found with filter "%s" for %s.\n' % (ldap_filter,certdn)) l.unbind_s() pyca-20031118/bin/ca2ldif.py0100755000076400001440000001171407434757702014711 0ustar michaelusers#!/usr/bin/python ######################################################################## # ca2ldif.py # (c) by Michael Stroeder, michael@stroeder.com ######################################################################## __version__ = '0.6.6' ######################################################################## # This simple script generates a LDIF file containing all CA certs # and CRLs. ######################################################################## import sys, string, os, getopt, types, shutil def findoption(options,paramname): for i in options: if i[0]==paramname: return i return () def PrintUsage(ErrorMsg='',ErrorCode=1): script_name = string.split(sys.argv[0],os.sep)[-1] sys.stderr.write("""*** %s *** (C) by Michael Stroeder, 1999 usage: %s [options] Options: -h or --help Print out this message --config=[pathname] Pathname of OpenSSL configuration file. Default: /etc/openssl/openssl.cnf --pycalib=[directory] Specify directory containing the pyCA modules Default: /usr/local/pyca/pylib --out=[pathname] Pathname of LDIF file for output Default: stdout --dntemplate=[Python dict string] A Python string used as template for building LDAP Distinguished Names E.g. cn=%%(CN)s,ou=TestCA,o=My company,c=DE Default: cn=%%(CN)s --crl Add CRLs to entries. """ % (script_name,script_name)) if ErrorMsg: sys.stderr.write('Error: %s\n' % ErrorMsg) sys.exit(ErrorCode) script_name=sys.argv[0] try: options,args=getopt.getopt(sys.argv[1:],'h',['help','config=','pycalib=','out=','dntemplate=','crl']) except getopt.error,e: PrintUsage(str(e)) if findoption(options,'-h')!=() or findoption(options,'--help')!=(): PrintUsage(script_name) if findoption(options,'--config')!=(): opensslcnfname = findoption(options,'--config')[1] else: opensslcnfname = os.environ.get('OPENSSL_CONF','/etc/openssl/openssl.cnf') if not os.path.isfile(opensslcnfname): PrintUsage('Config file %s not found.' % (opensslcnfname)) sys.exit(1) if findoption(options,'--pycalib')!=(): pycalib = findoption(options,'--pycalib')[1] else: pycalib = os.environ.get('PYCALIB','/usr/local/pyca/pylib') if not os.path.exists(pycalib) or not os.path.isdir(pycalib): PrintUsage('Module directory %s does not exist or is no directory.' % (pycalib)) sys.exit(1) sys.path.append(pycalib) try: import openssl, charset, ldif, ldapbase except ImportError: PrintUsage('Required pyCA modules not found in directory %s!' % (pycalib)) # Read the configuration file if os.path.isfile('%s.pickle' % (opensslcnfname)): # Try to read OpenSSL's config file from a pickled copy f=open('%s.pickle' % (opensslcnfname),'rb') try: # first try to use the faster cPickle module from cPickle import load except ImportError: from pickle import load opensslcnf=load(f) f.close() else: # Parse OpenSSL's config file from source opensslcnf=openssl.cnf.OpenSSLConfigClass(opensslcnfname) create_crls = findoption(options,'--crl')!=() pyca_section = opensslcnf.data.get('pyca',{}) openssl.bin_filename = pyca_section.get('OpenSSLExec','/usr/local/ssl/bin/openssl') if not os.path.isfile(openssl.bin_filename): sys.stderr.write('Did not find OpenSSL executable %s.\n' % (openssl.bin_filename)) sys.exit(1) if findoption(options,'--out')!=(): ldiffile = open(findoption(options,'--out')[1],'w') else: ldiffile = sys.stdout if findoption(options,'--dntemplate')!=(): dntemplate = findoption(options,'--dntemplate')[1] else: dntemplate = r'cn=%(CN)s' ca_names = opensslcnf.sectionkeys.get('ca',[]) ca_dn_dict = {} for ca_name in ca_names: ca = opensslcnf.getcadata(ca_name) if os.path.isfile(ca.certificate): cacert = openssl.cert.X509CertificateClass(ca.certificate) ca_dn = charset.iso2utf(charset.t612iso(dntemplate % (cacert.subject))) if ca_dn_dict.has_key(ca_dn): sys.stderr.write('Warning: DN of %s conflicts with %s.\n' % (ca_name,ca_dn_dict[ca_dn])) else: ca_dn_dict[ca_dn]=ca_name if ldapbase.dn_regex.match(ca_dn): ca_entry = {'objectclass':['top','certificationAuthority']} ca_entry['cACertificate;binary'] = [cacert.readcertfile('der')] if create_crls: if os.path.isfile(ca.crl): cacrl = openssl.cert.CRLClass(ca.crl) ca_entry['certificateRevocationList;binary'] = [cacrl.readcertfile('der')] ca_entry['authorityRevocationList;binary'] = [cacrl.readcertfile('der')] else: sys.stderr.write('Warning: CRL file %s not found.\n' % (ca.crl)) certificateRevocationList_binary='' ldiffile.write(ldif.CreateLDIF(ca_dn,ca_entry,['cACertificate;binary','certificateRevocationList;binary'])) ldiffile.write('\n') else: sys.stderr.write('Warning: DN "%s" is not a valid DN.\nCheck parameter --dntemplate="%s".\n' % (ca_dn,dntemplate)) cACertificate_binary='' else: sys.stderr.write('Warning: CA certificate file %s not found.\n' % (ca.certificate)) cACertificate_binary='' ldiffile.close() pyca-20031118/bin/print-cacerts.py0100755000076400001440000001170707321127433016150 0ustar michaelusers#!/usr/bin/python ######################################################################## # print-ca-certs.py # (c) by Michael Stroeder, michael@stroeder.com ######################################################################## __version__ = '0.6.6' ######################################################################## # This simple script prints all CA certs on stdout. # This is intended to generate a authentic printout of the fingerprints # on the private CA system. # Choose the option --html to generate nicer formatted HTML-output # instead of the default textual output in ISO-8859-1. ######################################################################## import sys, string, os, getopt def findoption(options,paramname): for i in options: if i[0]==paramname: return i return () def PrintUsage(ErrorMsg='',ErrorCode=1): script_name = string.split(sys.argv[0],os.sep)[-1] sys.stderr.write("""*** %s *** (C) by Michael Stroeder, 1999 usage: %s [options] Options: -h or --help Print out this message --config=[pathname] Pathname of OpenSSL configuration file. You may also use env variable OPENSSL_CONF. Default: /etc/openssl/openssl.cnf --pycalib=[directory] Specify directory containing the pyCA modules Default: /usr/local/pyca/pylib --html Generate nicer formatted HTML output """ % (script_name,script_name)) if ErrorMsg: sys.stderr.write('Error: %s\n' % ErrorMsg) sys.exit(ErrorCode) script_name=sys.argv[0] try: options,args=getopt.getopt(sys.argv[1:],'h',['help','config=','pycalib=','html']) except getopt.error,e: PrintUsage(str(e)) if findoption(options,'-h')!=() or findoption(options,'--help')!=(): PrintUsage() if findoption(options,'--config')!=(): opensslcnfname = findoption(options,'--config')[1] else: opensslcnfname = os.environ.get('OPENSSL_CONF','/etc/openssl/openssl.cnf') if not os.path.isfile(opensslcnfname): PrintUsage('Config file %s not found.' % (opensslcnfname)) if findoption(options,'--pycalib')!=(): pycalib = findoption(options,'--pycalib')[1] else: pycalib = os.environ.get('PYCALIB','/usr/local/pyca/pylib') if not os.path.exists(pycalib) or not os.path.isdir(pycalib): PrintUsage('Module directory %s not exists or not a directory.' % (pycalib)) sys.path.append(pycalib) try: import openssl, charset, htmlbase except ImportError: PrintUsage('Required pyCA modules not found in directory %s!' % (pycalib)) # Read the configuration file if os.path.isfile('%s.pickle' % (opensslcnfname)): # Try to read OpenSSL's config file from a pickled copy f=open('%s.pickle' % (opensslcnfname),'rb') try: # first try to use the faster cPickle module from cPickle import load except ImportError: from pickle import load opensslcnf=load(f) f.close() else: # Parse OpenSSL's config file from source opensslcnf=openssl.cnf.OpenSSLConfigClass(opensslcnfname) pyca_section = opensslcnf.data.get('pyca',{}) openssl.bin_filename = pyca_section.get('OpenSSLExec','/usr/local/ssl/bin/openssl') if not os.path.isfile(openssl.bin_filename): PrintUsage('Did not find OpenSSL executable %s.' % (openssl.bin_filename)) ca_names = opensslcnf.sectionkeys.get('ca',[]) htmlmode = findoption(options,'--html')!=() if htmlmode: #HTML mode sys.stdout.write('\n\nCA certs\n\n\n
\n') for ca_name in ca_names: ca = opensslcnf.getcadata(ca_name) if os.path.isfile(ca.certificate): # Parse certificate textual output cacert = openssl.cert.X509CertificateClass(ca.certificate) sys.stdout.write('

%s

%s

' % (ca_name,cacert.htmlprint())) sys.stdout.write('

\n\n\n') else: # Text mode for ca_name in ca_names: ca = opensslcnf.getcadata(ca_name) if os.path.isfile(ca.certificate): # Parse certificate textual output cacert = openssl.cert.X509CertificateClass(ca.certificate) # Convert character sets subject,issuer = {},{} for attr in ['CN','Email','OU','O','L','ST','C']: subject[attr] = string.strip(charset.asn12iso(cacert.subject.get(attr,''))) issuer[attr] = string.strip(charset.asn12iso(cacert.issuer.get(attr,''))) sys.stdout.write('Subject:\nCommon Name: "%(CN)s"\nOrganizational Unit: "%(OU)s"\nOrganization: "%(O)s"\nLocation: "%(L)s"\nState/Province: "%(ST)s"\nCountry: "%(C)s"\n\n' % (subject)) sys.stdout.write('Issuer:\nCommon Name: "%(CN)s"\nOrganizational Unit: "%(OU)s"\nOrganization: "%(O)s"\nLocation: "%(L)s"\nState/Province: "%(ST)s"\nCountry: "%(C)s"\n\n' % (issuer)) sys.stdout.write('Serial: %s\n' % (cacert.serial)) sys.stdout.write('Validity: from %s until %s\n' % (cacert.notBefore,cacert.notAfter)) sys.stdout.write('Hash: %s\n' % (cacert.hash)) sys.stdout.write('SHA-1 Fingerprint: %s\n' % (cacert.getfingerprint('sha1'))) sys.stdout.write('MD5 Fingerprint: %s\n' % (cacert.getfingerprint('md5'))) sys.stdout.write('\n%s\n\n' % (72*'-')) pyca-20031118/bin/ldap2certs.py0100755000076400001440000002012507301173255015430 0ustar michaelusers#!/usr/bin/python ######################################################################## # ldap2certs.py # (c) by Michael Stroeder, michael@stroeder.com # This simple script retrieves client certs from a LDAP repository # and stores the fingerprint in a text mapping file for use with # Postfix/TLS (see option relay_clientcert) and ######################################################################## __version__ = '0.6.6' import sys, string, os, getopt def findoption(options,paramname): for i in options: if i[0]==paramname: return i return () def PrintUsage(ErrorMsg='',ErrorCode=1): script_name = string.split(sys.argv[0],os.sep)[-1] sys.stderr.write("""*** %s *** (C) by Michael Stroeder, 1999 usage: %s [options] Options: -h or --help Print out this message -v or --verbose Print debugging output to stdout --pylib=[directory] Specify directory containing the additonal Python modules. Default: --host=[LDAP host] Specify an alternate host:port on which the ldap server is running. Default: localhost:389 --basedn=[searchbase] Use searchbase as the starting point for the search instead of the default. Default: emtpy string --binddn=[binddn] Use binddn to bind to the LDAP directory. Default: cn=root[,searchbase] --bindpasswd=[password] Use password to bind to the LDAP directory. For security reasons it is better to set this with the env variable LDAP_PASSWD if you really have to provide the password in a non-interactive script. Default: emtpy string --searchfilter=[Python dict string] LDAP search filter for finding entries with certificate data. Default: (usercertificate;binary=*) --certdnfilter=[regex] Specify a filter as comma separated list of regular expressions for DNs of the certificates which should be sent to the LDAP host. E.g. C=DE,CN=.*,Email=.*@domain.my Default: Email=.* --outdir=[directory name] Directory where to store the client cert files and symbolic links ([hash value].0). If not defined no client cert files are written. --rcc_filename=[path name] Path name of lookup table file. (parameter relay_clientcerts in main.cf of Postfix/TLS) If not defined the table is not written. --rcc_delimiter=(:|_) Character used as delimiter for fingerprint string Default: ":" --replace Replace existing files (append or add otherwise) """ % (script_name,script_name)) if ErrorMsg: sys.stderr.write('Error: %s\n' % ErrorMsg) sys.exit(ErrorCode) script_name=sys.argv[0] try: options,args=getopt.getopt( sys.argv[1:],'vh', [ 'help', 'verbose', 'pylib=', 'host=', 'basedn=', 'binddn=', 'bindpasswd=', 'searchfilter=', 'outdir=', 'rcc_filename=', 'rcc_delimiter=', 'replace' ] ) except getopt.error,e: PrintUsage(str(e)) if findoption(options,'-h')!=() or findoption(options,'--help')!=(): PrintUsage() if findoption(options,'-v')!=() or findoption(options,'--verbose')!=(): pass else: sys.stdout.close() sys.stdout = open('/dev/null','w') if findoption(options,'--pylib')!=(): pylib = findoption(options,'--pylib')[1] if not os.path.exists(pylib) or not os.path.isdir(pylib): PrintUsage('Modules directory %s not found or is not a directory!' % (pylib)) sys.path.append(pylib) import charset,ldap,certhelper if findoption(options,'--host')!=(): ldap_host = findoption(options,'--host')[1] else: ldap_host = 'localhost:389' if findoption(options,'--basedn')!=(): basedn = findoption(options,'--basedn')[1] else: basedn = '' if findoption(options,'--binddn')!=(): binddn = findoption(options,'--binddn')[1] else: if basedn: binddn = 'cn=root,%s' % basedn else: binddn = 'cn=root' if findoption(options,'--bindpasswd')!=(): bindpasswd = findoption(options,'--bindpasswd')[1] else: if os.environ.has_key('LDAP_PASSWD'): bindpasswd = os.environ.get['LDAP_PASSWD'] else: from getpass import getpass bindpasswd = getpass() if findoption(options,'--searchfilter')!=(): searchfilter = charset.iso2utf(findoption(options,'--searchfilter')[1]) else: searchfilter = '(usercertificate;binary=*)' if findoption(options,'--replace')!=(): replace = 1 else: replace = 0 sys.stderr.write('replace=%s\n' % replace) rcc_filemode = {0:'a',1:'w'} cert_filemode = {0:'w',1:'w'} if findoption(options,'--rcc_filename')!=(): rcc_filename = findoption(options,'--rcc_filename')[1] sys.stdout.write('rcc_filename=%s\n' % rcc_filename) rcc_file = open(rcc_filename,rcc_filemode[replace]) else: rcc_filename = None if findoption(options,'--rcc_delimiter')!=(): rcc_delimiter = findoption(options,'--rcc_delimiter')[1] else: rcc_delimiter = ':' sys.stdout.write('rcc_delimiter=%s\n' % rcc_delimiter) if findoption(options,'--outdir')!=(): outdir = findoption(options,'--outdir')[1] if not os.path.exists(outdir) or not os.path.isdir(outdir): PrintUsage('Directory %s not found or is not a directory!' % (outdir)) else: outdir = None try: l = ldap.open(ldap_host) except: exc_obj,exc_value,exc_traceback = sys.exc_info() sys.stderr.write('Error %d connecting to %s: %s\n' % (exc_value[0],ldap_host,exc_value[1])) sys.exit(1) try: l.bind_s(binddn,bindpasswd,ldap.AUTH_SIMPLE) except: exc_obj,exc_value,exc_traceback = sys.exc_info() sys.stderr.write('Unable to bind as "%s" to "%s":\n%s\n' % (binddn,ldap_host,exc_value)) sys.exit(1) try: ldap_msgid = l.search( basedn, ldap.SCOPE_SUBTREE, searchfilter, ['cn','mail','usercertificate','usersmimecertificate','usercertificate;binary','usersmimecertificate;binary'], 0 ) except ldap.NO_SUCH_OBJECT: result_dnlist = [] except ldap.FILTER_ERROR: sys.stderr.write('Bad search filter %s.\n' % charset.utf2iso(searchfilter)) sys.exit(1) except ldap.SIZELIMIT_EXCEEDED: sys.stderr.write('Sizelimit exceeded. Please refine search.\n') sys.exit(1) except ldap.NO_SUCH_OBJECT: sys.stderr.write('No search results with filter %s.\n' % charset.utf2iso(searchfilter)) sys.exit(1) except ldap.error: exc_obj,exc_value,exc_traceback = sys.exc_info() sys.stderr.write('LDAP exception %(desc)s: %(info)s.\n' % exc_value) sys.exit(1) #except: # exc_obj,exc_value,exc_traceback = sys.exc_info() # sys.stderr.write('Unhandled exception: %s.\n' % exc_value) # sys.exit(1) result_type,result_data = l.result(ldap_msgid,0) if not result_type in ['RES_SEARCH_ENTRY','RES_SEARCH_RESULT']: l.abandon(ldap_msgid) sys.stderr.write('Wrong result type: "%s"' % (result_type)) sys.exit(1) # Retrieve the search results while result_data: # Process found entries for dn,data in result_data: sys.stdout.write('Processing entry "%s".\n' % (charset.utf2iso(dn))) if data.has_key('usercertificate'): pass elif data.has_key('usersmimecertificate'): pass elif data.has_key('usercertificate;binary'): sys.stdout.write('%d DER-encoded certificate(s) found.\n' % (len(data['usercertificate;binary']))) # Process all certificates in attribute for cert_der in data['usercertificate;binary']: cert_md5fingerprint = certhelper.MD5Fingerprint(cert_der,':') if rcc_filename: # append fingerprint to rcc_filename rcc_name = data.get('mail',data.get('cn',[dn]))[0] rcc_file.write('%s %s\n' % (string.replace(cert_md5fingerprint,':',rcc_delimiter),rcc_name)) if outdir: cert_pem = certhelper.der2pem(cert_der) cert_pem_filename = os.path.join(outdir,'%s.pem' % string.replace(cert_md5fingerprint,':','_')) if replace or not os.path.isfile(cert_pem_filename): cert_pem_file = open(cert_pem_filename,cert_filemode[replace]) cert_pem_file.write(cert_pem) cert_pem_file.close() sys.stdout.write('Certificate file %s written.\n' % (cert_pem_filename)) else: sys.stdout.write('Certificate file %s already exists.\n' % (cert_pem_filename)) elif data.has_key('usersmimecertificate;binary'): pass else: sys.stdout.write('No certificate data.\n') # Get next result result_type,result_data = l.result(ldap_msgid,0) l.unbind_s() pyca-20031118/bin/copy-cacerts.py0100755000076400001440000001241507301173255015764 0ustar michaelusers#!/usr/bin/python ######################################################################## # copy-cacerts.py # (c) by Michael Stroeder, michael@stroeder.com ######################################################################## __version__ = '0.6.6' ######################################################################## # This simple script copies either # - all CA certs into a single PEM file # (intended for use ApacheSSL SSLCACertificateFile) or # - a directory with appropriate hash symlinks to the CA cert files # (intended for use ApacheSSL SSLCACertificatePath). ######################################################################## import sys, string, os, getopt, types, shutil def findoption(options,paramname): for i in options: if i[0]==paramname: return i return () def PrintUsage(ErrorMsg='',ErrorCode=1): script_name = string.split(sys.argv[0],os.sep)[-1] sys.stderr.write("""*** %s *** (C) by Michael Stroeder, 1999 usage: %s [options] Options: -h or --help Print out this message --config=[pathname] Pathname of OpenSSL configuration file. Default: /etc/openssl/openssl.cnf --pycalib=[directory] Specify directory containing the pyCA modules You may also use env variable PYCALIB. Default: /usr/local/pyca/pylib --certfile=[pathname of output PEM file] Pathname of output PEM file containing all CA certs --certdir=[directoryname] Target directory for all CA certs. The CA certs are copied to files named like the subject DN and symbolic links are created like cacert.pem -> certhash.0. Default is to use the current directory. If you do not specify any of these options --certfile=./ca-certs.pem is assumed. """ % (script_name,script_name)) if ErrorMsg: sys.stderr.write('Error: %s\n' % ErrorMsg) sys.exit(ErrorCode) script_name=sys.argv[0] try: options,args=getopt.getopt(sys.argv[1:],'h',['help','config=','pycalib=','certdir=','certfile=']) except getopt.error,e: PrintUsage(str(e)) if findoption(options,'-h')!=() or findoption(options,'--help')!=(): PrintUsage(script_name) if findoption(options,'--config')!=(): opensslcnfname = findoption(options,'--config')[1] else: opensslcnfname = os.environ.get('OPENSSL_CONF','/etc/openssl/openssl.cnf') if not os.path.isfile(opensslcnfname): PrintUsage('Config file %s not found.' % (opensslcnfname)) sys.exit(1) if findoption(options,'--pycalib')!=(): pycalib = findoption(options,'--pycalib')[1] else: pycalib = os.environ.get('PYCALIB','/usr/local/pyca/pylib') if not os.path.exists(pycalib) or not os.path.isdir(pycalib): PrintUsage('Module directory %s not exists or not a directory.' % (pycalib)) sys.exit(1) sys.path.append(pycalib) try: import openssl, charset except ImportError: PrintUsage('Required pyCA modules not found in directory %s!' % (pycalib)) # Read the configuration file if os.path.isfile('%s.pickle' % (opensslcnfname)): # Try to read OpenSSL's config file from a pickled copy f=open('%s.pickle' % (opensslcnfname),'rb') try: # first try to use the faster cPickle module from cPickle import load except ImportError: from pickle import load opensslcnf=load(f) f.close() else: # Parse OpenSSL's config file from source opensslcnf=openssl.cnf.OpenSSLConfigClass(opensslcnfname) pyca_section = opensslcnf.data.get('pyca',{}) openssl.bin_filename = pyca_section.get('OpenSSLExec','/usr/local/ssl/bin/openssl') if findoption(options,'--certfile')!=(): certfilename = findoption(options,'--certfile')[1] certfile = open(certfilename,'w') else: certfilename = '' if findoption(options,'--certdir')!=(): certdir = findoption(options,'--certdir')[1] if not os.path.exists(certdir) or not os.path.isdir(certdir): PrintUsage('Directory %s not exists or not a directory.' % (certdir)) sys.exit(1) else: certdir = '' if not certfilename and not certdir: # Set at least one usable default certfilename = './ca-certs.pem' certfile = open(certfilename,'w') ca_names = opensslcnf.sectionkeys.get('ca',[]) for ca_num in range(len(ca_names)): ca_name = ca_names[ca_num] ca = opensslcnf.getcadata(ca_name) if os.path.isfile(ca.certificate): cacert = openssl.cert.X509CertificateClass(ca.certificate) # Copy the CA certificate file to directory if certdir: # Convert character sets for dict in [cacert.issuer,cacert.subject]: for attr in dict.keys(): dict[attr] = charset.asn12iso(dict[attr]) # New filename for CA cert cacert_filename = '%(CN)s_%(OU)s_%(O)s_%(L)s_%(ST)s_%(C)s' % (cacert.subject) + os.path.splitext(ca.certificate)[1] # Copy the file shutil.copyfile(ca.certificate,os.path.join(certdir,cacert_filename)) # Create appropriate symlink symlinkname = os.path.join(certdir,'%s.0' % (cacert.hash)) try: os.symlink(cacert_filename,symlinkname) except OSError: sys.stderr.write('Warning: Could not create symbolic link.\n') # Append CA certificate file to single certificate file if certfilename: cacertfile = open(ca.certificate,'r') buf = cacertfile.read() while buf: certfile.write(buf) buf = cacertfile.read() cacertfile.close() else: sys.stderr.write('Warning: CA certificate file %s not found.\n') if certfilename: certfile.close() pyca-20031118/bin/ns-jsconfig.py0100755000076400001440000001214207301173255015605 0ustar michaelusers#!/usr/bin/python ######################################################################## # ns-jscertconfig.py # (c) by Michael Stroeder, michael@stroeder.com ######################################################################## __version__ = '0.6.6' ######################################################################## # This simple script generates Javascript code containing all CA certs # for a netscape.cfg ######################################################################## import sys, string, os, getopt, types, shutil def findoption(options,paramname): for i in options: if i[0]==paramname: return i return () def PrintUsage(ErrorMsg='',ErrorCode=1): script_name = string.split(sys.argv[0],os.sep)[-1] sys.stderr.write("""*** %s *** (C) by Michael Stroeder, 1999 usage: %s [options] Options: -h or --help Print out this message --config=[pathname] Pathname of OpenSSL configuration file. Default: /etc/openssl/openssl.cnf --pycalib=[directory] Specify directory containing the pyCA modules Default: /usr/local/pyca/pylib --nssecconf=[pathname] Pathname of Javascript file to output containing all CA certs for inclusion in netscape.cfg. The trust parameters are marked according to nsCertType. Self-signed certificates are marked with flag 'C' otherwise 'c'. Default: stdout --friendlynametemplate=[Python dict string] A Python string used as template for building friendly CA names. E.g. TestCA %%(CN)s %%(O)s Default: %%(CN)s """ % (script_name,script_name)) if ErrorMsg: sys.stderr.write('Error: %s\n' % ErrorMsg) sys.exit(ErrorCode) script_name=sys.argv[0] try: options,args=getopt.getopt(sys.argv[1:],'h',['help','config=','pycalib=','nssecconf=','friendlynametemplate=']) except getopt.error,e: PrintUsage(str(e)) if findoption(options,'-h')!=() or findoption(options,'--help')!=(): PrintUsage(script_name) if findoption(options,'--config')!=(): opensslcnfname = findoption(options,'--config')[1] else: opensslcnfname = os.environ.get('OPENSSL_CONF','/etc/openssl/openssl.cnf') if not os.path.isfile(opensslcnfname): PrintUsage('Config file %s not found.' % (opensslcnfname)) sys.exit(1) if findoption(options,'--pycalib')!=(): pycalib = findoption(options,'--pycalib')[1] else: pycalib = os.environ.get('PYCALIB','/usr/local/pyca/pylib') if not os.path.exists(pycalib) or not os.path.isdir(pycalib): PrintUsage('Module directory %s not exists or not a directory.' % (pycalib)) sys.exit(1) sys.path.append(pycalib) try: import openssl, charset except ImportError: PrintUsage('Required pyCA modules not found in directory %s!' % (pycalib)) # Read the configuration file if os.path.isfile('%s.pickle' % (opensslcnfname)): # Try to read OpenSSL's config file from a pickled copy f=open('%s.pickle' % (opensslcnfname),'rb') try: # first try to use the faster cPickle module from cPickle import load except ImportError: from pickle import load opensslcnf=load(f) f.close() else: # Parse OpenSSL's config file from source opensslcnf=openssl.cnf.OpenSSLConfigClass(opensslcnfname) pyca_section = opensslcnf.data.get('pyca',{}) openssl.bin_filename = pyca_section.get('OpenSSLExec','/usr/local/ssl/bin/openssl') if findoption(options,'--nssecconf')!=(): nssecconf = open(findoption(options,'--nssecconf')[1],'w') else: nssecconf = sys.stdout if findoption(options,'--friendlynametemplate')!=(): friendlynametemplate = findoption(options,'--friendlynametemplate')[1] else: friendlynametemplate = '%(CN)s' ca_names = opensslcnf.sectionkeys.get('ca',[]) nssecconf.write("""with (SecurityConfig) { function pyCACertConfig() {\n """) for ca_num in range(len(ca_names)): ca_name = ca_names[ca_num] ca = opensslcnf.getcadata(ca_name) if os.path.isfile(ca.certificate): cacert = openssl.cert.X509CertificateClass(ca.certificate) # Append CA certificate file to Javascript configuration file cacertfile = open(ca.certificate,'r') certbuf = [] line = cacertfile.readline() while line: certbuf.append('"%s\\n"' % (string.strip(line))) line = cacertfile.readline() cacertfile.close() if cacert.issuer==cacert.subject: ns_trustflag = 'C' else: ns_trustflag = 'c' if ca.nsCertType: ns_trustparams = ['','',''] if type(ca.nsCertType)==types.ListType: nsCertType = ca.nsCertType else: nsCertType = [ca.nsCertType] for nstype in nsCertType: if nstype in ['server','sslCA']: ns_trustparams[0]=ns_trustflag if nstype in ['email','client','emailCA']: ns_trustparams[1]=ns_trustflag if nstype in ['objsign','objCA']: ns_trustparams[2]=ns_trustflag else: ns_trustparams = [ns_trustflag,ns_trustflag,ns_trustflag] nssecconf.write("""CE_addCert%d = new Certificate( %s); if ( ! CE_addCert%d.isPerm ) { certDB.addCert(CE_addCert%d, "%s", "%s"); } """ % (ca_num,string.join(certbuf,'+\n'),ca_num,ca_num,string.join(ns_trustparams,','),friendlynametemplate % cacert.subject)) else: sys.stderr.write('Warning: CA certificate file %s not found.\n') nssecconf.write("""} pyCACertConfig() }""") nssecconf.close() pyca-20031118/conf/0040755000076400001440000000000007756443113013177 5ustar michaeluserspyca-20031118/conf/openssl.cnf0100644000076400001440000003776707745162000015362 0ustar michaelusers# # OpenSSL configuration file: Two-level hierarchy # # Root-+ # | # +-UserCerts (end user certs for S/MIME e-mail protection and # | client authentication) # | # +-AuthCerts (solely for strong authentication with SSL/TLS) # | # +-ServerCerts (solely for server certificates with SSL/TLS) # | # +-CodeSigning (solely for code signing, Authenticode etc.) RANDFILE = "$ENV::HOME/.rnd" oid_file = /etc/openssl/.oid oid_section = new_oids [ new_oids ] # We can add new OIDs in here for use by 'ca' and 'req'. # Add a simple OID like this: # testoid1=1.2.3.4 # Or use config file substitution like this: # testoid2=${testoid1}.5.6 dnQualifier = 2.5.4.46 surName = 2.5.4.4 givenName = 2.5.4.42 initials = 2.5.4.43 generationQualifier = 2.5.4.44 userID = 0.9.2342.19200300.100.1.1 #################################################################### [ ca ] Root = CA_Root EmailCerts = CA_EmailCerts AuthCerts = CA_AuthCerts CodeSigning = CA_CodeSigning ServerCerts = CA_ServerCerts #################################################################### [ CA_Root ] dir = /usr/local/myCA/Root# Where everything is kept certs = $dir/certs # Where the issued certs are kept crl_dir = $dir/crl # Where the issued crl are kept database = $dir/index.txt # database index file. new_certs_dir = $dir/newcerts # default place for new certs. pend_reqs_dir = "" # default place for new unconfirmed cert reqs. new_reqs_dir = "" # default place for new cert reqs. certificate = $dir/cacert.pem # The CA certificate serial = $dir/serial # The current serial number crl = $dir/crl.pem # The current CRL private_key = $dir/private/cakey.pem# The private key RANDFILE = $dir/private/.rand # private random number file default_days = 730 # how long to certify for default_crl_days= 5 # how long before next CRL default_md = sha1 # which md to use. preserve = no # keep passed DN ordering policy = policy_CA ca_x509_extfile = /etc/openssl/cacert_Root.cnf x509_extensions = x509v3_ext_CA # This section is only used for # displaying the params in ca-index.py [ CA_EmailCerts ] dir = /usr/local/myCA/EmailCerts # Where everything is kept certs = $dir/certs # Where the issued certs are kept crl_dir = $dir/crl # Where the issued crl are kept database = $dir/index.txt # database index file. new_certs_dir = $dir/newcerts # default place for new certs. pend_reqs_dir = $dir/pendreqs # default place for new unconfirmed cert reqs. new_reqs_dir = $dir/newreqs # default place for new cert reqs. certificate = $dir/cacert.pem # The CA certificate serial = $dir/serial # The current serial number crl = $dir/crl.pem # The current CRL private_key = $dir/private/cakey.pem# The private key RANDFILE = $dir/private/.rand # private random number file default_days = 200 # how long to certify for default_crl_days= 2 # how long before next CRL default_md = sha1 # which md to use. preserve = no # keep passed DN ordering policy = policy_EmailCerts x509_extensions = x509v3_ext_EmailCerts signedby = Root ca_x509_extfile = /etc/openssl/cacert_EmailCerts.cnf req = req_EmailCerts min_key_size = 768 [ CA_AuthCerts ] dir = /usr/local/myCA/AuthCerts # Where everything is kept certs = $dir/certs # Where the issued certs are kept crl_dir = $dir/crl # Where the issued crl are kept database = $dir/index.txt # database index file. new_certs_dir = $dir/newcerts # default place for new certs. pend_reqs_dir = $dir/pendreqs # default place for new unconfirmed cert reqs. new_reqs_dir = $dir/newreqs # default place for new cert reqs. certificate = $dir/cacert.pem # The CA certificate serial = $dir/serial # The current serial number crl = $dir/crl.pem # The current CRL private_key = $dir/private/cakey.pem# The private key RANDFILE = $dir/private/.rand # private random number file default_days = 200 # how long to certify for default_crl_days= 2 # how long before next CRL default_md = sha1 # which md to use. preserve = no # keep passed DN ordering policy = policy_AuthCerts x509_extensions = x509v3_ext_AuthCerts signedby = Root ca_x509_extfile = /etc/openssl/cacert_AuthCerts.cnf req = req_AuthCerts [ CA_CodeSigning ] dir = /usr/local/myCA/CodeSigning # Where everything is kept certs = $dir/certs # Where the issued certs are kept crl_dir = $dir/crl # Where the issued crl are kept database = $dir/index.txt # database index file. new_certs_dir = $dir/newcerts # default place for new certs. pend_reqs_dir = $dir/pendreqs # default place for new unconfirmed cert reqs. new_reqs_dir = $dir/newreqs # default place for new cert reqs. certificate = $dir/cacert.pem # The CA certificate serial = $dir/serial # The current serial number crl = $dir/crl.pem # The current CRL private_key = $dir/private/cakey.pem# The private key RANDFILE = $dir/private/.rand # private random number file default_days = 200 # how long to certify for default_crl_days= 5 # how long before next CRL default_md = sha1 # which md to use. preserve = no # keep passed DN ordering policy = policy_CodeSigning x509_extensions = x509v3_ext_CodeSigning signedby = Root ca_x509_extfile = /etc/openssl/cacert_CodeSigning.cnf req = req_EmailCerts [ CA_ServerCerts ] dir = /usr/local/myCA/ServerCerts # Where everything is kept certs = $dir/certs # Where the issued certs are kept crl_dir = $dir/crl # Where the issued crl are kept database = $dir/index.txt # database index file. new_certs_dir = $dir/newcerts # default place for new certs. pend_reqs_dir = $dir/pendreqs # default place for new unconfirmed cert reqs. new_reqs_dir = $dir/newreqs # default place for new cert reqs. certificate = $dir/cacert.pem # The CA certificate serial = $dir/serial # The current serial number crl = $dir/crl.pem # The current CRL private_key = $dir/private/cakey.pem# The private key RANDFILE = $dir/private/.rand # private random number file default_days = 60 # how long to certify for default_crl_days= 2 # how long before next CRL default_md = sha1 # which md to use. preserve = no # keep passed DN ordering policy = policy_ServerCerts x509_extensions = x509v3_ext_ServerCerts signedby = Root ca_x509_extfile = /etc/openssl/cacert_ServerCerts.cnf ########################### Policies ############################### [ policy_EmailCerts ] countryName = optional stateOrProvinceName = optional localityName = optional organizationName = supplied organizationalUnitName = optional commonName = supplied emailAddress = supplied [ policy_AuthCerts ] organizationName = supplied organizationalUnitName = optional userID = supplied #emailAddress = supplied [ policy_CodeSigning ] countryName = match stateOrProvinceName = match localityName = match organizationName = match organizationalUnitName = optional commonName = supplied userID = optional emailAddress = supplied [ policy_ServerCerts ] countryName = supplied stateOrProvinceName = optional localityName = supplied organizationName = supplied organizationalUnitName = optional commonName = supplied #################################################################### [ req ] default_bits = 1024 default_keyfile = privkey.pem distinguished_name = req_distinguished_name # attributes = req_attributes [ req_distinguished_name ] countryName = country ISO code countryName_default = DE countryName_min = 2 countryName_max = 2 countryName_regex = "[a-zA-Z][a-zA-Z]" stateOrProvinceName = State/Province Name stateOrProvinceName_default = "" localityName = Location localityName_default = Karlsruhe organizationName = Organization organizationName_default = "Your Organization" organizationalUnitName = Organizational Unit Name organizationalUnitName_default = Department One,Department Two,Department Three commonName = Common Name commonName_max = 64 emailAddress = Email Address emailAddress_default = "" emailAddress_max = 64 emailAddress_regex = "^([\w@.=/_ +-]+)@([\w-]+)(\.[\w-]+)*$" [ req_attributes ] challengePassword = A challenge password challengePassword_min = 4 challengePassword_max = 20 # unstructuredName = An optional company name #################################################################### [ req_EmailCerts ] distinguished_name = req_distinguished_name_EmailCerts [ req_distinguished_name_EmailCerts ] countryName = country ISO code countryName_default = "DE" countryName_min = 2 countryName_max = 2 countryName_regex = "[a-zA-Z][a-zA-Z]" stateOrProvinceName = State/Province Name stateOrProvinceName_default = "" localityName = Location localityName_default = Karlsruhe organizationName = Organization organizationName_default = "Your Organization" organizationalUnitName = Organizational Unit Name organizationalUnitName_default = Department One,Department Two,Department Three commonName = Common Name commonName_max = 64 emailAddress = Email Address emailAddress_default = "" emailAddress_max = 64 emailAddress_regex = "^([\w@.=/_ +-]+)@([\w-]+)(\.[\w-]+)*$" [ req_AuthCerts ] distinguished_name = req_distinguished_name_AuthCerts [ req_distinguished_name_AuthCerts ] organizationName = Organization organizationName_default = "Your Organization" userID = "User ID" userID_max = 8 emailAddress = Email Address emailAddress_default = "@ms.inka.de" emailAddress_max = 64 emailAddress_regex = "^([\w@.=/_ +-]+)@([\w-]+)(\.[\w-]+)*$" #################################################################### [ req_short_and_empty ] distinguished_name = req_distinguished_name_short_and_empty [ req_distinguished_name_short_and_empty ] countryName = country ISO code countryName_min = 2 countryName_max = 2 countryName_regex = "[a-zA-Z][a-zA-Z]" stateOrProvinceName = State/Province Name localityName = Location organizationName = Organization organizationalUnitName = Organizational Unit Name commonName = Common Name commonName_max = 64 emailAddress = Email Address emailAddress_max = 64 emailAddress_regex = "^([\w@.=/_ +-]+)@([\w-]+)(\.[\w-]+)*$" ############################################################################## [ x509v3_ext_CA ] basicConstraints = CA:true keyUsage = cRLSign,keyCertSign crlDistributionPoints = URI:"http://localhost/pyca/get-cert.py/Root/crl.crl" nsComment = "This certificate is used for issueing sub-CA certs." nsBaseUrl = "https://localhost/" nsCaRevocationUrl = pyca/get-cert.py/Root/crl.crl nsRevocationUrl = pyca/ns-check-rev.py/Root? nsRenewalUrl = pyca/ns-renewal.py/Root? nsCaPolicyUrl = TestCA/policy/CA-policy.html [ x509v3_ext_EmailCerts ] # PKIX extensions subjectKeyIdentifier = hash authorityKeyIdentifier = keyid:always,issuer:always keyUsage = nonRepudiation,digitalSignature,keyEncipherment extendedKeyUsage = emailProtection issuerAltName = URI:"https://localhost/pyca/get-cert.py/EmailCerts/ca.crt" crlDistributionPoints = URI:"http://localhost/pyca/get-cert.py/EmailCerts/crl.crl" subjectAltName = email:copy # Netscape-specific extensions nsComment = "This certificate is used for e-mail." nsBaseUrl = "https://localhost/" nsCaRevocationUrl = pyca/get-cert.py/EmailCerts/crl.crl nsRevocationUrl = pyca/ns-check-rev.py/EmailCerts? nsRenewalUrl = pyca/ns-renewal.py/EmailCerts? nsCaPolicyUrl = TestCA/policy/EmailCerts-policy.html nsCertType = email [ x509v3_ext_AuthCerts ] # PKIX extensions subjectKeyIdentifier = hash authorityKeyIdentifier = keyid:always,issuer:always keyUsage = digitalSignature extendedKeyUsage = clientAuth issuerAltName = URI:"https://localhost/pyca/get-cert.py/AuthCerts/ca.crt" crlDistributionPoints = URI:"http://localhost/pyca/get-cert.py/AuthCerts/crl.crl" # Netscape-specific extensions nsComment = "This certificate is used for strong authentication." nsBaseUrl = "https://localhost/" nsCaRevocationUrl = pyca/get-cert.py/AuthCerts/crl.crl nsRevocationUrl = pyca/ns-check-rev.py/AuthCerts? nsRenewalUrl = pyca/ns-renewal.py/AuthCerts? nsCaPolicyUrl = TestCA/policy/AuthCerts-policy.html nsCertType = client [ x509v3_ext_CodeSigning ] # PKIX extensions subjectKeyIdentifier = hash authorityKeyIdentifier = keyid:always,issuer:always keyUsage = digitalSignature extendedKeyUsage = codeSigning issuerAltName = URI:"https://localhost/pyca/get-cert.py/CodeSigning/ca.crt" crlDistributionPoints = URI:"http://localhost/pyca/get-cert.py/CodeSigning/crl.crl" # Netscape-specific extensions nsComment = "This certificate is used for CodeSigning signing." nsBaseUrl = "https://localhost/" nsCaRevocationUrl = pyca/get-cert.py/CodeSigning/crl.crl nsRevocationUrl = pyca/ns-check-rev.py/CodeSigning? nsRenewalUrl = pyca/ns-renewal.py/CodeSigning? nsCaPolicyUrl = TestCA/policy/CodeSigning-policy.html nsCertType = objsign [ x509v3_ext_ServerCerts ] # PKIX extensions subjectKeyIdentifier = hash authorityKeyIdentifier = keyid:always,issuer:always crlDistributionPoints = URI:"http://localhost/pyca/get-cert.py/ServerCerts/crl.crl" keyUsage = keyEncipherment extendedKeyUsage = serverAuth,nsSGC,msSGC # Netscape-specific extensions nsComment = "This certificate is used for SSL ServerCerts." nsBaseUrl = "https://localhost/" nsCaRevocationUrl = pyca/get-cert.py/ServerCerts/crl.crl nsRevocationUrl = pyca/ns-check-rev.py/ServerCerts? nsRenewalUrl = pyca/ns-renewal.py/ServerCerts? nsCaPolicyUrl = TestCA/policy/ServerCerts-policy.html nsCertType = server # [ pyca ] is a proprietary, non-OpenSSL section for the pyca-package # on http://www.pyca.de/ [ pyca ] caCertFormat = DER # Base-URL for the other URL addresses # This is meant as fallback option if the CA-specific # attribute nsBaseUrl is not set nsBaseUrl = "https://localhost/" # Relative URL address of ca-index.py nsCAIndexUrl = pyca/ca-index.py # Relative URL address of client-enroll.py nsEnrollUrl = pyca/client-enroll.py # Relative URL address of get-cert.py nsGetCertUrl = pyca/get-cert.py # Relative URL address of view-cert.py nsViewCertUrl = pyca/view-cert.py # Pathname of the openssl executable OpenSSLExec = /usr/bin/openssl # Username of caadmin userCAAdmin = caadmin # Username of WWW Server userWWWRun = wwwrun # Username of mail delivery demon userMailDaemon = daemon # Preferred HTTP method for submitting form parameters ScriptMethod = POST # Relative URL address of help texts (e.g. client-enroll-help.html) HelpUrl = inkasite/python/pyca/help/ # The default SMTP mail relay MailRelay = localhost # Directory for temporary files TmpDir = /tmp # Path to file for log output of ca-certreq-mail.py. # The directory must be writeable for the user defined with parameter # userMailDaemon caCertConfirmReqLog = /var/log/pyca/ca-certreq-mail.out # Pathname for the error log file. # stderr is used as default, if empty or not defined. #ErrorLog = /var/log/pyca/httpd_error_log # E-mail address of the mail dialogue script for certificate requests # if empty, no mail dialogue is initiated. caCertReqMailAdr = confirm-cert-req@ms.inka.de # Central e-mail address of the CA's administrator. # This is used as From: address if the subject name of a CA cert does # not contain an Email attribute. caAdminMailAdr = caadmin@ms.inka.de # Amount of time [h] how long a pending certificate request is stored # in caPendCertReqDir without being confirmed by e-mail. # Set to zero (this is the default) to disable automatic deletion of # unconfirmed certificate requests by ca-cycle-pub.py. caPendCertReqValid = 24 # List CA names for which certificate requests can only be created # from an internal network (see caInternalIPAdr and caInternalDomains). # The integrity of your PKI should not be based on such mechanisms! caInternalCertTypes = CodeSigning # List of network addresses/-masks which are considered internal caInternalIPAdr = 127.0.0.0/255.0.0.0,10.0.0.0/255.0.0.0 # List of email address domains which are handled as internal caInternalDomains = pyca.de # List of CA names for which handling of intermediate CA certs should # be provided. caIntermediateCACerts = EmailCerts # All parameters for tag (quote " with ´´). htmlBodyParam = ´TEXT="#000000" LINK="Red" VLINK="Green" BGCOLOR="#FFFFFF"´ pyca-20031118/conf/cacert_ServerCerts.cnf0100644000076400001440000000321607660756463017467 0ustar michaelusersextensions = extension_section x509_extensions = extension_section [ req ] default_bits = 1024 distinguished_name = req_dn encrypt_rsa_key = yes [ req_dn ] #1.domainComponent = DNS domain Component #1.domainComponent_default = "com" #2.domainComponent = DNS domain Component #2.domainComponent_default = "localhost" countryName = ISO country code countryName_default = "XX" stateOrProvinceName = State/Province Name stateOrProvinceName_default = "" localityName = Location localityName_default = "" organizationName = Organization organizationName_default = "Looser Org." organizationalUnitName = Organizational Unit Name organizationalUnitName_default = "bad CA!" commonName = Common Name commonName_default = "ServerCerts TestCA" commonName_max = 64 [extension_section] # PKIX basicConstraints=critical,CA:true subjectKeyIdentifier=hash authorityKeyIdentifier=keyid:always,issuer:always keyUsage = cRLSign, keyCertSign extendedKeyUsage = nsSGC,msSGC subjectAltName = URI:"http://localhost/pyca/get-cert.py/ServerCerts/ca.crt" issuerAltName = issuer:copy crlDistributionPoints = URI:"http://localhost/pyca/get-cert.py/Root/crl.crl" #certificatePolicies=ia5org,@polsect # Netscape cert extensions nsComment = "This CA issues SSL server certificates." nsCaPolicyUrl = "https://localhost/ServerCerts/policy.html" nsCertType = sslCA nsCaRevocationUrl = "http://localhost/pyca/get-cert.py/Root/crl.crl" [ polsect ] #policyIdentifier=1.2.3.4 #CPS="https://localhost/ServerCerts/policy.html" #userNotice=@notice [ notice ] explicitText="This CA issues SSL server certificates." organization="Looser Org. with bad CA admin." noticeNumbers=4, 2 pyca-20031118/conf/ssl_server_csr.cnf0100644000076400001440000000207407753215421016721 0ustar michaelusers# This is a config template for generating CSRs for # SSL server certs with OpenSSL. # # Usage: # openssl req -new -config ~michael/Proj/python/pyca/conf/ssl_server_csr.cnf # # You probably want to adjust the settings to reflect your # domain, company name etc. [ req ] default_bits = 1024 distinguished_name = req_dn encrypt_rsa_key = yes [ req_dn ] countryName = ISO country code countryName_default = "DE" stateOrProvinceName = State/Province Name stateOrProvinceName_default = "" localityName = Location localityName_default = "Karlsruhe" organizationName = Organization organizationName_default = "stroeder.com" organizationalUnitName = Organizational Unit Name organizationalUnitName_default = "ITS" commonName = FQDN of SSL Server commonName_default = "" commonName_max = 64 #emailAddress = E-Mail address #emailAddress_default = "" #emailAddress_max = 64 pyca-20031118/conf/cacert_Root.cnf0100644000076400001440000000331507660756463016143 0ustar michaelusersextensions = extension_section x509_extensions = extension_section [ req ] default_bits = 1024 distinguished_name = req_dn encrypt_rsa_key = yes [ req_dn ] #1.domainComponent = DNS domain Component #1.domainComponent_default = "com" #2.domainComponent = DNS domain Component #2.domainComponent_default = "localhost" countryName = ISO country code countryName_default = "XX" stateOrProvinceName = State/Province Name stateOrProvinceName_default = "" localityName = Location localityName_default = "" organizationName = Organization organizationName_default = "Looser Org." organizationalUnitName = Organizational Unit Name organizationalUnitName_default = "bad CA!" commonName = Common Name commonName_default = "Root TestCA" commonName_max = 64 [extension_section] # Netscape cert extensions nsCertType = sslCA,emailCA,objCA nsComment = "This Root CA issues sub-CA certs of different policies and has no contact with end-entities." nsCaPolicyUrl = "https://localhost/Root/policy.html" nsCaRevocationUrl = "http://localhost/pyca/get-cert.py/Root/crl.crl" # PKIX basicConstraints=critical,CA:true subjectKeyIdentifier=hash authorityKeyIdentifier=keyid:always,issuer:always keyUsage = cRLSign,keyCertSign extendedKeyUsage = nsSGC,msSGC subjectAltName = URI:"http://localhost/pyca/get-cert.py/Root/ca.crt" crlDistributionPoints = URI:"http://localhost/pyca/get-cert.py/Root/crl.crl" #certificatePolicies=ia5org,@polsect [ polsect ] #policyIdentifier=1.2.3.4 #CPS="https://localhost/Root/policy.html" #userNotice=@notice [ notice ] explicitText="This Root CA issues sub-CA certs of different policies and has no contact with end-entities." organization="Looser Org. with bad CA admin." noticeNumbers=4, 2 pyca-20031118/conf/cacert_CodeSigning.cnf0100644000076400001440000000314707660756463017414 0ustar michaelusersextensions = extension_section x509_extensions = extension_section [ req ] default_bits = 1024 distinguished_name = req_dn encrypt_rsa_key = yes [ req_dn ] #1.domainComponent = DNS domain Component #1.domainComponent_default = "com" #2.domainComponent = DNS domain Component #2.domainComponent_default = "localhost" countryName = ISO country code countryName_default = "XX" stateOrProvinceName = State/Province Name stateOrProvinceName_default = "" localityName = Location localityName_default = "" organizationName = Organization organizationName_default = "Looser Org." organizationalUnitName = Organizational Unit Name organizationalUnitName_default = "bad CA!" commonName = Common Name commonName_default = "CodeSigning TestCA" commonName_max = 64 [extension_section] # PKIX basicConstraints=critical,CA:true subjectKeyIdentifier=hash authorityKeyIdentifier=keyid:always,issuer:always keyUsage = cRLSign, keyCertSign subjectAltName = URI:"http://localhost/pyca/get-cert.py/CodeSigning/ca.crt" issuerAltName = issuer:copy crlDistributionPoints = URI:"http://localhost/pyca/get-cert.py/Root/crl.crl" #certificatePolicies=ia5org,@polsect # Netscape cert extensions nsComment = "This CA issues code signing certificates." nsCaPolicyUrl = "https://localhost/CodeSigning/policy.html" nsCertType = objCA nsCaRevocationUrl = "http://localhost/pyca/get-cert.py/Root/crl.crl" [ polsect ] #policyIdentifier=1.2.3.4 #CPS="https://localhost/CodeSigning/policy.html" #userNotice=@notice [ notice ] explicitText="This CA issues code signing certificates." organization="Looser Org. with bad CA admin." noticeNumbers=4, 2 pyca-20031118/conf/cacert_AuthCerts.cnf0100644000076400001440000000314707660756463017125 0ustar michaelusersextensions = extension_section x509_extensions = extension_section [ req ] default_bits = 1024 distinguished_name = req_dn encrypt_rsa_key = yes [ req_dn ] #1.domainComponent = DNS domain Component #1.domainComponent_default = "com" #2.domainComponent = DNS domain Component #2.domainComponent_default = "localhost" countryName = ISO country code countryName_default = "XX" stateOrProvinceName = State/Province Name stateOrProvinceName_default = "" localityName = Location localityName_default = "" organizationName = Organization organizationName_default = "Looser Org." organizationalUnitName = Organizational Unit Name organizationalUnitName_default = "bad CA!" commonName = Common Name commonName_default = "AuthCerts TestCA" commonName_max = 64 [extension_section] # PKIX basicConstraints=critical,CA:true subjectKeyIdentifier=hash authorityKeyIdentifier=keyid:always,issuer:always keyUsage = cRLSign, keyCertSign subjectAltName = URI:"http://localhost/pyca/get-cert.py/AuthCerts/ca.crt" issuerAltName = issuer:copy crlDistributionPoints = URI:"http://localhost/pyca/get-cert.py/Root/crl.crl" #certificatePolicies=ia5org,@polsect # Netscape cert extensions nsComment = "This CA issues SSL client certificates." nsCaPolicyUrl = "https://localhost/AuthCerts/policy.html" nsCertType = sslCA nsCaRevocationUrl = "http://localhost/pyca/get-cert.py/Root/crl.crl" [ polsect ] #policyIdentifier=1.2.3.4 #CPS="https://localhost/AuthCerts/policy.html" #userNotice=@notice [ notice ] explicitText="This CA issues SSL client certificates." organization="Looser Org. with bad CA admin." noticeNumbers=4, 2 pyca-20031118/conf/cacert_EmailCerts.cnf0100644000076400001440000000313107660756463017244 0ustar michaelusersextensions = extension_section x509_extensions = extension_section [ req ] default_bits = 1024 distinguished_name = req_dn encrypt_rsa_key = yes [ req_dn ] #1.domainComponent = DNS domain Component #1.domainComponent_default = "com" #2.domainComponent = DNS domain Component #2.domainComponent_default = "localhost" countryName = ISO country code countryName_default = "XX" stateOrProvinceName = State/Province Name stateOrProvinceName_default = "" localityName = Location localityName_default = "" organizationName = Organization organizationName_default = "Looser Org." organizationalUnitName = Organizational Unit Name organizationalUnitName_default = "bad CA!" commonName = Common Name commonName_default = "EmailCerts TestCA" commonName_max = 64 [extension_section] # PKIX basicConstraints=critical,CA:true subjectKeyIdentifier=hash authorityKeyIdentifier=keyid:always,issuer:always keyUsage = cRLSign, keyCertSign subjectAltName = URI:"http://localhost/pyca/get-cert.py/EmailCerts/ca.crt" issuerAltName = issuer:copy crlDistributionPoints = URI:"http://localhost/pyca/get-cert.py/Root/crl.crl" #certificatePolicies=ia5org,@polsect # Netscape cert extensions nsComment = "This CA issues e-mail certificates." nsCaPolicyUrl = "https://localhost/EmailCerts/policy.html" nsCertType = emailCA nsCaRevocationUrl = "http://localhost/pyca/get-cert.py/Root/crl.crl" [ polsect ] #policyIdentifier=1.2.3.4 #CPS="https://localhost/EmailCerts/policy.html" #userNotice=@notice [ notice ] explicitText="This CA issues e-mail certificates." organization="Looser Org. with bad CA admin." noticeNumbers=4, 2 pyca-20031118/docs/0040755000076400001440000000000007756443113013202 5ustar michaeluserspyca-20031118/help/0040755000076400001440000000000007756443113013202 5ustar michaeluserspyca-20031118/sbin/0040755000076400001440000000000007756443113013205 5ustar michaeluserspyca-20031118/sbin/ca-revoke.py0100755000076400001440000001376707301173261015437 0ustar michaelusers#!/usr/bin/python ######################################################################## # ca-revoke.py # (c) by Michael Stroeder, michael@stroeder.com ######################################################################## __version__ = '0.6.6' ######################################################################## # This script is used to revoke certificates from the command line # ca-revoke.py -h prints usage of parameters. ######################################################################## import sys, string, os, smtplib, getopt from time import time,localtime,strftime,mktime def findoption(options,paramname): for i in options: if i[0]==paramname: return i return () def PrintUsage(ErrorMsg='',ErrorCode=1): script_name = string.split(sys.argv[0],os.sep)[-1] sys.stderr.write("""*** %s *** (C) by Michael Stroeder, 1999 usage: %s [options] Options: -h or --help Print out this message --config=[pathname] Pathname of OpenSSL configuration file. You may also use env variable OPENSSL_CONF. Default: /etc/openssl/openssl.cnf --pycalib=[directory] Specify directory containing the pyCA modules You may also use env variable PYCALIB. Default: /usr/local/pyca/pylib --name=[CA name] Name of CA in section [ca] of OpenSSL config. --serial=[hex number] Serial number of certificate to revoke. """ % (script_name,script_name)) if ErrorMsg: sys.stderr.write('Error: %s\n' % ErrorMsg) sys.exit(ErrorCode) ######################################################################## # Main ######################################################################## script_name=sys.argv[0] try: options,args=getopt.getopt(sys.argv[1:],'h',['help','config=','pycalib=','name=','serial=']) except getopt.error,e: PrintUsage(str(e)) if findoption(options,'-h')!=() or findoption(options,'--help')!=(): PrintUsage() if findoption(options,'--config')!=(): opensslcnfname = findoption(options,'--config')[1] else: opensslcnfname = os.environ.get('OPENSSL_CONF','/etc/openssl/openssl.cnf') if not os.path.isfile(opensslcnfname): PrintUsage('Config file %s not found.' % (opensslcnfname)) if findoption(options,'--pycalib')!=(): pycalib = findoption(options,'--pycalib')[1] else: pycalib = os.environ.get('PYCALIB','/usr/local/pyca/pylib') if not os.path.exists(pycalib) or not os.path.isdir(pycalib): PrintUsage('Module directory %s not exists or not a directory.' % (pycalib)) sys.path.append(pycalib) try: import openssl,charset from openssl.db import \ empty_DN_dict, \ DB_type,DB_exp_date,DB_rev_date,DB_serial,DB_file,DB_name,DB_number, \ DB_TYPE_REV,DB_TYPE_EXP,DB_TYPE_VAL, \ dbtime2tuple,GetEntriesbyDN,SplitDN except ImportError: PrintUsage('Required pyCA modules not found in directory %s!' % (pycalib)) # Read the configuration file if os.path.isfile('%s.pickle' % (opensslcnfname)): # Try to read OpenSSL's config file from a pickled copy f=open('%s.pickle' % (opensslcnfname),'rb') try: # first try to use the faster cPickle module from cPickle import load except ImportError: from pickle import load opensslcnf=load(f) f.close() else: # Parse OpenSSL's config file from source opensslcnf=openssl.cnf.OpenSSLConfigClass(opensslcnfname) pyca_section = opensslcnf.data.get('pyca',{}) ca_names = opensslcnf.sectionkeys.get('ca',[]) openssl.bin_filename = pyca_section.get('OpenSSLExec','/usr/local/ssl/bin/openssl') if not os.path.isfile(openssl.bin_filename): sys.stderr.write('Did not find OpenSSL executable %s.\n' % (openssl.bin_filename)) sys.exit(1) if findoption(options,'--name')!=(): ca_name = findoption(options,'--name')[1] if not ca_name in ca_names: PrintUsage('Wrong CA name.\nCA names listed in the current configuration:\n%s.' % string.join(ca_names,', ')) else: PrintUsage('You have to provide a name of a CA definition.') if findoption(options,'--serial')!=(): try: serial = string.atoi(findoption(options,'--serial')[1],16) except ValueError: PrintUsage('No valid serial number.') else: PrintUsage('You have to provide the serial number of the certificate you want to revoke.') ca = opensslcnf.getcadata(ca_name) sys.stdout.write('Searching database %s for certificate %x...\n' % (ca.database,serial)) ca_db = openssl.db.OpenSSLcaDatabaseClass(ca.database) result = ca_db.GetEntrybySerial(serial) if result: sys.stdout.write("""Found the following certificate: Serial number: %s Distinguished Name: %s """ % (result[DB_serial],charset.asn12iso(result[DB_name]))) if result[DB_type]==DB_TYPE_REV: sys.stdout.write('Certificate already revoked since %s.\n' % strftime('%d.%m.%Y',localtime(mktime(dbtime2tuple(result[DB_rev_date]))))) sys.exit(0) elif result[DB_type]==DB_TYPE_EXP: sys.stdout.write('Certificate already expired since %s.\n' % strftime('%d.%m.%Y',localtime(mktime(dbtime2tuple(result[DB_exp_date]))))) sys.exit(0) elif result[DB_type]==DB_TYPE_VAL: sys.stdout.write('Valid until %s.\n\nRevoke the certificate? (y/n) ' % strftime('%d.%m.%Y',localtime(mktime(dbtime2tuple(result[DB_exp_date]))))) answer = sys.stdin.readline() if string.lower(string.strip(answer))=='y': ca_db.Revoke(serial) sys.stdout.write('Certificate %x in %s marked as revoked.\n' % (serial,ca_name)) # CA's private key present <=> we are on the private CA system if os.path.isfile(ca.certificate) and os.path.isfile(ca.private_key): sys.stdout.write('Issue new CRL? (y/n) ') answer = sys.stdin.readline() if string.lower(string.strip(answer))=='y': sys.stdout.write('Issueing new CRL %s.\n' % (ca.crl)) rc = os.system('%s ca -config %s -name %s -gencrl -out %s' % \ (openssl.bin_filename,opensslcnfname,ca.sectionname,ca.crl)) if rc: sys.stderr.write('Error %d creating CRL %s.\n' % (rc,ca.crl)) else: raise ValueError, 'Unknown type field %s in certificate database.' % result[DB_type] else: PrintUsage('No certificate found in "%s" with serial %x.\n' % (ca_name,serial)) pyca-20031118/sbin/ca-make.py0100755000076400001440000003072307475760310015061 0ustar michaelusers#!/usr/bin/python """ ca-make.py - boot-strap of certificate authorities (c) by Michael Stroeder, michael@stroeder.com This script creates if non-existent (in the order given below, does not overwrite existing files with file length > 0): Directory structure: dir Where everything is kept certs Where the issued certs are kept new_certs_dir default place for new certs. crl_dir Where the issued crl are kept Files: database database index file. serial The current serial number Certificate files: private_key The private key of the CA certificate The CA certificate """ __version__ = '0.6.6' import sys, string, os, stat, pwd, grp, getopt, time def filenotvalid(pathname): return not os.path.isfile(pathname) or os.stat(pathname)[stat.ST_SIZE]==0 def CheckedMakeDir(dirname,perms=0,uid=0,gid=0): if not dirname: return if os.path.exists(dirname): # Directory does already exist if not os.path.isdir(dirname): sys.stderr.write('Warning: %s already exists but is no directory.\n' % (dirname)) else: # Create directory try: os.makedirs(dirname) sys.stdout.write('Created directory %s\n' % (dirname)) except OSError: sys.stderr.write('Error: Could not create directory %s.\n' % (dirname)) return # Get current file stat info fstat = os.stat(dirname) if perms: os.chmod(dirname,perms) sys.stdout.write('Changed permissions of %s to %o\n' % (dirname,perms)) if (uid and fstat[stat.ST_UID]!=uid) or \ (gid and fstat[stat.ST_GID]!=gid): if not uid: uid=fstat[stat.ST_UID] if not gid: gid=pwd.getpwuid(uid)[3] os.chown(dirname,uid,gid) sys.stdout.write('Changed owner/group of %s to %s.%s\n' % (dirname,pwd.getpwuid(uid)[0],grp.getgrgid(gid)[0])) def findoption(options,paramname): for i in options: if i[0]==paramname: return i return () def PrintUsage(ErrorMsg='',ErrorCode=1): script_name = string.split(sys.argv[0],os.sep)[-1] sys.stderr.write("""*** %s *** (C) by Michael Stroeder, 1999 usage: %s [options] Options: -h or --help Print out this message --config=[pathname] Pathname of OpenSSL configuration file. You may also use env variable OPENSSL_CONF. Default: /etc/openssl/openssl.cnf --pycalib=[directory] Specify directory containing the pyCA modules Default: /usr/local/pyca/pylib """ % (script_name,script_name)) if ErrorMsg: sys.stderr.write('Error: %s\n' % ErrorMsg) sys.exit(ErrorCode) ######################################################################## # Main ######################################################################## script_name=sys.argv[0] try: options,args=getopt.getopt(sys.argv[1:],'h',['help','config=','pycalib=']) except getopt.error,e: PrintUsage(str(e)) if findoption(options,'-h')!=() or findoption(options,'--help')!=(): PrintUsage() if findoption(options,'--config')!=(): opensslcnfname = findoption(options,'--config')[1] else: opensslcnfname = os.environ.get('OPENSSL_CONF','/etc/openssl/openssl.cnf') if not os.path.isfile(opensslcnfname): PrintUsage('Config file %s not found.' % (opensslcnfname)) if findoption(options,'--pycalib')!=(): pycalib = findoption(options,'--pycalib')[1] else: pycalib = os.environ.get('PYCALIB','/usr/local/pyca/pylib') if not os.path.exists(pycalib) or not os.path.isdir(pycalib): PrintUsage('Module directory %s not exists or not a directory.' % (pycalib)) sys.path.append(pycalib) try: import openssl, charset except ImportError: PrintUsage('Required pyCA modules not found in directory %s!' % (pycalib)) # Read the configuration file if os.path.isfile('%s.pickle' % (opensslcnfname)): # Try to read OpenSSL's config file from a pickled copy f=open('%s.pickle' % (opensslcnfname),'rb') try: # first try to use the faster cPickle module from cPickle import load except ImportError: from pickle import load opensslcnf=load(f) f.close() else: # Parse OpenSSL's config file from source opensslcnf=openssl.cnf.OpenSSLConfigClass(opensslcnfname) pyca_section = opensslcnf.data.get('pyca',{}) openssl.bin_filename = pyca_section.get('OpenSSLExec','/usr/local/ssl/bin/openssl') if not os.path.isfile(openssl.bin_filename): PrintUsage('Did not find OpenSSL executable %s.' % (openssl.bin_filename)) OpenSSLExec = openssl.bin_filename currentusername = pwd.getpwuid(os.getuid())[0] # Getting UIDs # Set current UID as default uidCAAdmin = pwd.getpwnam(pyca_section.get('userCAAdmin',currentusername))[2] uidMailDaemon = pwd.getpwnam(pyca_section.get('userMailDaemon',currentusername))[2] uidWWWRun = pwd.getpwnam(pyca_section.get('userWWWRun',currentusername))[2] gidCAAdmin = pwd.getpwuid(uidCAAdmin)[3] gidMailDaemon = pwd.getpwuid(uidMailDaemon)[3] gidWWWRun = pwd.getpwuid(uidWWWRun)[3] ca_names = opensslcnf.sectionkeys.get('ca',[]) sys.stdout.write(""" ############################################################# # Create directories and various files ############################################################# """) for ca_name in ca_names: sys.stdout.write('\nProcessing %s\n' % ca_name) ca = opensslcnf.getcadata(ca_name) # Create sub-directories CheckedMakeDir(ca.dir,perms=0755,uid=uidCAAdmin,gid=gidCAAdmin) CheckedMakeDir(ca.certs,perms=0755,uid=uidCAAdmin,gid=gidCAAdmin) CheckedMakeDir(ca.new_certs_dir,perms=0700,uid=uidCAAdmin,gid=gidCAAdmin) CheckedMakeDir(ca.crl_dir,perms=0755,uid=uidCAAdmin,gid=gidCAAdmin) if ca.pend_reqs_dir==ca.new_reqs_dir: CheckedMakeDir(ca.new_reqs_dir,perms=0370,uid=uidWWWRun,gid=gidCAAdmin) else: CheckedMakeDir(ca.pend_reqs_dir,perms=0370,uid=uidWWWRun,gid=gidMailDaemon) CheckedMakeDir(ca.new_reqs_dir,perms=0370,uid=uidMailDaemon,gid=gidCAAdmin) CheckedMakeDir(ca.old_reqs_dir,perms=0700,uid=uidCAAdmin,gid=gidCAAdmin) CheckedMakeDir(os.path.dirname(ca.certificate),perms=0755,uid=uidCAAdmin,gid=gidCAAdmin) if os.path.isfile(ca.certificate): # In any case we set permission and ownership of # CA certificate file if already existent os.chown(ca.certificate,uidCAAdmin,gidCAAdmin) os.chmod(ca.certificate,0444) CheckedMakeDir(os.path.dirname(ca.private_key),perms=0700,uid=uidCAAdmin,gid=gidCAAdmin) if os.path.isfile(ca.private_key): # In any case we set permission and ownership of # CA private key file if existent os.chown(ca.private_key,uidCAAdmin,gidCAAdmin) os.chmod(ca.private_key,0400) # database: database index file if not os.path.isfile(ca.database): sys.stdout.write('Creating database file %s\n' % (ca.database)) file=open(ca.database,'w') file.write('') file.close() os.chown(ca.database,uidCAAdmin,gidCAAdmin) os.chmod(ca.database,0644) # serial: next serial number for issueing certificates if filenotvalid(ca.serial): sys.stdout.write('Creating serial file %s\n' % (ca.serial)) file=open(ca.serial,'w') file.write('01\n') file.close() os.chown(ca.serial,uidCAAdmin,gidCAAdmin) os.chmod(ca.serial,0600) os.setgid(gidCAAdmin) os.setuid(uidCAAdmin) sys.stdout.write(""" ############################################################# # create self-signed CA certs or certificate requests #############################################################\n Give passwords for each CAs here. """) subca = [] for ca_name in ca_names: sys.stdout.write('\nProcessing %s\n' % ca_name) ca = opensslcnf.getcadata(ca_name) if ca.signedby: # Add CA to list of sub-CAs to be signed late subca.append(ca_name) if filenotvalid('%s-req' % ca.certificate) and filenotvalid(ca.private_key): sys.stdout.write('Creating certificate request %s with private key %s.\n' % (ca.certificate,ca.private_key)) if not ca.ca_reqfile: ca.ca_reqfile = ca.ca_x509_extfile if not ca.ca_reqfile: ca.ca_reqfile = opensslcnfname rc = os.system('%s req -config %s -new -outform pem -out %s-req -keyout %s' % \ (OpenSSLExec,ca.ca_reqfile,ca.certificate,ca.private_key)) os.chmod(ca.private_key,0400) if rc: sys.stderr.write('Error %d creating CA cert request %s-req.\n' % (rc,ca.certificate)) if filenotvalid(ca.certificate) and not ca.signedby: sys.stdout.write('How many days should this certificate be valid (minimum=%d, default=%d days): ' % (ca.default_days+1,2*ca.default_days+1)) days = string.strip(sys.stdin.readline()) if not days: days = 2*ca.default_days+1 rc = os.system('%s x509 -req -inform pem -in %s-req -outform pem -out %s -signkey %s -days %s -extfile %s' % \ (OpenSSLExec,ca.certificate,ca.certificate,ca.private_key,days,ca.ca_x509_extfile)) if rc: sys.stderr.write('Error %d self-signing CA cert %s.\n' % (rc,ca.certificate)) if subca: sys.stdout.write(""" ############################################################# # Create certs of sub-CAs #############################################################\n Use passwords of parent CAs here.\n """) for ca_name in subca: sys.stdout.write('\nProcessing %s\n' % ca_name) # Get the sub-CA's config data subca = opensslcnf.getcadata(ca_name) # Check if signedby points to a valid CA section name if not subca.signedby in ca_names: sys.stderr.write('CA name "%s" given in signedby parameter of section [%s] not found.\n' % (subca.signedby,subca.sectionname)) sys.exit(1) # Get the issuer's CA config data ca = opensslcnf.getcadata(subca.signedby) # Check if issuer's certificate and key files are present if filenotvalid(ca.certificate) or filenotvalid(ca.private_key): sys.stderr.write("""CA certificate or key file of issuer %s not found or zero-length. Check the files %s and %s. """ % (subca.signedby,ca.certificate,ca.private_key)) sys.exit(1) # Check if issuer certificate is valid at current time gmt = time.time() ca_cert = openssl.cert.X509CertificateClass(ca.certificate) if gmt+86400*ca.default_days>ca_cert.notAfter_secs: sys.stderr.write("""Certificate of issueing parent CA "%s" is not valid until %s. You can either set parameter default_days<=%d in section [%s] or issue a new parent CA cert. """ % (ca.name,time.strftime('%Y-%m-%d %H:%M',time.gmtime(gmt+86400*ca.default_days)),(ca_cert.notAfter_secs-gmt)/86400,ca.sectionname)) sys.exit(1) # Create the new sub-CA certificate if there's no older file in the way if filenotvalid(subca.certificate): sys.stdout.write('Creating sub-CA certificate %s with issuer "%s".\n' % (subca.certificate,ca.name)) rc = os.system('%s x509 -req -inform pem -in %s-req -outform pem -out %s -CA %s -CAkey %s -CAserial %s -days %s -extfile %s' % \ (OpenSSLExec,subca.certificate,subca.certificate,ca.certificate,ca.private_key,ca.serial,ca.default_days,subca.ca_x509_extfile)) if rc: sys.stderr.write('Error %d issueing CA cert %s.\n' % (rc,ca.certificate)) else: sys.stdout.write('Sub-CA certificate file %s already exists. Skipping...\n' % (subca.certificate)) sys.stdout.write(""" ############################################################# # Verifying CA certs #############################################################\n """) for ca_name in ca_names: ca = opensslcnf.getcadata(ca_name) if ca.signedby: if ca.signedby in ca_names: parentca = opensslcnf.getcadata(ca.signedby) else: parentca = None sys.stderr.write('CA name "%s" given in signedby parameter of section [%s] not found.\n' % (subca.signedby,subca.sectionname)) else: parentca = ca if not (filenotvalid(ca.certificate) or filenotvalid(parentca.certificate)): sys.stdout.write('Verifying sub-CA certificate %s with issuer certificate %s.\n' % (ca.certificate,parentca.certificate)) rc = os.system('%s verify -verbose -CAfile %s %s' % \ (OpenSSLExec,parentca.certificate,ca.certificate)) if rc: sys.stderr.write('Error %d verifying CA cert %s.\n' % (rc,ca.certificate)) ca_cert = openssl.cert.X509CertificateClass(ca.certificate) if not ca_cert.subject.has_key('CN'): sys.stderr.write('CA certificate %s has no CN attribute.\nThis might cause weird problems with some software.\n' % (ca.certificate)) for subject_attr in ca_cert.subject.keys(): if not charset.is_ascii(charset.asn12iso(ca_cert.subject[subject_attr])): sys.stderr.write('CA certificate %s has NON-ASCII attribute %s.\nThis might cause weird problems with some software.\n' % (ca.certificate,subject_attr)) else: sys.stderr.write('Certificate file %s or %s not found.\n' % (ca.certificate,parentca.certificate)) pyca-20031118/sbin/ca-cycle-priv.py0100755000076400001440000001641607310470205016211 0ustar michaelusers#!/usr/bin/python ######################################################################## # ca-cycle-priv.py # (c) by Michael Stroeder, michael@stroeder.com ######################################################################## __version__ = '0.6.6' ######################################################################## # Process some modifications which involve private CA keys. # This script is typically run by CRON or in a similar manner. # It does several jobs: # - Mark expired certificates in OpenSSL certificate database # - Process certificate requests # - Process certificate revocation requests # - Generate new CRLs, move old CRLs to archive # Special notes: # The private systems only handles PEM format certificate data ######################################################################## import sys, string, os, stat, time, getopt def filenotvalid(pathname): return not os.path.isfile(pathname) or os.stat(pathname)[stat.ST_SIZE]==0 def findoption(options,paramname): for i in options: if i[0]==paramname: return i return () def PrintUsage(ErrorMsg='',ErrorCode=1): script_name = string.split(sys.argv[0],os.sep)[-1] sys.stderr.write("""*** %s *** (C) by Michael Stroeder, 1999 usage: %s [options] Options: -h or --help Print out this message --config=[pathname] Pathname of OpenSSL configuration file. You may also use env variable OPENSSL_CONF. Default: /etc/openssl/openssl.cnf --pycalib=[directory] Specify directory containing the pyCA modules Default: /usr/local/pyca/pylib --issuecrls Force issuing of new CRLs Default: CRLs are only created if no valid CRL is present """ % (script_name,script_name)) if ErrorMsg: sys.stderr.write('Error: %s\n' % ErrorMsg) sys.exit(ErrorCode) ######################################################################## # Main ######################################################################## script_name=sys.argv[0] try: options,args=getopt.getopt(sys.argv[1:],'h',['help','config=','pycalib=','issuecrls']) except getopt.error,e: PrintUsage(str(e)) if findoption(options,'-h')!=() or findoption(options,'--help')!=(): PrintUsage() if findoption(options,'--config')!=(): opensslcnfname = findoption(options,'--config')[1] else: opensslcnfname = os.environ.get('OPENSSL_CONF','/etc/openssl/openssl.cnf') if not os.path.isfile(opensslcnfname): PrintUsage('Config file %s not found.' % (opensslcnfname)) if findoption(options,'--pycalib')!=(): pycalib = findoption(options,'--pycalib')[1] else: pycalib = os.environ.get('PYCALIB','/usr/local/pyca/pylib') issuecrls = findoption(options,'--issuecrls')!=() if not os.path.exists(pycalib) or not os.path.isdir(pycalib): PrintUsage('Module directory %s not exists or not a directory.' % (pycalib)) sys.path.append(pycalib) try: import openssl except ImportError: PrintUsage('Required pyCA modules not found in directory %s!' % (pycalib)) # Read the configuration file if os.path.isfile('%s.pickle' % (opensslcnfname)): # Try to read OpenSSL's config file from a pickled copy f=open('%s.pickle' % (opensslcnfname),'rb') try: # first try to use the faster cPickle module from cPickle import load except ImportError: from pickle import load opensslcnf=load(f) f.close() else: # Parse OpenSSL's config file from source opensslcnf=openssl.cnf.OpenSSLConfigClass(opensslcnfname) pyca_section = opensslcnf.data.get('pyca',{}) openssl.bin_filename = pyca_section.get('OpenSSLExec','/usr/local/ssl/bin/openssl') if not os.path.isfile(openssl.bin_filename): PrintUsage('Did not find OpenSSL executable %s.' % (openssl.bin_filename)) ca_names = opensslcnf.sectionkeys.get('ca',[]) gmt = time.time() gmtstr = time.strftime('%Y%m%d%H%M%S',time.gmtime(gmt)) ###################################################################### # Spool certificates and certificate revocation lists # from system holding the private keys ###################################################################### pass ###################################################################### # CA specific actions ###################################################################### ca_names = opensslcnf.sectionkeys.get('ca',[]) # Lists of processed files and dirs to avoid double-processing processed_ca_databases = [] processed_ca_crls = [] processed_pend_reqs_dirs = [] processed_new_certs_dirs = [] for ca_name in ca_names: sys.stdout.write(""" ############################################################# # Processing CA "%s" #############################################################\n\n """ % (ca_name)) ca = opensslcnf.getcadata(ca_name) ######################################################################## # Processing certificate database ######################################################################## if not ca.database in processed_ca_databases: if os.path.isfile(ca.database): processed_ca_databases.append(ca.database) # Certificate database not processed up to now ca_db = openssl.db.OpenSSLcaDatabaseClass(ca.database) # Mark expired certificates in OpenSSL certificate database ca_db.Expire() else: sys.stderr.write('Warning: CA database file %s not found.\n' % (ca.database)) ######################################################################## # Process certificate requests ######################################################################## pass ######################################################################## # Process certificate revocation requests ######################################################################## pass ######################################################################## # Generate new CRLs, move old CRLs to archive ######################################################################## sys.stdout.write(""" ############################################################# # Move expired CRLs to archive and generate new CRLs #############################################################\n Use password of each CA here.\n """) if not ca.crl in processed_ca_crls: if os.path.isfile(ca.crl): processed_ca_crls.append(ca.crl) ca_crl = openssl.cert.CRLClass(ca.crl) if issuecrls or (ca_crl.nextUpdate_secs<=gmt+ca.crl_treshold*3600): # CRL is expired => move it to archive ca_archived_crl = os.path.join(ca.crl_dir,os.path.basename('%s-%s.pem' % (os.path.splitext(ca.crl)[0],gmtstr))) os.rename(ca.crl,ca_archived_crl) sys.stdout.write('Archived expired CRL file %s.\n' % (ca.crl)) if filenotvalid(ca.crl): if os.path.isfile(ca.certificate) and os.path.isfile(ca.private_key): if os.path.isfile(ca.database): sys.stdout.write('Issuing new CRL %s.\n' % (ca.crl)) rc = os.system('%s ca -config %s -name %s -gencrl -out %s' % \ (openssl.bin_filename,opensslcnfname,ca.sectionname,ca.crl)) if rc: sys.stderr.write('Error %d creating CRL %s.\n' % (rc,ca.crl)) else: sys.stderr.write('Warning: CA database file %s not found.\n' % (ca.database)) else: sys.stderr.write('CA cert %s or CA key %s missing.\n' % (ca.certificate,ca.private_key)) if filenotvalid(ca.crl): sys.stderr.write('Warning: No valid CRL file %s after processing.\n' % (ca.crl)) pyca-20031118/sbin/ca-cycle-pub.py0100755000076400001440000003205607310433641016020 0ustar michaelusers#!/usr/bin/python ######################################################################## # ca-cycle-pub.py # (c) by Michael Stroeder, michael@stroeder.com ######################################################################## __version__ = '0.6.6' ######################################################################## # This script is typically run by CRON or a similar task manager. # It does several jobs (some not implemented yet): # - Mark expired certificates in OpenSSL certificate database # - Sort in new certificates and inform user via e-mail where to # download his certificate # - Spool certificate requests and certificate revocation requests # - Remove stale certificate requests from caPendCertReqDir ######################################################################## import sys, string, os, smtplib, getopt from time import time,gmtime,localtime,strftime,mktime def findoption(options,paramname): for i in options: if i[0]==paramname: return i return () def PrintUsage(ErrorMsg='',ErrorCode=1): script_name = string.split(sys.argv[0],os.sep)[-1] sys.stderr.write("""*** %s *** (C) by Michael Stroeder, 1999 usage: %s [options] Options: -h or --help Print out this message --config=[pathname] Pathname of OpenSSL configuration file. You may also use env variable OPENSSL_CONF. Default: /etc/openssl/openssl.cnf --pycalib=[directory] Specify directory containing the pyCA modules Default: /usr/local/pyca/pylib """ % (script_name,script_name)) if ErrorMsg: sys.stderr.write('Error: %s\n' % ErrorMsg) sys.exit(ErrorCode) ######################################################################## # Main ######################################################################## script_name=sys.argv[0] try: options,args=getopt.getopt(sys.argv[1:],'h',['help','config=','pycalib=']) except getopt.error,e: PrintUsage(str(e)) if findoption(options,'-h')!=() or findoption(options,'--help')!=(): PrintUsage() if findoption(options,'--config')!=(): opensslcnfname = findoption(options,'--config')[1] else: opensslcnfname = os.environ.get('OPENSSL_CONF','/etc/openssl/openssl.cnf') if not os.path.isfile(opensslcnfname): PrintUsage('Config file %s not found.' % (opensslcnfname)) if findoption(options,'--pycalib')!=(): pycalib = findoption(options,'--pycalib')[1] else: pycalib = os.environ.get('PYCALIB','/usr/local/pyca/pylib') if not os.path.exists(pycalib) or not os.path.isdir(pycalib): PrintUsage('Module directory %s not exists or not a directory.' % (pycalib)) sys.path.append(pycalib) try: import openssl,charset from openssl.db import \ empty_DN_dict, \ DB_type,DB_exp_date,DB_rev_date,DB_serial,DB_file,DB_name,DB_number, \ DB_TYPE_REV,DB_TYPE_EXP,DB_TYPE_VAL, \ dbtime2tuple,GetEntriesbyDN,SplitDN except ImportError: PrintUsage('Required pyCA modules not found in directory %s!' % (pycalib)) # Read the configuration file if os.path.isfile('%s.pickle' % (opensslcnfname)): # Try to read OpenSSL's config file from a pickled copy f=open('%s.pickle' % (opensslcnfname),'rb') try: # first try to use the faster cPickle module from cPickle import load except ImportError: from pickle import load opensslcnf=load(f) f.close() else: # Parse OpenSSL's config file from source opensslcnf=openssl.cnf.OpenSSLConfigClass(opensslcnfname) pyca_section = opensslcnf.data.get('pyca',{}) openssl.bin_filename = pyca_section.get('OpenSSLExec','/usr/local/ssl/bin/openssl') if not os.path.isfile(openssl.bin_filename): PrintUsage('Did not find OpenSSL executable %s.' % (openssl.bin_filename)) MailRelay = pyca_section.get('MailRelay','localhost') nsGetCertUrl = pyca_section.get('nsGetCertUrl','cgi-bin/get-cert.py') nsViewCertUrl = pyca_section.get('nsViewCertUrl','cgi-bin/view-cert.py') caPendCertReqValid = 3600*string.atoi(pyca_section.get('caPendCertReqValid','0')) newcert_mailtext = r"""From: %s To: %s Subject: Your certificate %d The certificate you requested has been issued and is valid from %s until %s. You can retrieve your certificate from here: %s%s/%s/%s.crt?%x Please use the same web browser on the same machine, with same login and configuration data as when you created the certificate request. Otherwise your software will likely refuse to install the certificate. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! It is highly recommended to make a backup copy of your ! ! private key and certificate right after installing it. ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! If you have further questions simply reply to this e-mail. ---------------------------------------------------------- %s Detail view of your certificate: %s%s/%s/%s?%x """ gmt = time() gmtstr = strftime('%Y%m%d%H%M%S',gmtime(gmt)) ###################################################################### # Spool certificates and certificate revocation lists # from system holding the private keys ###################################################################### pass ###################################################################### # CA specific actions ###################################################################### ca_names = opensslcnf.sectionkeys.get('ca',[]) # Lists of processed files and dirs to avoid double-processing processed_ca_databases = [] processed_ca_crls = [] processed_pend_reqs_dirs = [] processed_new_reqs_dirs = [] processed_new_certs_dirs = [] for ca_name in ca_names: sys.stdout.write('\n\nProcessing certificate authority "%s"\n' % (ca_name)) ca = opensslcnf.getcadata(ca_name) ###################################################################### # Processing certificate database ###################################################################### if not ca.database in processed_ca_databases: if os.path.isfile(ca.database): processed_ca_databases.append(ca.database) # Certificate database not processed up to now ca_db = openssl.db.OpenSSLcaDatabaseClass(ca.database) # Mark expired certificates in OpenSSL certificate database expired_db_entries = ca_db.Expire() if expired_db_entries: sys.stdout.write('The following entries were marked as expired:\n') for db_entry in expired_db_entries: sys.stdout.write('%s\n' % (charset.asn12iso(db_entry[DB_name]))) # Mark expired certificates in OpenSSL certificate database expire_treshold=7*86400 expired_db_entries = ca_db.ExpireWarning(expire_treshold) if expired_db_entries: sys.stdout.write('The following entries will expire soon:\n') for db_entry in expired_db_entries: sys.stdout.write('%s, %s, %s\n' % ( db_entry[DB_serial], strftime('%Y-%m-%d %H:%M',localtime(mktime(dbtime2tuple(db_entry[DB_exp_date])))), charset.asn12iso(db_entry[DB_name]) ) ) else: sys.stderr.write('Warning: CA database file %s not found.\n' % (ca.database)) ###################################################################### # Move expired CRLs to archive ###################################################################### if not ca.crl in processed_ca_crls: if os.path.isfile(ca.crl): processed_ca_crls.append(ca.crl) ca_crl = openssl.cert.CRLClass(ca.crl) if ca_crl.nextUpdate_secs os.path.getmtime(reqpathname): os.remove(reqpathname) stalecerts = stalecerts+1 if stalecerts: sys.stdout.write('Removed %d stale certificate requests from %s.\n' % (stalecerts,ca.pend_reqs_dir)) else: pendcertfilenames = [] sys.stderr.write('Directory %s not found!\n' % (ca.pend_reqs_dir)) ###################################################################### # New certificate requests ###################################################################### if ca.new_reqs_dir and \ (not ca.new_reqs_dir in processed_new_reqs_dirs): processed_new_reqs_dirs.append(ca.new_reqs_dir) # new_certs_dir not processed up to now if os.path.isdir(ca.new_reqs_dir): newreqfilenames = os.listdir(ca.new_reqs_dir) newreqcounter = 0 for reqfilename in newreqfilenames: if os.path.splitext(reqfilename)[1] in ['.spkac','.pem']: newreqcounter = newreqcounter+1 if newreqcounter: sys.stdout.write('%d valid certificate requests in %s.\n' % (newreqcounter,ca.new_reqs_dir)) else: newcertfilenames = [] sys.stderr.write('Directory %s not found!\n' % (ca.new_reqs_dir)) ###################################################################### # Publish new client certificates and inform user via e-mail where to # download his certificate ###################################################################### if (not ca.new_certs_dir in processed_new_certs_dirs) and \ os.path.isfile(ca.certificate): processed_new_certs_dirs.append(ca.new_certs_dir) # new_certs_dir not processed up to now newcertfilenames = os.listdir(ca.new_certs_dir) if newcertfilenames: sys.stdout.write('Publish %d new certificates in %s.\n' % (len(newcertfilenames),ca.new_certs_dir)) if ca.isservercert(): certtype = 'server' elif ca.isclientcert(): certtype = 'user' else: certtype = 'user' for certfilename in newcertfilenames: newcertpathname = os.path.join(ca.new_certs_dir,certfilename) cert = openssl.cert.X509CertificateClass(newcertpathname) if openssl.db.GetEntrybySerial(ca.database,cert.serial): certpathname = os.path.join(ca.certs,certfilename) if not os.path.isfile(certpathname): import mimify issuername = charset.asn12iso(cert.issuer.get('CN','')) issueremail = cert.issuer.get('Email','root@localhost') subjectname = charset.asn12iso(cert.subject.get('CN','')) subjectemail = cert.subject.get('Email','') from_addr = mimify.mime_encode_header('%s <%s>' % (issuername,issueremail)) if subjectemail: to_name,to_email = subjectname,subjectemail else: to_name,to_email = issuername,issueremail to_addr = mimify.mime_encode_header('%s <%s>' % (to_name,to_email)) # Mailbody mail_msg = newcert_mailtext % ( from_addr, to_addr, cert.serial, strftime('%Y-%m-%d %H:%M',gmtime(cert.notBefore_secs)), strftime('%Y-%m-%d %H:%M',gmtime(cert.notAfter_secs)), ca.nsBaseUrl,nsGetCertUrl,ca_name,certtype,cert.serial, cert.asciiprint(), ca.nsBaseUrl,nsViewCertUrl,ca_name,certtype,cert.serial, ) smtpconn=smtplib.SMTP(MailRelay) smtpconn.set_debuglevel(0) try: smtpconn.sendmail(issueremail,to_email,mail_msg) sys.stderr.write('Sent e-mail to %s <%s>.\n' % (to_name,to_email)) except: sys.stderr.write('Unable to send an e-mail to %s <%s>.\n' % (to_name,to_email)) smtpconn.quit() # Move file from newcerts to certs dir os.rename(newcertpathname,certpathname) else: sys.stderr.write('Target file %s already exists. Maybe something went wrong?\n' % (certfilename)) else: sys.stderr.write('Did not find certificate with serial number %d in file %s! Certificate database up to date?\n' % (cert.serial,ca.database)) ###################################################################### # Spool certificate requests and certificate revocation requests # to system holding the private keys ###################################################################### pass ###################################################################### # Check again if everything is in place and give out summary report ###################################################################### sys.stdout.write('\n\n##### Summary report #####\n\n') for ca_name in ca_names: ca = opensslcnf.getcadata(ca_name) sys.stdout.write('CA definition "%s":\n' % (ca_name)) if not os.path.isfile(ca.crl): sys.stderr.write('Warning: No valid CRL file %s after processing.\n' % (ca.crl)) else: ca_crl = openssl.cert.CRLClass(ca.crl) sys.stdout.write('CRL file %s valid from %s until %s.\n' % (ca.crl,ca_crl.lastUpdate,ca_crl.nextUpdate)) pyca-20031118/sbin/ca-certreq-mail.py0100755000076400001440000002056207745153414016532 0ustar michaelusers#!/usr/bin/python """ ca-certreq-mail.py (c) by Michael Stroeder, michael@stroeder.com This script is intended to handle the confirmation mail for a cert request. It receives the mail on stdin and moves the certificate request file from pend_reqs_dir to new_reqs_dir. If the senders from: address is different of the one in the certificate request the sender is notified about this by e-mail. If an error occurs the exit code is still zero to prevent the mail system from generating a bounce revealing internal informations. """ __version__ = '0.6.6' import sys, os, shutil, time, string, smtplib, rfc822, getopt from mimify import mime_decode_header, mime_encode_header # Einen Datensatz in Protokolldatei schreiben # log Filehandle von bereits geoeffneter Protokolldatei # Kategorie des Eintrags, z.B. 'Error:' # Mail Mailheader # Kommentar klar def LogWrite(log,Kategorie,Mail,Kommentar): log.write('%s %s: ' % (time.strftime('%d.%m.%Y %X',time.localtime(time.time())),Kategorie)) if Mail: for i in ['from','subject','message-id']: if Mail.has_key(i): log.write('%s ' % (Mail[i])) log.write('%s\n' % (Kommentar)) return def findoption(options,paramname): for i in options: if i[0]==paramname: return i return () ######################################################################## # Main ######################################################################## script_name=sys.argv[0] # The log file can only be stderr as long as the config is not read logfile = sys.stderr # Parse command-line options try: options,args=getopt.getopt(sys.argv[1:],'h',['help','config=','pycalib=']) except getopt.error,e: LogWrite(logfile,'Error',None,str(e)) # Try to find modules directory if findoption(options,'--pycalib')!=(): pycalib = findoption(options,'--pycalib')[1] else: pycalib = os.environ.get('PYCALIB','/usr/local/pyca/pylib') if not os.path.exists(pycalib) or not os.path.isdir(pycalib): LogWrite(logfile,'Error',None,'Module directory %s not exists or not a directory.' % (pycalib)) sys.path.append(pycalib) if findoption(options,'--config')!=(): opensslcnfname = findoption(options,'--config')[1] else: opensslcnfname = os.environ.get('OPENSSL_CONF','/etc/openssl/openssl.cnf') if not os.path.isfile(opensslcnfname): LogWrite(logfile,'Error',None,'Config file %s not found.' % (opensslcnfname)) import openssl,charset from openssl.db import \ empty_DN_dict, \ DB_type,DB_exp_date,DB_rev_date,DB_serial,DB_file,DB_name,DB_number, \ DB_TYPE_REV,DB_TYPE_EXP,DB_TYPE_VAL, \ dbtime2tuple,GetEntriesbyDN,SplitDN # Read the configuration file if os.path.isfile('%s.pickle' % (opensslcnfname)): # Try to read OpenSSL's config file from a pickled copy f=open('%s.pickle' % (opensslcnfname),'rb') try: # first try to use the faster cPickle module from cPickle import load except ImportError: from pickle import load opensslcnf=load(f) f.close() else: # Parse OpenSSL's config file from source opensslcnf=openssl.cnf.OpenSSLConfigClass(opensslcnfname) pyca_section = opensslcnf.data.get('pyca',{}) logfile_name = pyca_section.get('caCertConfirmReqLog','/var/log/pyca/ca-certreq-mail.out') logfile = open(logfile_name,'a') openssl.bin_filename = pyca_section.get('OpenSSLExec','/usr/local/ssl/bin/openssl') if not os.path.isfile(openssl.bin_filename): LogWrite(logfile,'Error',None,'Did not find OpenSSL executable %s.' % (openssl.bin_filename)) ca_names = opensslcnf.sectionkeys.get('ca',[]) MailRelay = pyca_section.get('MailRelay','localhost') caCertReqMailAdr = pyca_section.get('caCertReqMailAdr','') caInternalCertTypes = pyca_section.get('caInternalCertTypes',[]) caInternalDomains = pyca_section.get('caInternalDomains','') if type(caInternalDomains)!=type([]): caInternalDomains = [caInternalDomains] ############################################################# # Hauptprogramm ############################################################# m=rfc822.Message(sys.stdin) ############################################################# # Format von Subject und Body ueberpruefen # Wirkt recht paranoid, soll aber gegen Muellmails schuetzen ############################################################# # Ueberlange Subjects verbieten if len(m["subject"])>80: LogWrite(logfile,'Error',m,'Subject too long.') sys.exit(0) if m.has_key('from'): from_addr = mime_decode_header(string.strip(m["from"])) from_name, from_mail = rfc822.AddressList(from_addr).addresslist[0] else: from_mail = '' subject = string.strip(m["subject"]) subjectstart = string.find(subject,'cert-req-') # Format dreiteilig? try: prefix,ca_name,caChallengeId = string.split(subject[subjectstart:len(subject)],'.',2) except ValueError: LogWrite(logfile,'Error',m,'Subject has wrong format.') sys.exit(0) # Prefix richtig? if prefix=='cert-req-SPKAC': request_filenamesuffix = 'spkac' elif prefix=='cert-req-PKCS10': request_filenamesuffix = 'pem' else: LogWrite(logfile,'Error',m,'Subject has wrong format.') sys.exit(0) # ChallengeID nicht zu lang? if len(caChallengeId)>30: LogWrite(logfile,'Error',m,'caChallengeId %s has bad format.' % (caChallengeId)) sys.exit(0) # CA Name gueltig? if not (ca_name in ca_names): LogWrite(logfile,'Error',m,'ca_name "%s" wrong.' % (ca_name)) sys.exit(0) ca = opensslcnf.getcadata(ca_name) # Eine Benutzerantwort ist eingetroffen request_filename = os.path.join(ca.pend_reqs_dir,'%s.%s.%s' % (prefix,ca_name,caChallengeId)) pubkey_filename = '%s.%s' % (request_filename,request_filenamesuffix) # Existieren die benoetigten Dateien? if not os.path.isfile(pubkey_filename): LogWrite(logfile,'Error',m,'Certificate request file %s not found.' % (pubkey_filename)) sys.exit(0) # Hier sind jetzt alle Angaben gueltig, soweit pruefbar newrequest_filename = os.path.join(ca.new_reqs_dir,'%s.%s.%s' % (prefix,ca_name,caChallengeId)) target_pubkey_filename = '%s.%s' % (newrequest_filename,request_filenamesuffix) # Now copy files to target directory, use copy to get new ownership try: shutil.copyfile(pubkey_filename,target_pubkey_filename) os.chmod(target_pubkey_filename,0440) except IOError: LogWrite(logfile,'Error',m,'Copying %s to %s failed.' % (pubkey_filename,target_pubkey_filename)) sys.exit(0) else: try: os.remove(pubkey_filename) except IOError: LogWrite(logfile,'Error',m,'Removing %s failed.' % (pubkey_filename)) sys.exit(0) LogWrite(logfile,'Challenge',m,'Request challenge: %s Id=%s' % (ca_name,caChallengeId)) # FIX ME! We also would like to look into PKCS10 requests! if prefix!='cert-req-SPKAC': sys.exit(0) # Read the certificate request file certreq = openssl.cert.SPKACClass(target_pubkey_filename) certreq_name_attr = certreq.data.get('commonName','') certreq_mail_attr = certreq.data.get('emailAddress','') if (certreq_name_attr and from_name!=certreq_name_attr) or \ (certreq_mail_attr and from_mail!=certreq_mail_attr): cacert = openssl.cert.X509CertificateClass(ca.certificate) ca_from_addr = cacert.subject.get('Email',pyca_section.get('caAdminMailAdr','')) mail_msg = """From: %s <%s> To: %s Subject: Your confirmation e-mail with ID %s We received the correct confirmation e-mail for your certificate request. However the from: field of your confirmation e-mail From: %s did not match the attributes commonName = %s emailAddress = %s given in your certificate request. Your certificate request will be processed anyway. But if you intend to use the requested certificate for signing e-mails you might want to adjust the from address in your mail clients preferences / options menu to avoid trouble with other mail users reporting invalid signatures. If you have further questions simply reply to this e-mail. """ % ( mime_encode_header( charset.t612iso( cacert.subject.get('CN','CA administrator')) ), ca_from_addr, certreq_mail_attr, caChallengeId, from_addr, certreq_name_attr,certreq_mail_attr ) try: smtpconn=smtplib.SMTP(MailRelay) try: try: smtpconn.set_debuglevel(0) smtpconn.sendmail(ca_from_addr,certreq_mail_attr,mail_msg) finally: smtpconn.quit() except: LogWrite(logfile,'Error',m,'Unable to send an e-mail to %s!\n' % (certreq_mail_attr)) else: LogWrite(logfile,'Error',m,'Sent from address warning to %s!\n' % (certreq_mail_attr)) except socket.error: LogWrite(logfile,'Error',m,'Unable to contact default mail relay %s!\n' % (MailRelay)) sys.exit(0) pyca-20031118/sbin/pickle-cnf.py0100755000076400001440000000550307301173261015563 0ustar michaelusers#!/usr/bin/python ######################################################################## # pickle-cnf.py # (c) by Michael Stroeder, michael@stroeder.com ######################################################################## __version__ = '0.6.6' ######################################################################## # This short script creates a pickled object file of the # OpenSSL configuration file. ######################################################################## import sys, string, os, pickle, getopt def findoption(options,paramname): for i in options: if i[0]==paramname: return i return () def PrintUsage(ErrorMsg='',ErrorCode=1): script_name = string.split(sys.argv[0],os.sep)[-1] sys.stderr.write("""*** %s *** (C) by Michael Stroeder, 1999 usage: %s [options] Options: -h or --help Print out this message --config=[pathname] Pathname of OpenSSL configuration file. You may also use env variable OPENSSL_CONF. Default: /etc/openssl/openssl.cnf --pycalib=[directory] Specify directory containing the pyCA modules You may also use env variable PYCALIB. Default: /usr/local/pyca/pylib """ % (script_name,script_name)) if ErrorMsg: sys.stderr.write('Error: %s\n' % ErrorMsg) sys.exit(ErrorCode) ######################################################################## # Main ######################################################################## script_name=sys.argv[0] try: options,args=getopt.getopt(sys.argv[1:],'h',['help','config=','pycalib=']) except getopt.error,e: PrintUsage(str(e)) if findoption(options,'-h')!=() or findoption(options,'--help')!=(): PrintUsage() if findoption(options,'--config')!=(): opensslcnfname = findoption(options,'--config')[1] else: opensslcnfname = os.environ.get('OPENSSL_CONF','/etc/openssl/openssl.cnf') if not os.path.isfile(opensslcnfname): PrintUsage('Config file %s not found.' % (opensslcnfname)) if findoption(options,'--pycalib')!=(): pycalib = findoption(options,'--pycalib')[1] else: pycalib = os.environ.get('PYCALIB','/usr/local/pyca/pylib') if not os.path.exists(pycalib) or not os.path.isdir(pycalib): PrintUsage('Module directory %s not exists or not a directory.' % (pycalib)) sys.path.append(pycalib) try: import openssl except ImportError: PrintUsage('Module openssl not found in directory %s!' % (pycalib)) print 'Reading source file %s...' % (opensslcnfname) opensslcnf = openssl.cnf.OpenSSLConfigClass(opensslcnfname) pickle_opensslcnfname = '%s.pickle' % (opensslcnfname) #if os.path.isfile(pickle_opensslcnfname): # print 'Removing old pickle file %s' % (pickle_opensslcnfname) # os.remove(pickle_opensslcnfname) print 'Write new pickled file %s...' % (pickle_opensslcnfname) f=open(pickle_opensslcnfname,'wb') pickle.dump(opensslcnf, f,1) f.close() pyca-20031118/pylib/0040755000076400001440000000000007756443113013371 5ustar michaeluserspyca-20031118/pylib/ipadr.py0100644000076400001440000000147607005664045015043 0ustar michaelusersimport string # IP-Adresse in Stringform zu 4er-Tupel konvertieren def ipadrstr2tuple(IPAdrStr=''): return map(string.atoi,string.split(IPAdrStr,'.',3)) # IP-Adresse mit Netzwerkadresse/Netzwerkmaske vergleichen def MatchIPAdr(IPAdress,NetworkAdress,Mask): IPAdressT=ipadrstr2tuple(IPAdress) MaskT=ipadrstr2tuple(Mask) NetworkAdressT=ipadrstr2tuple(NetworkAdress) return (IPAdressT[0]&MaskT[0] == NetworkAdressT[0]&MaskT[0]) and \ (IPAdressT[1]&MaskT[1] == NetworkAdressT[1]&MaskT[1]) and \ (IPAdressT[2]&MaskT[2] == NetworkAdressT[2]&MaskT[2]) and \ (IPAdressT[3]&MaskT[3] == NetworkAdressT[3]&MaskT[3]) def MatchIPAdrList(IPAdr,subnetlist): if IPAdr and subnetlist: for subnet in subnetlist: net, mask = string.split(subnet,'/',1) if MatchIPAdr(IPAdr,net,mask): return 1 return 0 pyca-20031118/pylib/ldapbase.py0100644000076400001440000000604607025154247015515 0ustar michaelusers############################################################################## # ldapbase.py Version 0.1.2 # (c) by Michael Stroeder, michael.stroeder@propack-data.de ############################################################################## import sys, string, re dn_pattern = r'([\w;.]+[\s]*=[^,]+)(,[ ]*[\w;.]+[\s]*=[^,]+)*' dn_regex = re.compile(dn_pattern) # returns 1 if s is a LDAP DN def is_dn(s): rm = dn_regex.match(s) return rm!=None and rm.group(0)==s def normalize_dn(dn): result = string.split(dn,',') result = map(string.strip,result) return string.join(result,',') # returns parent-DN of dn def ParentDN(dn): return string.join(string.split(dn,',')[1:],',') # returns a list of parent-DNs of dn def ParentDNList(dn): result = [] DNComponentList = string.split(dn,',') for i in range(1,len(DNComponentList)): result.append(string.join(DNComponentList[i:],',')) return result # parse a LDAP URL and return (host,dn,attributes,scope,filter) # host LDAP host # dn distinguished name # attributes list with attributes # scope search scope string # filter LDAP search filter def parse_ldap_url(ldap_url): dummy,rest = string.split(ldap_url,'://',1) try: host,rest = string.split(rest,'/',1) except ValueError: host='' ; dn=rest paramlist=string.split(rest,'?') dn = paramlist[0] try: attributes = string.split(paramlist[1],',') except IndexError: attributes = [] try: scope = paramlist[2] except IndexError: scope = '' try: filter = paramlist[3] except IndexError: filter = '' return (host,dn,attributes,scope,filter) class Attribute: def __init__(self): self.name='' def put(self,name,oid='',syntax='',alias=[],notes=''): self.name=name self.oid=oid self.alias=alias self.notes=notes def parse(self,attr_schemastr): pass class ObjectClass: def __init__(self): self.name='' def put(self,name,oid='',syntax='',sup='',must=[],may=[],notes=''): self.name=name self.oid=oid self.abstract=abstract self.sup=sup self.must=must self.may=may self.syntax=syntax self.notes=notes def parse(self,oc_schemastr): pass class Schema: def __init__(self,host): self.host=host self.oc_def = {} self.oc_list = [] self.attr_def = {} self.attr_list = [] def AddObjectClass(self,name,oid='',sup='',must=['objectClass'],may=[],syntax='',notes=''): if not name in self.oc_list: self.oc_list.append(name) self.oc_def['name']=ObjectClass() self.oc_def['name'].put(name,oid,sup,must,may,syntax,notes) def AddAttribute(self,name,oid='',syntax='',alias=[],notes=''): if not name in self.attr_list: self.attr_list.append(name) self.attr_def['name']=Attribute() self.attr_def['name'].put(name,oid,syntax,alias,notes) def v3SchemaQuery(self,ldapconn,basedn='cn=schema',searchfilter='objectclass=subschema'): schema = ldapconn.search_s() def ReadOpenLDAPConf(self,slapdconf): f = open(slapdconf,'r') pyca-20031118/pylib/ldif.py0100644000076400001440000001077707156165370014672 0ustar michaelusers""" ldif.py - Various routines for handling LDIF data This module is distributed under the terms of the GPL (GNU GENERAL PUBLIC LICENSE) Version 2 (see http://www.gnu.org/copyleft/gpl.html) """ __version__ = '0.2.5' import sys,string,binascii,re try: from cStringIO import StringIO except ImportError: from StringIO import StringIO attr_pattern = r'[\w;.]+(;[\w_-]+)*' data_pattern = '([^,]+|".*?")' rdn_pattern = attr_pattern + r'[\s]*=[\s]*' + data_pattern dn_pattern = rdn_pattern + r'([\s]*,[\s]*' + rdn_pattern + r')*' #rdn_regex = re.compile('^%s$' % rdn_pattern) dn_regex = re.compile('^%s$' % dn_pattern) ldif_pattern = '^((dn(:|::) %(dn_pattern)s)|(%(attr_pattern)s(:|::) %(data_pattern)s)$)+' % vars() ldif_regex = re.compile('^%s$' % ldif_pattern,re.M) def is_dn(s): """ returns 1 if s is a LDAP DN """ rm = dn_regex.match(s) return rm!=None and rm.group(0)==s def is_ascii(s): """ returns 1 if s is plain ASCII """ if s: pos=0 ; s_len = len(s) while ((ord(s[pos]) & 0x80) == 0) and (pos binary data assumed data = data[1:] binary = 1 else: # Read attr: data line binary = 0 s = f.readline() s = string.rstrip(s) # Reading continued multi-line data while s and s[0]==' ': data = data + string.strip(s) # Read next line s = f.readline() s = string.rstrip(s) attr = string.lower(string.strip(attr)) if not attr in ignored_attrs: if binary: # binary data has to be BASE64 decoded data = binascii.a2b_base64(data) else: data = string.strip(data) # Add attr: data to entry if attr=='dn': dn = string.strip(data) ; attr = '' ; data = '' if not is_dn(dn): raise ValueError, 'No valid string-representation of distinguished name.' else: if entry.has_key(attr): entry[attr].append(data) else: entry[attr]=[data] # end of entry reached marked by newline character(s) if entry: # append entry to result list result.append((dn,entry)) entries_read = entries_read+1 # Start reading next entry s = f.readline() return result pyca-20031118/pylib/cgihelper.py0100644000076400001440000000445107047337543015710 0ustar michaelusers############################################################################## # cgihelper.py Version 0.1.4 # (c) by Michael Stroeder, michael.stroeder@propack-data.de ############################################################################## # Misc. stuff useful in CGI-BINs ############################################################################## import sys, os, string, re known_browsers = { 'MSIE':'Microsoft Internet Explorer', 'Mozilla':'Netscape Navigator', 'Lynx':'Lynx', 'Opera':'Opera', 'StarOffice':'StarOffice', 'NCSA_Mosaic':'NCSA Mosaic', 'NetPositive':'Net Positive' } known_browsers_rev = {} for b in known_browsers.keys(): known_browsers_rev[known_browsers[b]]=b compatible_browsers = known_browsers.keys() compatible_browsers.remove('Mozilla') compatible_browsers_re = re.compile('(%s)[/ ]+([0-9.]*)' % string.join(compatible_browsers,'|')) mozilla_re = re.compile('(Mozilla)[/ ]+([0-9.]*)') # This function trys to parse the HTTP_USER_AGENT environment variable # set in CGI-BINs and returns the tuple (Browser,Version). I am not sure # if this succeeds in every situation since most browsers have very obscure # HTTP_USER_AGENT entries for compability reasons. # The following browsers are known by name: # Netscape Netscape Navigator, Netscape Communicator) # MSIE MS Internet Explorer # Opera Opera browser from http://www.operasoftware.com/ # StarOffice built-in browser of Star Office # Lynx the text-based browser Lynx # NetPositive Net Positive (BeOS) def BrowserType(http_user_agent): if not http_user_agent: return ('','') else: browserrm = compatible_browsers_re.search(http_user_agent) if browserrm: return browserrm.groups() else: browserrm = mozilla_re.search(http_user_agent) if browserrm: return browserrm.groups() else: return ('','') # Main # Read and parse some CGI-BIN environment variables http_user_agent = os.environ.get('HTTP_USER_AGENT','') if http_user_agent: http_user_agent_type,http_user_agent_version = BrowserType(http_user_agent) script_name = os.environ.get('SCRIPT_NAME','') request_method = os.environ.get('REQUEST_METHOD') remote_addr = os.environ.get('REMOTE_ADDR') path_info = os.environ.get('PATH_INFO','') pyca-20031118/pylib/charset.py0100644000076400001440000001174607534626353015405 0ustar michaelusers""" charset.py - Module for converting characters sets (c) by Michael Stroeder This module is distributed under the terms of the GPL (GNU GENERAL PUBLIC LICENSE) Version 2 (see http://www.gnu.org/copyleft/gpl.html) """ __version__ = '0.4.1' import sys, string # Alphabet for encrypted passwords (see module crypt) crypt_alphabet = './0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' crypt_alphabet_len = len(crypt_alphabet) def is_ascii(s): """ returns 1 if s is plain ASCII """ if s: pos=0 ; s_len = len(s) while ((ord(s[pos]) & 0x80) == 0) and (pos> 6)))+chr(0x80 | (0x3F & c)) return new UTF8len= ( 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 5, 6 ) UTF8mask = (0x3F,0x7F,0x1F,0x0F,0x07,0x03,0x01) def utf2iso(s): """ Convert UTF-8 encoded Unicode to ISO-8859-1 """ new = '' ind = 0 slen = len(s) while ind < slen: c=ord(s[ind]) ind = ind+1 clen = UTF8len[(c >> 2) & 0x3F] u = c & UTF8mask[clen] if clen==0: clen = 4 else: clen = clen-1 while clen and ind < slen: c=ord(s[ind]) ind = ind+1 if (c & 0xC0) == 0x80: u = (u << 6) | (c & 0x3F) else: ind = ind-1 break clen = clen-1 if (u <= 0xFF): new = new+chr(u) else: new = new+'?' return new def utf2html4(s): """ Convert UTF-8 encoded Unicode to HTML-4 character representation """ new = '' ind = 0 slen = len(s) while ind < slen: c=ord(s[ind]) ind = ind+1 clen = UTF8len[(c >> 2) & 0x3F] u = c & UTF8mask[clen] if clen==0: clen = 4 else: clen = clen-1 while clen and ind < slen: c=ord(s[ind]) ind = ind+1 if (c & 0xC0) == 0x80: u = (u << 6) | (c & 0x3F) else: ind = ind-1 break clen = clen-1 if u<128: new = new + chr(u) else: new = new + '&#%d;' % u return new def iso2html4(s): """ Convert ISO-8859-1 to HTML-4 character representation """ new = '' for ch in s: c=ord(ch) if (c & 0x80)==0: new = new + ch else: new = new + '&#%d;' % (c) return new def iso2t61(s): """ Convert ISO-8859-1 to T.61 character representation """ new = '' for ch in s: c=ord(ch) if (c & 0x80) == 0: new = new+ch else: new = '%s\\x%X' % (new,ord(ch)) return new def t612iso(s): """ Convert T.61 character representation to ISO-8859-1 """ new = '' slashpos = string.find(s,'\\x') while slashpos!=-1: if (s[slashpos]==0) or (s[slashpos]>0 and s[slashpos-1]!='\\'): new = new+s[0:slashpos]+chr(string.atoi(s[slashpos+2:slashpos+4],16)) s = s[slashpos+4:] else: new = new+s[0:slashpos-1] s = s[slashpos+1:] slashpos = string.find(s,'\\x') return new+s def t612html4(s): """ Convert T.61 character representation to HTML-4 character representation """ new = '' slashpos = string.find(s,'\\x') while slashpos!=-1: if (s[slashpos]==0) or (s[slashpos]>0 and s[slashpos-1]!='\\'): new = new+s[0:slashpos]+'&#%d;' % string.atoi(s[slashpos+2:slashpos+4],16) s = s[slashpos+4:] else: new = new+s[0:slashpos-1] s = s[slashpos+1:] slashpos = string.find(s,'\\x') return new+s def iso2asn1(s): """ Convert ISO-8859-1 to BMPString """ new = '' for ch in s: c=ord(ch) if (c & 0x80) == 0: new = '%s\\x00%s' % (new,ch) else: new = '%s\\x00\\x%X%s' % (new,ord(ch),ch) return new def asn12iso(s): """ Convert BMPString to ISO-8859-1 """ return t612iso(string.replace(s,'\\x00','')) def asn12html4(s): """ Convert BMPString to HTML-4 character representation """ return t612html4(string.replace(s,'\\x00','')) recode_func = { 'ISO-8859-1': { 'UTF-8' : iso2utf, 'HTML4' : iso2html4 }, 'ISO-8859-15': { 'UTF-8' : iso2utf, 'HTML4' : iso2html4 }, 'UTF-8': { 'HTML4' : utf2html4, 'ISO-8859-1' : utf2iso } } def recode(s,source,target): """ Convert from/to known character set / encoding """ if source==target: return s return recode_func[string.upper(source)][string.upper(target)](s) pyca-20031118/pylib/htmlbase.py0100644000076400001440000000260507321124447015534 0ustar michaelusers######################################################################## # Funktionen fuer immer wiederkehrende HTML-Ausgaben ######################################################################## ######################################################################## # Hier einige Variablen zur Konfiguration ######################################################################## # Parameter fuers -Tag bodyPARAM='' import os, string # Ausgabe einer Ueberschrift def PrintHeading(Msg,Type=1): print '%s' % (Type,Msg,Type) # Ausdrucken eines HTML-Kopfes mit Titelzeile def PrintHeader(TitleMsg,HTTP_charset='iso-8859-1'): print """Content-type: text/html;charset=%s pragma: no-cache %s """ % (HTTP_charset,TitleMsg,bodyPARAM) return # Ausdrucken eines HTML-Endes def PrintFooter(): print """

Powered by pyCA

""" return # Fehlernachricht ausgeben def PrintErrorMsg(Msg): PrintHeader('Error') print """

Error

%s

""" % (Msg) server_admin = os.environ.get('SERVER_ADMIN','') if server_admin: print 'Please contact %s.' % ( server_admin,server_admin ) PrintFooter() return pyca-20031118/pylib/certhelper.py0100644000076400001440000000403207165050244016065 0ustar michaelusers######################################################################## # certhelper.py # (c) by Michael Stroeder, michael@stroeder.com ######################################################################## # Deal with X.509 certificates and cert requests with # plain Python 1.5.2 lib ######################################################################## import string, re, base64, md5, sha pem_re = re.compile('-----BEGIN (CERTIFICATE|X509 CRL|CERTIFICATE REQUEST)-----([ \w+/=\r\n]+?)-----END (CERTIFICATE|X509 CRL|CERTIFICATE REQUEST)-----',re.S+re.M) def MD5Fingerprint(cert_der='',delimiter=':'): """ MD5 fingerprint in dotted notation (delimiter between bytes) """ cert_md5 = md5.new(cert_der).digest() cert_fingerprint = [] for i in cert_md5: cert_fingerprint.append(string.upper('%02x' % (ord(i)))) return string.join(cert_fingerprint,delimiter) def SHA1Fingerprint(cert_der='',delimiter=':'): """ Return SHA-1 fingerprint in dotted notation (delimiter between bytes) """ cert_sha1 = sha.new(cert_der).digest() cert_fingerprint = [] for i in cert_sha1: cert_fingerprint.append(string.upper('%02x' % (ord(i)))) return string.join(cert_fingerprint,delimiter) def extract_pem(cert_text): """ Extract all base64 encoded certs in a text file to a list of strings """ result = [] for begin_type,cert_base64,end_type in pem_re.findall(cert_text): if begin_type!=end_type: raise ValueError,"-----BEGIN %s----- and -----END %s----- does not match" % (begin_type,end_type) result.append((begin_type,string.strip(cert_base64))) return result def der2pem(cert_der,cert_type='CERTIFICATE'): """ Convert single binary DER-encoded certificate to base64 encoded format """ return """-----BEGIN %s----- %s-----END %s----- """ % (cert_type,base64.encodestring(cert_der),cert_type) def pem2der(cert_text): """ Convert single base64 encoded certificate to binary DER-encoded format """ cert_type,cert_base64 = extract_pem(cert_text)[0] return base64.decodestring(string.strip(cert_base64)) pyca-20031118/pylib/vbs.py0100644000076400001440000001253207756113147014536 0ustar michaelusers""" vbs.py - generate VBScript for cert enrollment with M$ Internet Explorer c) by Michael Stroeder, michael@stroeder.com Special thanks to: - inspired code contributed by Jordi Floriach , - VBScript function PrintVBSCryptoProvider() appears by courtesy of Michael Konietzka """ __version__='0.6.6' import sys, string def PrintVBSXenrollObject(codebase='xenroll.dll'): """ for xenroll.dll """ print """ """ % (codebase) def PrintVBSKeyGenCode(form): """ Generate code for key generation in Internet Explorer """ # Mapping von SPKAC- zu DN-Attributnamen spkac2DNname = { 'countryName':'C', 'stateOrProvinceName':'S', 'localityName':'L', 'organizationName':'O', 'organizationalUnitName':'OU', 'commonName':'CN', 'emailAddress':'1.2.840.113549.1.9.1', 'initials':'2.5.4.43', 'uid':'0.9.2342.19200300.100.1.1', 'userID':'0.9.2342.19200300.100.1.1', } formKeys = form.inputkeys[:] for unwantedkey in ['userpassword','challenge','browsertype','PKCS10']: try: formKeys.remove(unwantedkey) except ValueError: pass CertDN_list = [] for i in formKeys: if (i in form.inputkeys) and form.field[i][0].content: CertDN_list.append('%s=%s' % (spkac2DNname.get(i,i),form.field[i][0].content)) print """ Function GenTheKeyPair() Dim certHelper Set certHelper = CreateObject("CEnroll.CEnroll.2") if ( (Err.Number = 438) OR (Err.Number = 429) ) Then Err.Clear Set certHelper = CreateObject("CEnroll.CEnroll.1") End If Dim keyprov, key, prov, pkcs10data keyprov = document.KeyGenForm.KeySize.value key = Mid(keyprov,1,1) prov = Mid(keyprov,2) certHelper.providerType = 1 certHelper.providerName = prov If key = "4" Then certHelper.GenKeyFlags = &h04000003 Else certHelper.GenKeyFlags = &h02000003 End If certHelper.HashAlgorithm = "MD5" certHelper.KeySpec = 1 certHelper.EnableT61DNEncoding = True pkcs10data = certHelper.createPKCS10("%s", "") If (pkcs10data = Empty) Then Alert "Error " & Hex(Err) & ": Your credentials could not be generated." Else Alert "The following certificate request was generated:"&chr(13)&chr(10)&"%s" Document.KeyGenForm.PKCS10.value = pkcs10data Document.KeyGenForm.submit() End If End Function """ % (string.join(CertDN_list,';'),string.join(CertDN_list,'"&chr(13)&chr(10)&"')) ########################################################################### # Generate code for installing certificate in Internet Explorers cert DB ########################################################################### def PrintVBSCertInstallCode(subject,serial,notafterstr,cert): certlines = string.split(cert,'\n')[1:-2] certcode = string.join(certlines,'" & chr(10) & _\n "') msgstr = """"The following certificate will be installed:" & chr(13) & chr(10) & _ "Serial: %s" & chr(13) & chr(10) & _ "Subject: %s" & chr(13) & chr(10) & _ "Valid until: %s" & chr(13) & chr(10) """ % (serial,subject,notafterstr) print """ Function InstallPKCS7Cert() Dim certHelper Set certHelper = CreateObject("CEnroll.CEnroll.2") if ( (Err.Number = 438) OR (Err.Number = 429) ) Then Err.Clear Set certHelper = CreateObject("CEnroll.CEnroll.1") End If Dim PKCS7Cert PKCS7Cert = "%s" On Error Resume Next MsgBox %s err.clear certHelper.AcceptPKCS7(PKCS7Cert) If Err.Number <> 0 Then Alert "Error " & Hex(Err) & ": The certificate could not be installed." Else MsgBox "The certificate was successfully installed." End If End Function Call InstallPKCS7Cert() """ % (certcode,msgstr) ########################################################################### # Generate code for choosing cryptographic provider # Derived from example code by Michael Konietzka ########################################################################### def PrintVBSCryptoProvider(): print """ Sub KeySizeSelectList Dim certHelper Set certHelper = CreateObject("CEnroll.CEnroll.2") if ( (Err.Number = 438) OR (Err.Number = 429) ) Then Err.Clear Set certHelper = CreateObject("CEnroll.CEnroll.1") End If Dim i Dim providers() Dim KeySizeOption Dim cryptoprovider Dim DefaultKeySize Dim enhanced On Error Resume Next i = 0 DefaultKeySize = 0 certHelper.providerType = 1 Do cryptoprovider = "" cryptoprovider = certHelper.enumProviders(i,0) If Len(cryptoprovider) = 0 Then Exit Do Else enhanced = InStr(1,cryptoprovider,"Enhanced",1) set KeySizeOption = document.createElement("OPTION") If enhanced = 0 Then KeySizeOption.text = "512 bit, " & cryptoprovider KeySizeOption.value = "2"&cryptoprovider Else KeySizeOption.text = "1024 bit, " & cryptoprovider KeySizeOption.value = "4" & cryptoprovider document.KeyGenForm.KeySize.add(KeySizeOption) DefaultKeySize = i i = i+1 set KeySizeOption = document.createElement("OPTION") KeySizeOption.text = "512 bit, " & cryptoprovider KeySizeOption.value = "2" & cryptoprovider End If document.KeyGenForm.KeySize.add(KeySizeOption) End If i = i+1 Loop document.KeyGenForm.KeySize.DefaultKeySizeIndex = DefaultKeySize End Sub """ pyca-20031118/pylib/openssl/0040755000076400001440000000000007756443113015054 5ustar michaeluserspyca-20031118/pylib/openssl/db.py0100644000076400001440000002365707745152531016024 0ustar michaelusers####################################################################### # openssl.db.py Version 0.6.4 # (c) by Michael Stroeder, michael.stroeder@propack-data.de ######################################################################## # Module for OpenSSL certificate database ######################################################################## import openssl import sys, os, string, time, re, charset # Konstanten aus SSLeay/apps/ca.c DB_type = 0 DB_exp_date = 1 DB_rev_date = 2 DB_serial = 3 # index - unique DB_file = 4 DB_name = 5 # index - unique for active DB_number = 6 DB_TYPE_REV = 'R' DB_TYPE_EXP = 'E' DB_TYPE_VAL = 'V' # Struktur eines DN # /C=ISO-Laendercode # /ST=Bundesstaat # /L=Ort # /O=Organisation # /OU=Abteilung # /CN=Common Name # /Email=Mailadresse empty_DN_dict = {'C':'','ST':'','L':'','O':'','OU':'','CN':'','Email':''} ############################################################################## # Eintrag aus der database mittels serial suchen # Eingabe: # Seriennummer des Zertifikats als int # Ergebnis: # Liste mit Feldern des Eintrags, falls vorhanden # [], falls kein Eintrag mit angegebener # Seriennummer gefunden wurde ############################################################################## def GetEntrybySerial(db_filename,serial): # Oeffnen der SSLeay-Zertifikatliste database=open(db_filename,'r') # Zeile mit serial suchen dbline=string.strip(database.readline()) while dbline: dbfields=string.split(dbline,'\t') dbserial=string.atoi(dbfields[DB_serial],16) if dbserial==serial: # Zertifikat mit angegebener Nummer gefunden return dbfields dbline=database.readline() # keine passende Zeile gefunden return [] ############################################################################## # DN auseinandernehmen # Ergebnis ist Dictionary mit Feldnamen als Keys (siehe empty_DN_dict) ############################################################################## def SplitDN(DN): result = {} s = string.split(DN[1:],'/') for i in s: try: id,value = string.split(i,'=',1) result[id] = value except: pass return result ############################################################################## # Ueberpruefen, ob Eintrag dbfields (Liste der DB-Felder) noch gueltig ist. # 1 falls gueltig, 0 falls abgelaufen oder widerrufen ############################################################################## def IsValid(db_entry): # Aktuelle Zeit in GMT abholen gmt = time.time() exp_time = time.mktime(dbtime2tuple(db_entry[DB_exp_date])) return (db_entry[DB_type]==DB_TYPE_VAL) and (exp_time > gmt) ############################################################################## # Eintrag aus der database mittels DN suchen: # Gesucht wird nach Feldern, welche als Substring, die jeweilige # Angabe haben. # Eingabe: # DN: # Dictionary mit Suchstrings (regulaere Ausdruecke) # Umlaute ISO-codiert # casesensitive: # Gibt an, ob Gross-/Kleinschreibung beachtet werden soll. # onlyvalid: # Es werden nur gueltige Zertifikate gesucht, d.h. das Feld DB_type # muss DB_TYPE_VAL enthalten und das Expire-Datum wird gleich geprueft. # # Ergebnis: # Liste mit Eintragslisten, falls gefunden # [], falls kein Eintrag mit angegebenen # Attributen gefunden wurde # [], falls keine Suchmuster angegeben wurden ############################################################################## def GetEntriesbyDN(db_filename,DN=empty_DN_dict,casesensitive=0,onlyvalid=0): searchcounter = 0 searchindex = [] searchregex = {} for i in DN.keys(): if DN[i]!='': # Liste mit Indizes der angegebenen Suchfelder searchindex.append(i) # Liste mit kompilierten regex if not casesensitive: DN[i]=string.lower(DN[i]) searchregex[i] = re.compile(DN[i]) # Angegebene Suchfelder zaehlen searchcounter = searchcounter + 1 # Keinerlei Suchangabe => leere Liste if searchcounter==0: return[] # Oeffnen der SSLeay-Zertifikatliste db_file=open(db_filename,'r') # Liste der gefundenen Eintraege found = [] # Erste Zeile lesen und LF abschneiden db_line=string.strip(db_file.readline()) while db_line: # Eintragszeile auseinandernehmen db_entry = string.split(db_line,'\t') # DN-Feld auseinander nehmen dnfield = SplitDN(db_entry[DB_name]) # Alle Teile des DN-Feldes mit jeweiligem Suchmuster vergleichen matchcounter = 0 for i in searchindex: if dnfield.has_key(i): dnfield[i] = charset.asn12iso(dnfield[i]) if not casesensitive: dnfield[i] = string.lower(dnfield[i]) matchcounter = matchcounter+(searchregex[i].search(dnfield[i])!=None) # Alle Angaben gefunden? if matchcounter==searchcounter: if onlyvalid: if IsValid(db_entry): found.append(db_entry) else: found.append(db_entry) # naechste Zeile lesen und LF abschneiden db_line=db_file.readline()[:-1] # keine passende Zeile gefunden return found ######################################################################## # Konvertieren einer Zeitangabe in der SSLeay-DB in ein Python-Tupel # kompatibel zum Modul time. # Eingabe: # openssltime Zeitangabe aus SSLeay-DB als String # Augabe: # konvertiertes time-Tupel als Funktionsergebnis ######################################################################## def dbtime2tuple(openssltime): # return time.strptime(openssltime,'%y%m%d%H%M%SZ') # would be easier but since strptime is broken in glibc... openssltime=openssltime[:-1] year = string.atoi(openssltime[0:2]) # Ja, diese Software ist Y2K ;-) if year<50: year=year+2000 else: year=year+1900 month = string.atoi(openssltime[2:4]) day = string.atoi(openssltime[4:6]) hour = string.atoi(openssltime[6:8]) minute = string.atoi(openssltime[8:10]) if len(openssltime)>10: second = string.atoi(openssltime[10:12]) else: second = 0 return (year,month,day,hour,minute,second,0,0,0) ######################################################################## # Ein Zertifikat mit der angegebenen Seriennr. zurueckrufen, also # DB_type-Feld auf DB_TYPE_REV = 'R' setzen. # Eingabe # serial eine Ganzzahl mit der Seriennr. # oder eine Liste mit Seriennr. # der zu widerrufenden Zertifikate ######################################################################## def Revoke(db_filename,serial): # Umbenennen der Datei os.rename(db_filename,db_filename+'.old') # Oeffnen der alten SSLeay-Zertifikatliste zum Lesen db_old=open(db_filename+'.old','r') # Erzeugen der neuen SSLeay-Zertifikatliste zum Schreiben db_new=open(db_filename,'w') # Aktuelle Zeit in GMT abholen und in passenden String formatieren gmtstr = time.strftime('%y%m%d%H%M%SZ',time.gmtime(time.time())) # Erste Zeile aus alter Datei lesen db_line = string.strip(db_old.readline()) while db_line: # Eintragszeile auseinandernehmen db_entry = string.split(db_line,'\t') # Ist das Zertifikat noch als gueltig markiert? if ((type(serial)==type([]) and \ string.atoi(db_entry[DB_serial],16) in serial) \ or \ string.atoi(db_entry[DB_serial],16)==serial): # Zertifikat als abgelaufen markieren db_entry[DB_type] = DB_TYPE_REV # Zertifikat als abgelaufen markieren db_entry[DB_rev_date] = gmtstr # Eintragszeile in neue Datei schreiben db_new.write('%s\n' % string.join(db_entry,'\t')) # Naechste Zeile aus alter Datei lesen db_line = string.strip(db_old.readline()) db_old.close() db_new.close() ######################################################################## # Alle Eintraege durchsuchen und DB_type-Feld auf DB_TYPE_EXP = 'E' # setzen, falls Gueltigkeitsdauer des Zertifikats abgelaufen ist. # Als Ergebnis eine Liste der abgelaufenen DB-Eintraege zurueckliefern. # Falls db_write=0, dann werden keine Schreibaktionen ausgefuehrt. ######################################################################## def Expire(db_filename,db_expiretreshold=0,db_write=1): if db_write: # Umbenennen der Datei os.rename(db_filename,db_filename+'.old') # Oeffnen der alten SSLeay-Zertifikatliste zum Lesen db_old=open(db_filename+'.old','r') # Erzeugen der neuen SSLeay-Zertifikatliste zum Schreiben db_new=open(db_filename,'w') else: db_old=open(db_filename,'r') # Aktuelle Zeit in GMT abholen gmt = time.time() expired_db_entries = [] # Erste Zeile aus alter Datei lesen db_line = string.strip(db_old.readline()) while db_line: # Eintragszeile auseinandernehmen db_entry = string.split(db_line,'\t') # Ist das Zertifikat noch als gueltig markiert? if db_entry[DB_type]==DB_TYPE_VAL: exp_time = time.mktime(dbtime2tuple(db_entry[DB_exp_date])) # Zertifikat abgelaufen? if exp_time < gmt+db_expiretreshold: if db_write: db_entry[DB_type] = DB_TYPE_EXP expired_db_entries.append(db_entry) if db_write: # Eintragszeile in neue Datei schreiben db_new.write('%s\n' % string.join(db_entry,'\t')) # Naechste Zeile aus alter Datei lesen db_line = string.strip(db_old.readline()) db_old.close() if db_write: db_new.close() return expired_db_entries ######################################################################## # Objektklasse fuer eine Konfigurationsdatei ######################################################################## class OpenSSLcaDatabaseClass: def __init__(self,pathname): self.pathname = pathname def Expire(self): return Expire(self.pathname) def ExpireWarning(self,treshold): return Expire(self.pathname,db_expiretreshold=treshold,db_write=0) def Revoke(self,serial): Revoke(self.pathname,serial) def GetEntriesbyDN(self,DN=empty_DN_dict,casesensitive=0,onlyvalid=0): return GetEntriesbyDN(self.pathname,DN,casesensitive,onlyvalid) def GetEntrybySerial(self,serial): return GetEntrybySerial(self.pathname,serial) pyca-20031118/pylib/openssl/cnf.py0100644000076400001440000002435307301004445016163 0ustar michaelusers####################################################################### # openssl.cnf.py # (c) by Michael Stroeder, michael.stroeder@propack-data.de ######################################################################## # Modul fuer den Zugriff auf die SSLeay-Konfigurationsdatei # openssl.cnf ######################################################################## ######################################################################## # Ab hier gibt es nix mehr zu konfigurieren ######################################################################## import sys,types,string,re,charset __version__ = '0.6.6' ######################################################################## # Funktion GetAllSections() # Eingabe: # keine # Ausgabe: # Dictionary mit Abschnittsnamen als Index und Dictionaries der # einzelnen Abschnitte als Feldelemente # {}, falls Konfigurationsdatei leer # Anmerkung: # Fuer Konfigurationseintraege ausserhalb eines Abschnittes wird der # Pseudo-Abschnittsnamen '_' gesetzt. ######################################################################## def GetAllSections(filename): keys = {'_':[]} result = {'_':{}} # parameters not within a section will be stored in dummy section "_" section_name = '_' # regular exp for a line defining a section name issection_re=re.compile(r'^\s*\[\s*\w+\s*\]\s*(#)*.*') # regular exp for extracting a section name in a section line section_name_re=re.compile(r'\[\s*\w+\s*\]') # regular exp for a syntactically correct line defining a parameter # e.g. name = "parameter1",parameter2 # comment isparamline_re=re.compile(r'^(\d+.)*[\w_]+\s*=.*[\s#]*.*') # regular exp for extracting a parameter [num.]name in a parameter line paramline_numname_re=re.compile(r'(\d+.)*[\w_]+') # regular exp for extracting all parameter values from value incl. comment part onevalue_regex = r'(´.*?´|".*?"|[^:^,^#]*[^:^,^#^\s]|[^:^,^#]+:([^:^,^#]*[^:^,^#^\s]+|".*?"|´.*?´))' paramline_valuepart_re=re.compile(onevalue_regex+r'+(,'+onevalue_regex+')*\s*(#)*') # regular exp for splitting all values from value part paramline_valuesplit_re=re.compile('%s' % (onevalue_regex)) # regular exp for testing if param is quoted isquoted_re=re.compile('(%s|%s|%s)' % ('´.*´','".*"',"'.*'")) # Open config file cnf_file = open(filename,'r') # Read first line from file line = cnf_file.readline() linenum = 0 while line: line = string.strip(line) # sys.stderr.write('***%s\n' % (line)) if issection_re.search(line)!=None: # Section found => new section in result dict # extract plain section name by searching [ aphanum-string ] # and stripping "[", "]" and white-spaces section_name = string.strip(section_name_re.search(line).group(0)[1:-1]) # Create new sub-dict. If there are multiple sections (broken cnf-file) # with the same name, the last one is valid. keys[section_name]=[] result[section_name]={} elif isparamline_re.search(line)!=None: # Is valid parameter line # Extract parameter name name = paramline_numname_re.search(line).group(0) # Extract parameter num.name try: num,name = string.split(name,'.',1) except ValueError: num = '' # extract plain value part # and strip "=", "#" and white-spaces line = string.strip(line[string.index(line,'=')+1:]) if line: valuepart = string.strip(paramline_valuepart_re.match(line).group(0)) valuegroups = paramline_valuesplit_re.findall(valuepart) else: valuepart = '' valuegroups = [] # sys.stderr.write('***valuegroups=%s\n' % (valuegroups)) if len(valuegroups)>1: result[section_name][name] = [] for valuetuple in valuegroups: if isquoted_re.search(valuetuple[0]): value = valuetuple[0][1:-1] else: value = valuetuple[0] # New entry in current section keys[section_name].append(name) # Store value of entry in dict value = string.strip(value) if value: result[section_name][name].append(value) elif len(valuegroups)==1: valuetuple = valuegroups[0] if isquoted_re.search(valuetuple[0]): value = valuetuple[0][1:-1] else: value = valuetuple[0] # New entry in current section keys[section_name].append(name) # Store value of entry in dict result[section_name][name] = string.strip(value) # Read next line from file line = cnf_file.readline() linenum = linenum+1 cnf_file.close() return keys,result ######################################################################## # Objektklasse fuer eine CA-Definition ######################################################################## class caDataClass: def __init__(self): pass # returns 1 if the certificates of CA ca_name are # client certificates (depending on keyUsage and nsCertType). def isclientcert(self): if self.basicConstraints and self.basicConstraints=='CA:true': return 0 if self.nsCertType: if type(self.nsCertType)==types.ListType: isClientCert=0 for i in self.nsCertType: isClientCert = isClientCert or (i in ['email','client','objsign']) else: isClientCert = self.nsCertType in ['email','client','objsign'] return isClientCert else: return 1 # returns 1 if the certificates of CA ca_name are usable for # email (depending on keyUsage and nsCertType). def isemailcert(self): if self.basicConstraints and self.basicConstraints=='CA:true': return 0 return (type(self.nsCertType)==types.ListType and ('email' in self.nsCertType)) or \ (self.nsCertType=='email') or \ (self.nsCertType=='') # returns 1 if the certificates of CA ca_name are usable for # email (depending on keyUsage and nsCertType). def isservercert(self): if self.basicConstraints and self.basicConstraints=='CA:true': return 0 return (type(self.nsCertType)==types.ListType and ('server' in self.nsCertType)) or \ (self.nsCertType=='server') or \ (self.nsCertType=='') ######################################################################## # Objektklasse fuer eine Konfigurationsdatei ######################################################################## class OpenSSLConfigClass: def __init__(self,pathname): self.sectionkeys,self.data = GetAllSections(pathname) # FIX ME!!! Look for Netscape specs about key usage determination. # Build tree with CA hierarchy def getcatree(self): catree = {'.':[]} ca_names = self.sectionkeys.get('ca',[]) for ca_name in ca_names: signedby = self.data[self.data['ca'][ca_name]].get('signedby','') if signedby: if catree.has_key(signedby): catree[signedby].append(ca_name) else: catree[signedby]=[ca_name] else: catree['.'].append(ca_name) return catree # Get all relevant data of a CA definition and its subsequent sections def getcadata(self,ca_name): ca = caDataClass() pyca_section = self.data.get('pyca',{}) ca.sectionname = self.data['ca'][ca_name] ca_section = self.data[ca.sectionname] ca.name = ca_name ca.dir = ca_section.get('dir','') ca.serial = string.replace(ca_section.get('serial','$dir/serial'),'$dir',ca.dir) ca.certificate = string.replace(ca_section.get('certificate','$dir/cacert.pem'),'$dir',ca.dir) ca.private_key = string.replace(ca_section.get('private_key','$dir/private/cakey.pem'),'$dir',ca.dir) ca.database = string.replace(ca_section.get('database','$dir/index.txt'),'$dir',ca.dir) ca.pend_reqs_dir = string.replace(ca_section.get('pend_reqs_dir','$dir/pendreqs'),'$dir',ca.dir) ca.crl = string.replace(ca_section.get('crl','$dir/crl.pem'),'$dir',ca.dir) ca.crl_dir = string.replace(ca_section.get('crl_dir','$dir/crl'),'$dir',ca.dir) ca.new_reqs_dir = string.replace(ca_section.get('new_reqs_dir','$dir/newreqs'),'$dir',ca.dir) ca.old_reqs_dir = string.replace(ca_section.get('old_reqs_dir','$dir/oldreqs'),'$dir',ca.dir) ca.new_certs_dir = string.replace(ca_section.get('new_certs_dir','$dir/newcerts'),'$dir',ca.dir) ca.certs = string.replace(ca_section.get('certs','$dir/certs'),'$dir',ca.dir) ca.req = ca_section.get('req','req') ca.policy = ca_section.get('policy','') ca.signedby = ca_section.get('signedby','') ca.ca_reqfile = ca_section.get('ca_reqfile','') ca.ca_x509_extfile = ca_section.get('ca_x509_extfile','') ca.min_key_size = string.atoi(ca_section.get('min_key_size','0')) ca.default_days = string.atoi(ca_section.get('default_days','0')) ca.crl_days = string.atoi(ca_section.get('crl_days','0')) ca.crl_treshold = string.atoi(ca_section.get('crl_treshold','0')) ca.x509_extensions = ca_section.get('x509_extensions','') x509_extensions_section = self.data.get(ca.x509_extensions,{}) # PKIX attributes ca.basicConstraints = x509_extensions_section.get('basicConstraints','') ca.keyUsage = x509_extensions_section.get('keyUsage','') ca.extendedKeyUsage = x509_extensions_section.get('extendedKeyUsage','') # Netscape attributes ca.nsCertType = x509_extensions_section.get('nsCertType','') ca.nsBaseUrl = x509_extensions_section.get('nsBaseUrl',pyca_section.get('nsBaseUrl','')) ca.nsCaRevocationUrl = x509_extensions_section.get('nsCaRevocationUrl','') ca.nsRevocationUrl = x509_extensions_section.get('nsRevocationUrl','') ca.nsCaPolicyUrl = x509_extensions_section.get('nsCaPolicyUrl','') ca.nsComment = x509_extensions_section.get('nsComment','') if type(ca.nsCertType)==types.ListType: ca.nsCertTypeStr=string.join(ca.nsCertType,'/') else: ca.nsCertTypeStr=ca.nsCertType return ca # get list of pathnames of all intermediate CA certficates # excluding the self-signed root CA cert def getcacertchain(self,ca_name): ca_section = self.data[self.data['ca'][ca_name]] result = [] while ca_section.has_key('signedby'): ca_dir = ca_section.get('dir','') ca_certificate = string.replace(ca_section.get('certificate','$dir/cacert.pem'),'$dir',ca_dir) result.append(ca_certificate) ca_signedby = ca_section['signedby'] if not self.data['ca'].has_key(ca_signedby): raise KeyError,'CA name not found' ca_section = self.data[self.data['ca'][ca_signedby]] return result pyca-20031118/pylib/openssl/__init__.py0100644000076400001440000000002507047337627017164 0ustar michaelusersimport db, cnf, cert pyca-20031118/pylib/openssl/cert.py0100644000076400001440000002146707321127431016360 0ustar michaelusers####################################################################### # openssl.cert.py Version 0.6.6 # (c) by Michael Stroeder, michael.stroeder@propack-data.de ######################################################################## # Module for accessing certificate files ######################################################################## import sys, string, os, time import openssl, charset, certhelper certformats = ['pem','der','txt','net'] X509v1_certattrlist = ['CN','Email','OU','O','L','ST','C'] ######################################################################## # functions used throughout this module ######################################################################## def GuessFormatbyExt(certfilename): ext = string.lower(os.path.splitext(certfilename)[1]) if ext in certformats: return ext else: return 'pem' def GetCertValues(certfilename,inform='',command='x509'): command = string.lower(command) if command == 'x509': opensslcommand = '%s x509 -in %s -fingerprint -inform %s -noout -subject -issuer -dates -serial -hash' % (openssl.bin_filename,certfilename,inform) elif command == 'crl': opensslcommand = '%s crl -in %s -inform %s -noout -issuer -lastupdate -nextupdate -hash' % (openssl.bin_filename,certfilename,inform) result={} hash='' pipe = os.popen(opensslcommand) s=pipe.readline() while s: try: name, value = string.split(s,'=',1) except ValueError: hash = string.strip(s) result[name]=string.strip(value) s=pipe.readline() rc = pipe.close() if rc and rc!=256: raise IOError,"Error %s: %s" % (rc,opensslcommand) return hash, result ######################################################################## # X509CertificateClass ######################################################################## class X509CertificateClass: def __init__(self,certfilename,inform=''): self.filename = certfilename if not inform: self.format = GuessFormatbyExt(self.filename) else: self.format = inform self.hash,certattrs = GetCertValues(self.filename,self.format,'x509') self.issuer = openssl.db.SplitDN(certattrs.get('issuer',{})) self.subject = openssl.db.SplitDN(certattrs.get('subject',{})) self.serial = string.atoi(certattrs.get('serial','-1'),16) self.notBefore = certattrs.get('notBefore','') if self.notBefore: self.notBefore_secs = time.mktime(time.strptime(self.notBefore,'%b %d %H:%M:%S %Y GMT')) else: self.notBefore_secs = 0 self.notAfter = certattrs.get('notAfter','') if self.notAfter: self.notAfter_secs = time.mktime(time.strptime(self.notAfter,'%b %d %H:%M:%S %Y GMT')) else: self.notAfter_secs = 0 # Fingerprint suchen certattrs_keys = certattrs.keys() self.fingerprint = {} for i in certattrs_keys: if i[-11:]=='Fingerprint': self.fingerprint[string.upper(string.split(i)[0])] = string.split(certattrs[i],':') # get serial number of certificate def serial(self): return string.atoi(self.serial) def getfingerprint(self,digest='md5',delimiter=':'): digest = string.upper(digest) if not digest in ['MD2','MD5','MDC2','RMD160','SHA','SHA1']: raise ValueError, 'Illegal parameter for digest: %s' % digest elif self.fingerprint.has_key(digest): result = self.fingerprint[digest] elif digest=='MD5': return certhelper.MD5Fingerprint(certhelper.pem2der(open(self.filename,'r').read()),delimiter) elif digest=='SHA1': return certhelper.SHA1Fingerprint(certhelper.pem2der(open(self.filename,'r').read()),delimiter) else: opensslcommand = '%s x509 -in %s -inform %s -outform DER | %s %s' % ( openssl.bin_filename, self.filename, self.format, openssl.bin_filename, string.lower(digest) ) f = os.popen(opensslcommand) rawdigest = string.strip(f.read()) rc = f.close() if rc and rc!=256: raise IOError,"Error %s: %s" % (rc,opensslcommand) result = [] for i in range(len(rawdigest)/2): result.append(rawdigest[2*i:2*(i+1)]) self.fingerprint[digest] = result return string.upper(string.join(result,delimiter)) # return certificate in a string, desired format given in outform def readcertfile(self,outform='PEM'): if not (string.lower(outform) in certformats): raise ValueError,'invalid certificate output format' f = os.popen('%s x509 -in %s -inform %s -outform %s' % (openssl.bin_filename,self.filename,self.format,outform)) buf = f.read() result = [] while buf: result.append(buf) buf = f.read() rc = f.close() if rc and rc!=256: raise IOError,"Error %s: %s" % (rc,opensslcommand) return string.join(result) # Return a string containing the cert attributes def asciiprint(self): # Convert character sets subjectdatalist = [] issuerdatalist = [] for attr in X509v1_certattrlist: subjectdatalist.append('%-5s: %s' % (attr,string.strip(charset.asn12iso(self.subject.get(attr,''))))) issuerdatalist.append('%-5s: %s' % (attr,string.strip(charset.asn12iso(self.issuer.get(attr,''))))) return """This certificate belongs to: %s This certificate was issued by: %s Serial Number: %s This certificate is valid from %s until %s. Certificate Fingerprint: SHA-1: %s MD5 : %s """ % ( \ string.join(subjectdatalist,'\n'), string.join(issuerdatalist,'\n'), self.serial, self.notBefore, self.notAfter, self.getfingerprint('sha1'), self.getfingerprint('md5'), ) # Return a string containing a nice formatted with cert info def htmlprint(self): # Convert character sets subjectdatalist = [] issuerdatalist = [] for attr in X509v1_certattrlist: subjectdatalist.append(string.strip(charset.asn12html4(self.subject.get(attr,'')))) issuerdatalist.append(string.strip(charset.asn12html4(self.issuer.get(attr,'')))) return """
This certificate belongs to:
%s
This certificate was issued by:
%s
Serial Number:
%s
This certificate is valid from %s until %s.
Certificate Fingerprint:
SHA-1: %s
MD5: %s
""" % ( \ string.join(subjectdatalist,'
'), string.join(issuerdatalist,'
'), self.serial, self.notBefore, self.notAfter, self.getfingerprint('sha1'), self.getfingerprint('md5'), ) ######################################################################## # CRLClass ######################################################################## class CRLClass: def __init__(self,crlfilename,inform=''): self.filename = crlfilename if not inform: self.format = GuessFormatbyExt(self.filename) else: self.format = inform self.hash,certattrs = GetCertValues(self.filename,self.format,'crl') self.issuer = openssl.db.SplitDN(certattrs.get('issuer',openssl.db.empty_DN_dict)) self.hash = certattrs.get('hash','') self.lastUpdate = certattrs.get('lastUpdate','') if self.lastUpdate: self.lastUpdate_secs = time.mktime(time.strptime(self.lastUpdate,'%b %d %H:%M:%S %Y GMT')) else: self.lastUpdate_secs = 0 self.nextUpdate = certattrs.get('nextUpdate','') if self.nextUpdate: self.nextUpdate_secs = time.mktime(time.strptime(self.nextUpdate,'%b %d %H:%M:%S %Y GMT')) else: self.notAfter_secs = 0 # return certificate in a string, desired format given in outform def readcertfile(self,outform='PEM'): if not (string.lower(outform) in certformats): raise ValueError,'invalid certificate output format' f = os.popen('%s crl -in %s -inform %s -outform %s' % (openssl.bin_filename,self.filename,self.format,outform)) buf = f.read() result = [] while buf: result.append(buf) buf = f.read() rc = f.close() if rc and rc!=256: raise IOError,"Error %s: %s" % (rc,opensslcommand) return string.join(result) ######################################################################## # SPKACClass ######################################################################## class SPKACClass: def __init__(self,spkacfilename,inform=''): self.filename = spkacfilename self.data = {} spkacfile = open(self.filename,'r') s = spkacfile.readline() while s: try: attr,value=string.split(s,'=',1) self.data[string.strip(attr)]=string.strip(value) except ValueError: pass s = spkacfile.readline() pyca-20031118/pylib/cgiforms.py0100644000076400001440000004501207745152531015552 0ustar michaelusers""" cgiforms.py - class library for handling
input (c) by Michael Stroeder This module is distributed under the terms of the GPL (GNU GENERAL PUBLIC LICENSE) Version 2 (see http://www.gnu.org/copyleft/gpl.html) Klassen zum Behandeln s und zu CGI-BINs uebermittelten Forminhalten Anmerkungen: - Eingabefelder mit gleichen name-Attributen werden logisch zusammengefasst und muessen in der selben Reihenfolge registriert und angegeben werden. - Es werden nur Parameter aus dem QUERY_STRING (GET) oder einzelne Datensaetze vom Typ application/x-www-form-urlencoded (POST) als Parameter angenommen. """ __version__ = '0.9.20' import sys, os, types, string, re, urllib, charset class formFieldClass: """ Base class for all kind of single input fields. In most cases this class is not used directly since derivate classes for most types of input fields exist. """ def __init__( self, name, # Field name used in text, # User-friendly text describing this field maxlength=0, # maximum lenght of parameter value [Bytes] pattern='', # regex pattern of valid values either as string # or tuple (pattern,options) default='', # default value to be used in method inputfield() required=0, # mark field as mandantory accesskey='', # key for accessing this field to be displayed # by method inputfield() multiple=0, # allow multiple appearances of same parameter name # and store parameter list in self.content ): self.name = name self.text = text self.maxlength = maxlength self.default = default self.required = required self.accesskey = accesskey self.multiple = multiple self.charset = 'iso-8859-1' self.counter = 0 self.inputfield_template = r'%s' self.contentprint_template = r'%s' if multiple: self.content = [] else: self.content = '' if type(pattern) is types.TupleType: patternstring, patternoptions = pattern else: patternstring, patternoptions = pattern, 0 self.regex = re.compile('^%s$' % patternstring, patternoptions) def __accesskeyfield__(self): if self.accesskey: return 'accesskey="%s"' % (self.accesskey) else: return '' def __label__(self,label): if label: return '' % (self.name,self.text) else: return '' def setdefault(self,input): """ Set the default of a input field. Mainly this is used if self.default shall be changed after initializing the field object. """ self.default = input def put(self,input): """ Store the user's input into the field object. This method can be used to modify the user's input before storing it into self.content. """ if self.multiple and len(self.content) """ def __init__( self, name, text, maxlength=0, pattern='', default='', required=0, accesskey='', multiple=0, rows=10, cols=60 ): formFieldClass.__init__(self,name,text,maxlength,(pattern,re.M+re.S),default,required,accesskey,multiple) self.rows = rows self.cols = cols def inputfield(self,escape_html_chars='<>":={}()',label=0): """ Input field. """ return self.__label__(label)+self.inputfield_template % ( '' % ( self.name, self.name, self.__accesskeyfield__(), self.rows,self.cols, self.__defaultprint__() ) ) class formInputClass(formFieldClass): """ """ def __init__(self,name,text,maxlength=0,pattern='',default='',required=0,accesskey='',multiple=0,size=0): formFieldClass.__init__(self,name,text,maxlength,pattern,default,required,accesskey,multiple) if size: self.size = size else: self.size = maxlength def inputfield(self,escape_html_chars='<>":={}()',label=0): return self.__label__(label)+self.inputfield_template % ( '' % ( self.name,self.name,self.__accesskeyfield__(),self.maxlength,self.size,self.__defaultprint__() ) ) class formHiddenInputClass(formInputClass): """ """ def __init__(self,name,text,maxlength=0,pattern='',default='',required=0,accesskey='',multiple=0,show=0): formFieldClass.__init__(self,name,text,maxlength,pattern,default,required,accesskey,multiple) self.show = show def inputfield(self,escape_html_chars='<>":={}()',label=0): if self.show: default_str = self.__defaultprint__() else: default_str = '' return self.__label__(label)+self.inputfield_template % ( '%s' % ( self.name,self.name,self.__accesskeyfield__(),self.__defaultprint__(),default_str ) ) class formPasswordClass(formFieldClass): """ Mainly it's an own class because of own method contentprint() """ def __init__(self,name,text,maxlength=0,pattern='',required=0,accesskey='',multiple=0,size=0): formFieldClass.__init__(self,name,text,maxlength,pattern,'',required) if size: self.size = size else: self.size = maxlength def inputfield(self,escape_html_chars='<>":={}()',label=0): return self.__label__(label)+self.inputfield_template % ( '' % ( self.name,self.name,self.__accesskeyfield__(),self.maxlength,self.size ) ) def textprint(self): return len(self.content)*'*' def contentprint(self): return self.contentprint_template % (len(self.content)*'*') class formSelectClass(formFieldClass): """ ' % ( self.name,self.name,self.__accesskeyfield__(),self.size," multiple"*(self.multiselect>0))] for i in self.options: if type(i) is types.TupleType: optionvalue = i[0] optiontext = i[1] else: optionvalue = optiontext = i if type(self.default) is types.ListType: optionselected = optionvalue in self.default else: optionselected = optionvalue == self.default s.append( '' % ( charset.recode(charset.escapeHTML(optionvalue,escape_html_chars),self.charset,'html4'), ' selected'*(optionselected), charset.recode(charset.escapeHTML(optiontext,escape_html_chars),self.charset,'html4') ) ) s.append('') return self.__label__(label)+self.inputfield_template % string.join(s,'\n') class formRadioClass(formFieldClass): """ """ def __init__(self,name,text,options='',default='',required=0,accesskey='',multiple=0): # pattern and maxlength are determined from __init__ params if len(options): pattern = '(%s)' % string.join(map(re.escape,options),'|') maxlength = len(options[0]) for i in options[1:]: if len(i)>maxlength: maxlength=len(i) else: pattern = '' maxlength = 0 formFieldClass.__init__(self,name,text,maxlength,pattern,default,required,accesskey,multiple) self.options = options def inputfield(self,escape_html_chars='<>":={}()',label=0): s = [] for i in self.options: s.append('%s
' % ( self.name,self.name,self.__accesskeyfield__(),i,' checked'*(i==self.default),i ) ) return self.__label__(label)+self.inputfield_template % string.join(s,'\n') class formCheckboxClass(formFieldClass): """ """ def __init__(self,name,text,value='',checked=0,required=0,accesskey='',multiple=0): pattern = value maxlength = len(value) formFieldClass.__init__(self,name,text,maxlength,pattern,'',required) self.value = value self.checked = checked def inputfield(self,escape_html_chars='<>":={}()',label=0): return self.__label__(label)+self.inputfield_template % ( '' % ( self.name,self.name, self.__accesskeyfield__(), self.value,' checked'*self.checked ) ) class formKeygenClass(formFieldClass): """ """ def __init__(self,name,text,maxlength=0,required=0,accesskey='',multiple=0): formFieldClass.__init__( self, name, text, maxlength, (r'[ -z\r\n]*',re.M+re.S), required ) def put(self,input): input = string.translate(input, string.maketrans("",""),"\r") input = string.translate(input, string.maketrans("",""),"\n") formFieldClass.put(self,input) def inputfield(self,challenge,label=0): return self.__label__(label)+self.inputfield_template % ( '' % ( self.name,self.name,self.__accesskeyfield__(),challenge ) ) def contentprint(self): return self.contentprint_template % ('%d Bytes' % (len(self.content))) class formException(Exception): """ Base exception class to indicate form processing errors. """ def __init__(self, *args): self.args = args class formContentLengthException(formException): """ Length of ALL input data too large. """ def __init__(self,contentlength,maxcontentlength): formException.__init__(self,contentlength,maxcontentlength) self.contentlength = contentlength self.maxcontentlength = maxcontentlength def __str__(self): return 'Content length invalid. Expected at most %d bytes but received %d.\n' % ( self.maxcontentlength,self.contentlength ) class formParamNameException(formException): """ Parameter with unknown name attribute received. """ def __init__(self,name): formException.__init__(self,name) self.name = name def __str__(self): return 'Unknown parameter %s.\n' % (self.name) class formParamsMissing(formException): """ Required parameters are missing. """ def __init__(self,paramnamelist): formException.__init__(self,paramnamelist) self.missing = paramnamelist def __str__(self): return 'Required fields missing: %s\n' % ( string.join(self.missing,', ') ) class formParamContentException(formException): """ The user's input does not match the required format. """ def __init__(self,name,text,content,reqregex): formException.__init__(self,name,text,content,reqregex) self.name = name self.text = text self.content = content self.reqregex = reqregex def __str__(self): return 'Content of field %s does not match "%s". Input was: "%s"\n' % ( self.text,self.reqregex,self.content ) class formParamStructException(formException): """ Too many parameters with the same name attribute in user's input. """ def __init__(self,name,count,maxfields): formException.__init__(self,name,count,maxfields) self.name = name self.count = count self.maxfields = maxfields def __str__(self): return 'Expected at most %d parameters for field %s, got %d.\n' % ( self.maxfields,self.name,self.count ) class formParamLengthException(formException): """ User's input for a certain parameter was too long. """ def __init__(self,name,text,length,maxlength): formException.__init__(self,name,text,length,maxlength) self.name = name self.text = text self.length = length self.maxlength = maxlength def __str__(self): return 'Content too long. Field %s has %d characters but is limited to %d.\n' % ( self.text,self.length,self.maxlength ) class formClass: """ Class for declaring and processing a whole """ def __init__( self, inf = None, # Read from this file object env = os.environ, # dictionary holding the environment vars charset = '' # character set used when submitting the form ): # Dictionary der Eingabefelder-Objekte # {name:[FormFieldClass]} self.field = {} # Dictionary mit Ordnungszaehler der Eingabefelder-Objekte # {name:len(field)} self.fieldcounter = {} # Reihenfolgetreue Liste der vorgesehenen Eingabefelder-Namen self.keys = [] # Liste der tatsaechlich eingegebenen Eingabefelder-Namen self.inputkeys = [] # Skriptname self.env = env if inf: self.inf = inf else: self.inf = sys.stdin self.request_method = env['REQUEST_METHOD'] self.server_name = env.get('SERVER_NAME',env.get('HTTP_HOST','')) self.server_port = env.get('SERVER_PORT','') self.script_name = env['SCRIPT_NAME'] self.path_info = env.get('PATH_INFO','') self.query_string = env.get('QUERY_STRING','') self.http_user_agent = env.get('HTTP_USER_AGENT','') if charset: self.accept_charset = charset else: self.accept_charset = string.split(env.get('HTTP_ACCEPT_CHARSET','utf-8'),',')[0] if self.request_method=='POST': # Parameter von self.inf lesen self.contentlength = int(env['CONTENT_LENGTH']) self.query_string = self.inf.read(self.contentlength) elif self.request_method=='GET': self.query_string = env.get('QUERY_STRING','') self.contentlength = len(self.query_string) else: raise ValueError,"Invalid request method %s." % self.request_method self.maxcontentlength = 0 def add(self,formfield): """ Add a input field object to the form. """ formfield.setcharset(self.accept_charset) if self.field.has_key(formfield.name): formfield.counter = self.fieldcounter[formfield.name] self.field[formfield.name].append(formfield) self.fieldcounter[formfield.name] = self.fieldcounter[formfield.name] + 1 else: formfield.counter = 0 self.field[formfield.name] = [formfield] self.keys.append(formfield.name) self.fieldcounter[formfield.name] = 1 self.maxcontentlength = self.maxcontentlength + formfield.maxlength def getparams( self, ignoreemptyparams=0 # Ignore empty strings in user's input ): """ Process user's input and store the values in the field objects. When a processing error occurs formException (or derivatives) are raised. """ # Parse user's input inputlist = string.split(self.query_string,'&') # Any input present? if not len(inputlist): return datalength = 0 # Zaehlerdict. mit Index paramname paramnumindex = {} # Loop over all name attributes declared for param in inputlist: if param: # Einzelne Parametername/-daten-Paare auseinandernehmen paramname,paramdata = string.split(param,'=',1) paramname = string.strip(urllib.unquote_plus(paramname)) paramdata = string.strip(urllib.unquote_plus(paramdata)) datalength = datalength+len(paramdata) # Gesamtlaenge der Daten noch zulaessig? if datalength > self.maxcontentlength: formContentLengthException(datalength,self.maxcontentlength) # Unbekannter Parametername angegeben? if not (paramname in self.keys): raise formParamNameException(paramname) if paramnumindex.has_key(paramname): paramnumindex[paramname] = paramnumindex[paramname] + 1 else: paramnumindex[paramname] = 0 # Anzahl der Parameter gleichen Namens noch zulaessig? if paramnumindex[paramname] >= len(self.field[paramname]): if (self.field[paramname][0].multiple): paramnumindex[paramname] = 0 else: raise formParamStructException(paramname,paramnumindex[paramname]+1,len(self.field[paramname])) field = self.field[paramname][paramnumindex[paramname]] # input is empty string? if paramdata: # Zusaetzlich pruefen # Laenge gueltig? if len(paramdata) > field.maxlength: raise formParamLengthException(field.name,field.text,len(paramdata),field.maxlength) rm = field.regex.match(paramdata) if rm==None or rm.group(0)!=paramdata: raise formParamContentException(field.name,field.text,paramdata,field.regex.pattern) # Eingabe ist gueltig und wird in content uebernommen if not paramname in self.inputkeys: self.inputkeys.append(paramname) else: if (not ignoreemptyparams) and (not paramname in self.inputkeys): self.inputkeys.append(paramname) # Store user's input in form field object field.put(paramdata) # Are all required parameters present? missing_params = [] for param in self.keys: for i in self.field[param]: if i.required and not (param in self.inputkeys): missing_params.append((i.name,i.text)) if missing_params: raise formParamsMissing(missing_params) return pyca-20031118/pylib/cgissl.py0100644000076400001440000002562007136144234015223 0ustar michaelusers############################################################################## # cgissl.py 0.4.1 # (C) 1998 by Michael Stroeder ############################################################################## # This module is distributed under the terms of the # GPL (GNU GENERAL PUBLIC LICENSE) Version 2 # (see http://www.gnu.org/copyleft/gpl.html) ############################################################################## import sys, os, re, string, charset def GetAllSSLEnviron(): SSLEnv = {} HTTPS = os.environ.get('HTTPS','off') if HTTPS=='on': SSLEnv['SSL_CIPHER_ALGKEYSIZE'] = \ os.environ.get('SSL_CIPHER_ALGKEYSIZE', os.environ.get('HTTPS_KEYSIZE', os.environ.get('SSL_KEYSIZE', os.environ.get('SSL_SERVER_KEY_SIZE', '')))) SSLEnv['SSL_CIPHER_EXPORT'] = \ os.environ.get('SSL_CIPHER_EXPORT', os.environ.get('HTTPS_EXPORT', os.environ.get('SSL_EXPORT', ''))) SSLEnv['SSL_CIPHER'] = \ os.environ.get('SSL_CIPHER', os.environ.get('HTTPS_CIPHER', '')) SSLEnv['SSL_CIPHER_USEKEYSIZE'] = \ os.environ.get('SSL_CIPHER_USEKEYSIZE', os.environ.get('HTTPS_SECRETKEYSIZE', os.environ.get('SSL_SECKEYSIZE', ''))) SSLEnv['SSL_CLIENT_A_SIG'] = \ os.environ.get('SSL_CLIENT_A_SIG', os.environ.get('SSL_CLIENT_SIGNATURE_ALGORITHM', '')) SSLEnv['SSL_CLIENT_CERT'] = \ os.environ.get('SSL_CLIENT_CERT', os.environ.get('SSL_CLIENT_CERTIFICATE', '')) SSLEnv['SSL_CLIENT_I_DN'] = \ os.environ.get('SSL_CLIENT_I_DN', os.environ.get('SSL_CLIENT_IDN', '')) SSLEnv['SSL_CLIENT_I_DN_CN'] = \ os.environ.get('SSL_CLIENT_I_DN_CN', os.environ.get('SSL_CLIENT_ICN', '')) SSLEnv['SSL_CLIENT_I_DN_C'] = \ os.environ.get('SSL_CLIENT_I_DN_C', os.environ.get('SSL_CLIENT_IC', '')) SSLEnv['SSL_CLIENT_I_DN_Email'] = \ os.environ.get('SSL_CLIENT_I_DN_Email', os.environ.get('SSL_CLIENT_IEMAIL', '')) SSLEnv['SSL_CLIENT_I_DN_L'] = \ os.environ.get('SSL_CLIENT_I_DN_L', os.environ.get('SSL_CLIENT_IL', '')) SSLEnv['SSL_CLIENT_I_DN_O'] = \ os.environ.get('SSL_CLIENT_I_DN_O', os.environ.get('SSL_CLIENT_IO', '')) SSLEnv['SSL_CLIENT_I_DN_OU'] = \ os.environ.get('SSL_CLIENT_I_DN_OU', os.environ.get('SSL_CLIENT_IOU', '')) SSLEnv['SSL_CLIENT_I_DN_SP'] = \ os.environ.get('SSL_CLIENT_I_DN_SP', os.environ.get('SSL_CLIENT_ISP', '')) SSLEnv['SSL_CLIENT_M_SERIAL'] = \ os.environ.get('SSL_CLIENT_M_SERIAL', os.environ.get('SSL_CLIENT_CERT_SERIAL', '')) SSLEnv['SSL_CLIENT_S_DN'] = \ os.environ.get('SSL_CLIENT_S_DN', os.environ.get('SSL_CLIENT_DN', '')) SSLEnv['SSL_CLIENT_S_DN_CN'] = \ os.environ.get('SSL_CLIENT_S_DN_CN', os.environ.get('SSL_CLIENT_CN', '')) SSLEnv['SSL_CLIENT_S_DN_C'] = \ os.environ.get('SSL_CLIENT_S_DN_C', os.environ.get('SSL_CLIENT_C', '')) SSLEnv['SSL_CLIENT_S_DN_Email'] = \ os.environ.get('SSL_CLIENT_S_DN_Email', os.environ.get('SSL_CLIENT_EMAIL', '')) SSLEnv['SSL_CLIENT_S_DN_L'] = \ os.environ.get('SSL_CLIENT_S_DN_L', os.environ.get('SSL_CLIENT_L', '')) SSLEnv['SSL_CLIENT_S_DN_O'] = \ os.environ.get('SSL_CLIENT_S_DN_O', os.environ.get('SSL_CLIENT_O', '')) SSLEnv['SSL_CLIENT_S_DN_OU'] = \ os.environ.get('SSL_CLIENT_S_DN_OU', os.environ.get('SSL_CLIENT_OU', '')) SSLEnv['SSL_CLIENT_S_DN_SP'] = \ os.environ.get('SSL_CLIENT_S_DN_SP', os.environ.get('SSL_CLIENT_SP', '')) SSLEnv['SSL_CLIENT_V_END'] = \ os.environ.get('SSL_CLIENT_V_END', os.environ.get('SSL_CLIENT_CERT_END', '')) SSLEnv['SSL_CLIENT_V_START'] = \ os.environ.get('SSL_CLIENT_V_START', os.environ.get('SSL_CLIENT_CERT_START', '')) SSLEnv['SSL_PROTOCOL'] = \ os.environ.get('SSL_PROTOCOL', os.environ.get('SSL_PROTOCOL_VERSION', '')) SSLEnv['SSL_SERVER_A_SIG'] = \ os.environ.get('SSL_SERVER_A_SIG', os.environ.get('SSL_SERVER_SIGNATURE_ALGORITHM', '')) SSLEnv['SSL_SERVER_CERT'] = \ os.environ.get('SSL_SERVER_CERT', os.environ.get('SSL_SERVER_CERTIFICATE', '')) SSLEnv['SSL_SERVER_I_DN_CN'] = \ os.environ.get('SSL_SERVER_I_DN_CN', os.environ.get('SSL_SERVER_ICN', '')) SSLEnv['SSL_SERVER_I_DN_C'] = \ os.environ.get('SSL_SERVER_I_DN_C', os.environ.get('SSL_SERVER_IC', '')) SSLEnv['SSL_SERVER_I_DN_Email'] = \ os.environ.get('SSL_SERVER_I_DN_Email', os.environ.get('SSL_SERVER_IEMAIL', '')) SSLEnv['SSL_SERVER_I_DN_L'] = \ os.environ.get('SSL_SERVER_I_DN_L', os.environ.get('SSL_SERVER_IL', '')) SSLEnv['SSL_SERVER_I_DN_O'] = \ os.environ.get('SSL_SERVER_I_DN_O', os.environ.get('SSL_SERVER_IO', '')) SSLEnv['SSL_SERVER_I_DN'] = \ os.environ.get('SSL_SERVER_I_DN', os.environ.get('SSL_SERVER_IDN', '')) SSLEnv['SSL_SERVER_I_DN_OU'] = \ os.environ.get('SSL_SERVER_I_DN_OU', os.environ.get('SSL_SERVER_IOU', '')) SSLEnv['SSL_SERVER_I_DN_SP'] = \ os.environ.get('SSL_SERVER_I_DN_SP', os.environ.get('SSL_SERVER_ISP', '')) SSLEnv['SSL_SERVER_M_SERIAL'] = \ os.environ.get('SSL_SERVER_M_SERIAL', os.environ.get('SSL_SERVER_CERT_SERIAL', '')) SSLEnv['SSL_SERVER_S_DN'] = \ os.environ.get('SSL_SERVER_S_DN', os.environ.get('SSL_SERVER_DN', '')) SSLEnv['SSL_SERVER_S_DN_CN'] = \ os.environ.get('SSL_SERVER_S_DN_CN', os.environ.get('SSL_SERVER_CN', '')) SSLEnv['SSL_SERVER_S_DN_C'] = \ os.environ.get('SSL_SERVER_S_DN_C', os.environ.get('SSL_SERVER_C', '')) SSLEnv['SSL_SERVER_S_DN_Email'] = \ os.environ.get('SSL_SERVER_S_DN_Email', os.environ.get('SSL_SERVER_EMAIL', '')) SSLEnv['SSL_SERVER_S_DN_L'] = \ os.environ.get('SSL_SERVER_S_DN_L', os.environ.get('SSL_SERVER_L', '')) SSLEnv['SSL_SERVER_S_DN_O'] = \ os.environ.get('SSL_SERVER_S_DN_O', os.environ.get('SSL_SERVER_O', '')) SSLEnv['SSL_SERVER_S_DN_OU'] = \ os.environ.get('SSL_SERVER_S_DN_OU', os.environ.get('SSL_SERVER_OU', '')) SSLEnv['SSL_SERVER_S_DN_SP'] = \ os.environ.get('SSL_SERVER_S_DN_SP', os.environ.get('SSL_SERVER_SP', '')) SSLEnv['SSL_SERVER_V_END'] = \ os.environ.get('SSL_SERVER_V_END', os.environ.get('SSL_SERVER_CERT_END', '')) SSLEnv['SSL_SERVER_V_START'] = \ os.environ.get('SSL_SERVER_V_START', os.environ.get('SSL_SERVER_CERT_START', '')) SSLEnv['SSL_VERSION_LIBRARY'] = \ os.environ.get('SSL_VERSION_LIBRARY', os.environ.get('SSL_SSLEAY_VERSION', '')) return SSLEnv ############################################################################## # Determine Security Level ############################################################################## def SecLevel(acceptedciphers,valid_dn_regex='',valid_idn_regex=''): SSL_CIPHER = os.environ.get('SSL_CIPHER', os.environ.get('HTTPS_CIPHER', '')) # SSL-Verbindung? if SSL_CIPHER and (SSL_CIPHER in acceptedciphers): SSL_CLIENT_S_DN = os.environ.get('SSL_CLIENT_S_DN', os.environ.get('SSL_CLIENT_DN', '')) if SSL_CLIENT_S_DN: SSL_CLIENT_I_DN = os.environ.get('SSL_CLIENT_I_DN', os.environ.get('SSL_CLIENT_IDN', '')) dn_rm = re.compile(valid_dn_regex).match(SSL_CLIENT_S_DN) idn_rm = re.compile(valid_idn_regex).match(SSL_CLIENT_I_DN) if (dn_rm) and \ (idn_rm): return 2 else: return 1 else: return 1 return 0 ############################################################################## # Print the SSL data in HTML format ############################################################################## def PrintSecInfo(acceptedciphers,valid_dn_regex='',valid_idn_regex='',f=sys.stdout): seclevel = SecLevel(acceptedciphers,valid_dn_regex,valid_idn_regex) f.write("""

Security level

Current security level is: %d

0 no encryption at all
1 Session is encrypted with SSL and cipher is accepted
2 Client presented valid certificate,
the DN of the certified object matches "%s"
and the DN of the certifier matches "%s"
""" % (seclevel,valid_dn_regex,valid_idn_regex)) if seclevel>=1: SSL_CIPHER_ALGKEYSIZE = os.environ.get('SSL_CIPHER_ALGKEYSIZE', os.environ.get('HTTPS_KEYSIZE', os.environ.get('SSL_KEYSIZE', os.environ.get('SSL_SERVER_KEY_SIZE', '')))) SSL_CIPHER_EXPORT = os.environ.get('SSL_CIPHER_EXPORT', os.environ.get('HTTPS_EXPORT', os.environ.get('SSL_EXPORT', ''))) SSL_CIPHER = os.environ.get('SSL_CIPHER', os.environ.get('HTTPS_CIPHER', '')) SSL_CIPHER_USEKEYSIZE = os.environ.get('SSL_CIPHER_USEKEYSIZE', os.environ.get('HTTPS_SECRETKEYSIZE', os.environ.get('SSL_SECKEYSIZE', ''))) SSL_SERVER_S_DN = os.environ.get('SSL_SERVER_S_DN', os.environ.get('SSL_SERVER_DN', '')) SSL_SERVER_I_DN = os.environ.get('SSL_SERVER_I_DN', os.environ.get('SSL_SERVER_IDN', '')) f.write("""You connected with cipher %s, key size %s Bit, actually used key size %s Bit.

Server certificate

This certificate belongs to:
%s
This certificate was issued by:
%s
""" % ( SSL_CIPHER, SSL_CIPHER_ALGKEYSIZE, SSL_CIPHER_USEKEYSIZE, string.join(string.split(charset.asn12html4(SSL_SERVER_S_DN),'/'),'
'), string.join(string.split(charset.asn12html4(SSL_SERVER_I_DN),'/'),'
') )) if seclevel>=2: SSL_CLIENT_I_DN = os.environ.get('SSL_CLIENT_I_DN', os.environ.get('SSL_CLIENT_IDN', '')) SSL_CLIENT_S_DN = os.environ.get('SSL_CLIENT_S_DN', os.environ.get('SSL_CLIENT_DN', '')) f.write("""

Your client certificate

This certificate belongs to:
%s
This certificate was issued by:
%s
""" % ( string.join(string.split(charset.asn12html4(SSL_CLIENT_S_DN),'/'),'
'), string.join(string.split(charset.asn12html4(SSL_CLIENT_I_DN),'/'),'
') )) pyca-20031118/htdocs/0040755000076400001440000000000007756443113013536 5ustar michaeluserspyca-20031118/htdocs/ssi/0040755000076400001440000000000007756443113014334 5ustar michaeluserspyca-20031118/htdocs/ssi/head.html0100644000076400001440000000165107254265556016131 0ustar michaelusers pyCA - <!--#echo var="pyca_subtitle" --> " /> pyca-20031118/htdocs/ssi/footer.html0100644000076400001440000000043207645600162016510 0ustar michaelusers
Page last modified: , sponsored by
stroeder.com - Information Technology, IT-Security, Identity Management, System Integration

pyca-20031118/htdocs/ssi/navigation.html0100644000076400001440000000243107276534735017366 0ustar michaelusers

pyCA -

General
<Download> <News> <Demo> <Related>
Support
<Commercial> <Feedback> <FAQ>
Documentation
<Features> <Overview> <Installing> <Configuration> <Changes> <Files> <Roadmap>
pyca-20031118/htdocs/help/0040755000076400001440000000000007756443113014466 5ustar michaeluserspyca-20031118/htdocs/help/client-enroll.html.de0100644000076400001440000002042207745153414020507 0ustar michaelusers TestCA - Angaben in der Zertifikatsanforderung

Angaben in der Zertifikatsanforderung


country ISO code (ISO-Bezeichnung des Landes)

Hier geben Sie die ISO-Kurzbezeichnung ihres Landes an.

Gültige Eingaben

Es sind ausschließlich zweibuchstabige Bezeichnungen erlaubt. Länderspezifische Buchstaben (wie z.B. Umlaute), Ziffern und sonstige Sonderzeichen ergeben eine Fehlermeldung.

Beispiele:

'DE' für Deutschland
'US' für U.S.A.

Staat

Voller Name des Bundeslandes/-staates oder einer Provinz.

Gültige Eingaben

Es sind nur alphanumerische Eingaben (Buchstaben, länderspezifische Buchstaben und Ziffern) und bestimmte Sonderzeichen ('.', '_' ,'-' und das Leerzeichen) erlaubt.

Beispiele:

'New Jersey'
'Rheinland-Pfalz'
'Deutschland; Baden' ergibt Fehlermeldung wegen Semikolon

Ort

Gültige Eingaben

Es sind nur alphanumerische Eingaben (Buchstaben, länderspezifische Buchstaben und Ziffern) und bestimmte Sonderzeichen ('.', '_' ,'-' und das Leerzeichen) erlaubt.

Beispiele:

'Frankfurt a.d. Oder'
'Frankfurt/Oder' ergibt Fehlermeldung wegen Schrägstrich

Organisation

Name der Organisation (z.B. Firma, staatliche Behörde, Verein etc.)

Gültige Eingaben

Es sind nur alphanumerische Eingaben (Buchstaben, länderspezifische Buchstaben und Ziffern) und bestimmte Sonderzeichen ('.', '_' ,'-' und das Leerzeichen) erlaubt.

Beispiele:

'BLOCKflÖte' e.V. ist erlaubt
'Karneval-/Stimmungsverein' ergibt Fehlermeldung wegen Schrägstrich

Abteilung

Gültige Eingaben

Es sind nur alphanumerische Eingaben (Buchstaben, länderspezifische Buchstaben und Ziffern) und bestimmte Sonderzeichen ('.', '_' ,'-' und das Leerzeichen) erlaubt.

Beispiele:

'Information & Kommunikation' ergibt Fehlermeldung wegen & (Ampersand)
'Abt. 08/15' ergibt Fehlermeldung wegen Schrägstrich

Name

Sie müssen einen allgemein verwendeten Namen angeben. Meist ist dies Vorname und Nachname einer Person.

Gültige Eingaben

Es sind nur alphanumerische Eingaben (Buchstaben, länderspezifische Buchstaben und Ziffern) und bestimmte Sonderzeichen ('.', '_' ,'-' und das Leerzeichen) erlaubt.

Beispiele:

'Karl Linder' ist eine gültige Angabe
'Alexander, der Große' ergibt Fehlermeldung wegen Komma (mal abgesehen von der schlechten Grammatik ;-)

Initialen

Falls es in ihrer Organisation üblich ist ein Namens-Kürzel zu verwenden, so geben Sie dies bitte an. Meist sind das z. B. die Initialen von Vor- und Nachname.

Gültige Eingaben

Es sind nur max. 5 alphanumerische Zeichen (Buchstaben, länderspezifische Buchstaben und Ziffern) erlaubt.

Beispiele:

'KL' ist eine gültige Angabe
'A-dG' ergibt Fehlermeldung wegen Bindestrich

E-Mail

Hier ist zwingend erforderlich eine gültige E-Mail-Adresse anzugeben. Ohne diese Angabe kann die weitere Bearbeitung der Zertifikatsanforderung nicht erfolgen.

Gültige Eingaben

Es sind alle Zeichen erlaubt, welche üblicherweise in E-Mail Adressen vorkommen. Das sind alle Buchstaben (ohne länderspezifische Buchstaben) und besondere Sonderzeichen ('@', '.', '=', '/', '-', '_' und das Leerzeichen).

Beispiele:

'michael@stroeder.com' ist gültig
'michael.ströder@inka.de' ergibt Fehlermeldung wegen Umlaut
'kein.benutzer@gibt.es.nicht' ergibt Fehlermeldung wegen ungültiger Internet-Domäne

Telefon- und FAX-Nummern

Zur weiteren Überprüfung der Identität ist die Angabe von Telefon- und FAX-Nummern hilfreich. Beide Angaben sind später nicht Bestandteil des Zertifikates und werden in keinem Fall veröffentlicht.

Gültige Eingaben

Die Nummern müssen im internationalen Format
+[Ländercode]-[Vorwahl]-[Anschlussnummer]
eingegeben werden. Der Ländercode darf nur aus 2 Ziffern, Vorwahl und Anschlussnummer aus einer Ziffernfolge bestehen.

Beispiele:

'+49-721-96506' ist gültig
'0721/9650-6' ergibt Fehlermeldung wegen Schrägstrich

Ansprechpartner

Gültige Eingaben

Zur weiteren Überprüfung ihrer Identität ist die Angabe ihres Ansprechpartners bei uns hilfreich. Es sind nur alphanumerische Eingaben (Buchstaben, länderspezifische Buchstaben und Ziffern) und bestimmte Sonderzeichen ('.', '_' ,'-' und das Leerzeichen) erlaubt.

Beispiele:

'Michael Ströder' ist eine gültige Angabe
'Alexander, der Große' ergibt Fehlermeldung wegen Komma (mal abgesehen von der schlechten Grammatik ;-)

Gültigkeitsdauer

Hier geben Sie an wieviele Tage das Zertifikat gültig sein soll. Die Zertifizierungsstelle entscheidet über die tatsächliche Gültigkeitsdauer in Abhängigkeit der jeweiligen Policy.

Initial Master Secret

Hier geben Sie ein Kennwort an, welches Sie vorher out-of-band von der Zertifizierungsstelle erhalten haben. Dies sichert zusätzlich die Identitätsprüfung.

Passwort

Hier geben Sie ein Passwort an, welches Sie im Falle eines von Ihnen initiierten Schlüsselrückrufs mit angeben müssen. Dies dient dazu einen nichtauthorisierten Rückruf ihres Zertifikats durch Dritte zu verhindern.
Dieses Passwort wird bei der Eingabe nicht angezeigt. Um sicher zu gehen, daß dabei keine Tippfehler passieren, wird das Passwort zweimal eingegeben.

Schlüssellänge

Hier geben Sie die Länge des zu erzeugenden RSA-Schlüssels an. Netscape erzeugt beim Abschicken des Formulars ein entsprechendes Schlüsselpaar und sendet den öffentlichen Schlüssel mit der Zertifikatsanforderung an den Server.

Anmerkung:

Längere Schlüssel benötigen mehr Rechenleistung, was aber bei heutigen Arbeitsplatzrechnern kein Problem mehr darstellt. Wählen Sie also am besten immer die längste zur Verfügung stehende Schlüssellänge (1024 Bit).

Die Auswahlmöglichkeiten der Schlüssellänge ist von der verwendeten Netscape-Version abhängig.
Wegen Restriktionen in den U.S.-amerikanischen Bestimmungen bzgl. dem Export von Verschlüsselungstechnik, können die Export-Versionen des Netscape Navigators max. nur RSA-Schlüssel mit 512 Bit erstellen. (Sollten Sie eine solche Netscape-Version besitzen, so lohnt sich ein Blick auf www.fortify.net oder ftp.replay.com.)

Gültige Eingaben

Die Auswahlmöglichkeiten der Schlüssellänge wird von Netscape (oder anderen Browser, die das <KEYGEN>-Tag beachten) bereitgestellt. Es sind RSA-Schlüssel der Länge 512 Bit, 768 Bit und 1024 Bit möglich. Für manche Arten von Zertifikaten ist ein bestimmte Mindestlänge vorgeschrieben. Bitte lesen sie die betreffende Policy. pyca-20031118/htdocs/help/client-enroll.html.en0100644000076400001440000002241507232753760020526 0ustar michaelusers SSL Certificates - User help

SSL Certificates

What are they and how to create them here

Most web traffic is sent unencrypted. That is, anyone with access to the right tools can view most of the traffic that travels the Web. In some circumstances this can be undesirable, such as in credit card and bank transactions.

Where greater web data security is needed, the Secure Socket Layer (SSL) is used to encrypt the data stream between the server and the client (usually a web browser).

If it is true that SSL securely encrypts data travelling over the Internet, then why is a certificate necessary?

The simple answer is that it is NOT!

However, certificates are still useful: A certificate, signed by a trusted Certificate Authority (CA), is designed to ensure that the certificate holder is really who they claim to be. Without a trusted, signed certificate, your data may still be encrypted but you can't be sure who you are communicating with.

If you need a certificate then keep reading and find out more below.


Specifications for certificate requests

Country Code (ISO designation of the country)

Enter the ISO short-name (2 letter) country ID here.

Valid inputs:

Enter two uppercase letters. Special characters (e.g. umlauts), digits and other special characters are disallowed and will result in an error message.

Examples:

' AU ' for Australia
' US ' for U.S.A.

State or Province

Full official name of the region, state or province.

Valid inputs:

Alphanumeric characters (letters, country-specific letters and digits). Some additional special characters ('.', ' _ ', ' - ' and the blank) are allowed. NB: semicolon and some others are disallowed.

Examples:

' New South Wales '
' new jersey '
' Germany; Bathe ' results in error message because of semicolon

City or Locality(LN)

Valid inputs:

Alphanumeric characters (letters, country-specific letters and digits) and some special characters ('.', ' _ ', ' - ' and the blank) are allowed (NB: semicolon and some others are disallowed).

Examples:

' Sydney '
' Washington D.C. '
' Frankfurt a.d. Or '
' Frankfurt/Oder ' results in error message because of diagonal stroke

Name of Organisation

Name of the organisation (e.g. company, national authority, association etc..)

Valid inputs:

Alphanumeric characters (letters, country-specific letters and digits) and certain special characters ('.', ' _ ', ' - ' and the blank) are allowed (NB: semicolon and some others are disallowed).

Examples:

' Microshaft Inc. ' is permitted
' Karneval /Stimmungsverein ' results in error message because of diagonal stroke

Department or Organisational Unit

Valid inputs

Alphanumeric characters (letters, country-specific letters and digits) and certain special characters ('.', ' _ ', ' - ' and the blank) are allowed (NB: semicolon and some others are disallowed).

Examples:

' IT Department '
' Network Services Division '
' information & communication ' results in error message because of & (Ampersand)
' Abbott 08/15 ' results in error message because of diagonal stroke

Common Name

If you are registering a certificate for a server, then the Common Name MUST be the fully qualified domain name of that server.

Otherwise....

If the certificate is for electronic mail or client identity, the Common Name is usually the first name and surname of a person (your own name!).

Valid inputs

Alphanumeric characters (letters, country-specific letters and digits) and certain special characters ('.', ' _ ', ' - ' and the blank) are allowed (NB: semicolon and some others are disallowed).

Examples:

' www.secure.site.com' is a valid name for a server certificate.
' Elvis Presley ' is a valid name for a client certificate.
' Elvis, the large one ' results in error message because of the comma (irrespective of the bad grammar; -)

Initials

If your organization commonly uses a name contraction (for example, MS instead of Microsoft), then enter this here please. This may also be the well known initials of a person - e.g. HRH or FUBAR

Valid inputs

Enter up to a maximum of five (5) alphanumeric characters (letters, country-specific letters and digits).

Examples:

' KL ' is a valid specification
' a-dG ' results in error message because of the hyphen

E-mail Address

NB: You MUST enter a valid E-Mail address. This certificate request will fail unless a valid email address is entered. The E-Mail address is checked for plausibility before the request is processed.

Valid inputs

All characters which are likely to be found in a valid email address are permitted. This includes are letters and special special characters ('@', '. ', ' = ', ' / ', ' - ', ' _ ' and the blank), but excluding country-specific characters such as umlaut.

Examples:

' michael@badexaple.com.au ' Won't work - it's not a registered domain name
' ben.venudo@to.no.where ' results in error message because of invalid Internet domain

Telephone and FAX Numbers

For more exact identification, the specification of telephone and FAX numbers is sometimes helpful. This information is not required, and even if entered here, it will not be published.

Valid inputs:

Plus sign and numbers only. The numbers must be entered in standard international telephone number format (or an error message will be generated).
+[CountryCode] [AreaCode] [LocalNumber]
The CountryCode may consist only of 2 digits.

Examples:

' +49 7219 6506 ' is valid
' +41 7219/9650 ' is invalid because of diagonal stroke

Contact Person

Valid inputs:

The name of a contact person is sometimes helpful. Alphanumeric characters (letters, country-specific letters and digits) and some special characters ('.', ' _ ', ' - ' and the blank) are allowed.

Examples:

' Michael Stroeder ' is a valid entry
' Bernie, at reception ' is invalid because of the comma

Valid Number Of Days

Enter the number of days from now, until the time this certificate will expire (e.g. valid for one year is 365 days!). The actual validity period is usually fixed by the Certification Authority as a matter of Policy .

Challenge Password

This is the Challenge Secret or Initial Master Secret password. This is a password, which you have choose to use for communication with with the certification body. This is not always required but it does provide additional protection.

User Password

This is an optional password which you use to manage your certificate. This password protects against non-authorized recall of the certificate by third parties. This password is not displayed during input. In order to check for typing errors, the password must be input twice.

RSA Key length

Enter the length of the RSA code. The RSA code is NOT the same as the certificate: The RSA key is used by some browsers when transmitting a certificate request to the server.

Note:

It is usually advisable to select the longest key available (usually 1024 bits).

The actual key length may depend on the browser version.
Because of U.S. regulations, some versions of Netscape navigator can only use RSA code with a maximum of 512 bits. Please visit these links for more information: www.fortify.net is worthwhile anyhow, and also ftp.replay.com .

Valid inputs:

Enter the key length (number of bits) used by the browser. For RSA code, possible values are 512 bits, 768 bits and 1024 bits.

Some types of certificates have a fixed minimum length. Please consult the local CA Policy documents for further information. pyca-20031118/htdocs/pyca.html0100644000076400001440000000411607660756463015370 0ustar michaelusers

Abstract

The usage of cryptographic techniques promises secure usage of Internet services concerning authentication of clients and servers and authorized access to sensitive data. During the last two years it turned out that X.509 certificates, SSL and S/MIME are the relevant, widely adopted cryptographic standards for securing various Internet services like WWW, Mail, etc.

However these standards require setting up a working X.509-based PKI (pulic key infrastructure). Although there is a quite lot of documentation and some example software for setting up a primitive PKI with an own certificate authority with the free package OpenSSL it seems that this task is not easy for most people. There is a lot of discussion on various mailing-lists, e.g. how to generate self-signed CA certificates, generate certificate requests with the famous WWW browsers and how to provide client certificates / certificate revocation lists for download, etc. Additionally if the certification business of an organization gets only a little bit more serious one has to take care about critical security issues.

pyCA tries to make it easier for people to set up and run a organizational certificate authority which fulfills the need for a fairly secure certification processing. The package also tries to reduce administrative tasks and user's frustration by providing a comfortable web interface to users contacting the certificate authority.

Project status

Unfortunately I do not have the time at the moment to spend more time on developing this project. I will apply bug fixes and patches submitted by users as long as they do not require too much rewriting of code.

pyca-20031118/htdocs/security.html0100644000076400001440000000042107232334223016253 0ustar michaelusers

...yet to be written...

pyca-20031118/htdocs/faq.html0100644000076400001440000000551607436202017015166 0ustar michaelusers
I have successfully created a certificate request. The file with suffix .spkac or .pem was stored in directory $dir/newreqs. How to issue the certificate?

You have to issue the cert manually by invoking "openssl ca" command.

Cert request created with Netscape or Opera:

openssl ca -name [name of CA section] -spkac [pathname of CSR.spkac]

Cert request created with M$ IE:

openssl ca -name [name of CA section] -in [pathname of CSR.pem]

This creates the certificate and stores it into newcerts/ as file [serial].pem. Call ca-cycle-pub.py afterwards and receive e-mail...

Do I need LDAP for deploying pyCA?
No. pyCA supports uploading certificates to a LDAP server but all data needed is stored in the directory structure in your file system.
How can I store the issued end-entity certificates on a LDAP host?
Mainly the certificates will be replicated by certs2ldap.py to a LDAP server by searching existing entries and adding the DER-encoded certificate into attribute userCertificate;binary. Expired certificates may be deleted (use carefully!).
New LDAP entries will not be created because most times the LDAP directory structure differs from the cert DN structure. It is up to your LDAP admin to create entries for the end entities.
How can I store the CA certificates and CRLs on a LDAP host?
ca2ldif.py can create a LDIF file of you CA cert hierarchy which you can upload to the LDAP server using the usual tools shipped with your LDAP server software.
Currently the CRLs are not updated on a regular basis.
It seems that during parsing the lines of openssl.cnf an exception is raised. Why is that happening?
Please check that the attribute values of single-valued configuration attributes do not contain a comma. A comma is used if a configuration attribute may have multiple values which are delimited by comma. (Frankly the openssl.cnf syntax and my parser suck both. I wouldn't use openssl.cnf for configuration today anymore.)
pyca-20031118/htdocs/features.html0100644000076400001440000000222607232346306016234 0ustar michaelusers

The following tasks will be provided by this package:

  • Generate CA certificate hierarchy and initials CRLs
  • Generate certificate requests with widely used web browsers
  • Search tool for client certificates in the OpenSSL certificate database
  • Download of client certificates / certificate revocation lists with appropriate MIME types
  • Online-validation of certificates
  • Storing all certificate data in LDAP repositories
  • Scripts for the handling of the certification process on a non-networked system holding the CA's private key(s)
  • Fairly easy configuration based on the OpenSSL configuration file (most times called openssl.cnf).

See the roadmap for a detailed list of what is still to be done.

pyca-20031118/htdocs/install.html0100644000076400001440000001004707514603714016066 0ustar michaelusers

Requirements

For running these scripts you need:

  • A Unix-like operating system (e.g. Linux, I would like to hear reports of pyCA running on other systems).
  • OpenSSL 0.9.4+

For running the CGI-BIN programs you need:

For using LDAP as a certificates repository you need:

Ready-to-use packages of the required software in current Linux distributions:

  • The Linux distribution S.u.S.E. already has RPM packages of Python, OpenSSL, Apache with mod_ssl, Netscape Navigator, OpenLDAP and ldapmodule (since 6.2) for Python.
  • Red Hat has included the Python interpreter with their Linux distribution. You might consider to grab some pre-packaged RPMs of OpenLDAP and ldapmodule.
  • There is a Debian package of pyCA itself. The Debian-Release (nickname Potato) includes packages of OpenSSL, Python, OpenLDAP and ldapmodule for Python as well as ApacheSSL and Apache with mod_ssl.

Documentation

Some rudimental documentation is available and included in the download archive above.

Installing

  • Install all required software.
  • Put the Python programs under cgi-bin/ anywhere in the CGI-BIN directory of your web server.
  • Put the Python modules under pylib/ anywhere in your Python path or adjust the list variable pylib in pycacnf.py.
  • You have to set up a reasonable OpenSSL configuration file openssl.cnf (see documentation, see example) and adjust variable cnf_filename in pycacnf.py.
  • You might want to create a CA hierarchy with ca-make.py.
  • call the CGI-BIN program ca-index to show some of the relevant data in your OpenSSL configuration file.
pyca-20031118/htdocs/roadmap.html0100644000076400001440000001036007232346306016037 0ustar michaelusers

There are still a lot things to do in pyCA.

Topic Status
Clean up the code and remove all special features to make it usable in a more common way. released (0.4.1)
Consequent use of the openssl.cnf throughout the whole package. released (0.4.1)
Make certificate enrollment process more comfortable to users, e.g. check parameters against openssl.cnf and give more detailed feedback about input errors. released (0.4.2)
Show clickable structure of openssl.cnf for downloading CA certificates and CRLs. released (0.4.2)
Documentation of configuration parameters released (0.4.3)
Script for generating of CA certificate hierarchies and initial CRLs released (0.4.5)
Speed up certificate loading by handling DER certificates released (0.4.5)
Documentation of all configuration parameters released (0.5.0)
Scripts ca-cycle-pub.py for cyclic CA tasks on the public server most done and released (0.5.0)
Update cnf-parsing to reflect the recent changes which were made in OpenSSL (complete rewrite). released (0.5.1)
Support for Microsoft Internet Explorer released (0.6.0)
Improve LDAP support. released (0.6.0)
Store initial master secrets in a database during registration process involving RA or user itself. Printing of registration info for postal shipment, automatic checking of initial master secret. to do
Scripts ca-cycle-priv.py for daily/hourly CA tasks on the system holding the private keys to do
English help texts. to do
Enrollment script server-enroll.py for server certificate requests. to do
Speed up access to bigger certificate databases by using the GDBM package for holding a copy of the certificate database to do
PKIX compliance (e.g. keyUsage etc.) to do
Flexible logging support for CGI-BINs to do
Documentation of the certification process to do
Script cert-renewal.py for certificate renewal requests to do
Script cert-revoke.py for certificate revocation requests to do
Instant certificate issueing (what some CAs call "Class 0") without admin interaction to do
Improving privacy of certified objects by implementing access control scheme to cert database (maybe just rely on LDAP bind) to do
Support for anonymized certificates for better privacy to do
Signing stored data and e-mails if possible (depends on further S/MIME support in OpenSSL) to do
Better localizing, multiple languages. to do
pyca-20031118/htdocs/overview.html0100644000076400001440000000447607232346306016275 0ustar michaelusers

This is an overview of the proposed systems architecure for running pyCA. Note that pyCA implements a certificate authority - not a trust center. This means:
No private keys of users are stored by pyCA at any time! The users themselves are responsible for making backup copies of their private keys and certificates!

Systems

The following systems are part of the public-key infrastructure (PKI):
Client system
This is the system of the user accessing the PKI services typically running a Mail, WWW and/or LDAP client software. The user creates the key pairs himself and stores his own private keys.
Public server system
The public server system(s) are holding only public certificate data like issued client-/server certificates and certificate revocation lists (CRLs) and are running Internet services like Mail, WWW and/or LDAP to give users access to the certificate data.
No private keys are stored on this system at all. However the systems administrator has to take care about securing this system in the usual manner (firewalls, no other users etc.). The services provided by this system should also be protected by the SSL protocol to ensure some kind of server authentication and integrity.
Private CA system
The private keys of the certificate authority should be hold on a non-networked system which is only accessible by persons authorized to issue certificates (e.g. a notebook put in a safe might be a practical choice). Data exchanged between this system and the public server system is transported with the help of removable media storage devices.
If better support for cryptographic devices is available in OpenSSL it is highly recommended that the private key data is stored e.g. on smart cards.
pyca-20031118/htdocs/files.html0100644000076400001440000001610007232331231015503 0ustar michaelusers

This is an overview of all Python programs in pyCA and is intended to give you an idea of what the package does.

bin/

The scripts in this section handle are simple helper scripts for copying public certificate data (mainly the CA certs).
Use [scriptname].py --help to find out more about the usage of each script.

ca2ldif.py
Write CA certificates and CRLs to a LDIF file. This is intended for initially setting up the CA entries not for daily CRL update. The entries are of objectclass certificationAuthority and contain the attributes cACertificate;binary, authorityRevocationList;binary and certificateRevocationList;binary. This might require extending schemas on LDAPv2 servers. Have a look at your LDAP servers configuration documentation.
certs2ldap.py
Send all certs and CRLs to a LDAP repository.
copy-cacerts.py
Copy all CA certificates defined in an OpenSSL configuration to a bundled PEM file or a directory with hash-named symbolic links. This is quite handy in conjunction with ApacheSSL or Apache with mod_ssl for copying the files for SSLCACertificateFile or SSLCACertificatePath.
ns-jsconfig.py
Create Javascript code containing all CA certificates defined in an OpenSSL configuration for use with the Netscape admin tool (creating netscape.cfg).
print-cacerts.py
This simple script prints all CA certs on stdout. It is intended to generate authentic printouts (on paper!) of the CA certs fingerprints and is typically run on the private CA system.
Choose the option --html to generate nicer formatted HTML-output instead of the default textual output in ISO-8859-1.
ns-jsconfig.py
Create a Javascript file to be included in a Netscape configuration file (netscape.cfg).

sbin/

The scripts in this section handle administrative tasks.
pickle-cnf.py
Create a pickled copy the OpenSSL configuration object for faster reading of the configuration. The pickle-file name is the name of the OpenSSL configuration file plus .pickle.
ca-make.py
Generate a CA hierarchy, all necessary files and directories and all initial CRLs (see also signedby extension in OpenSSL configuration file). This is intended to be run under user root since it sets the ownership and permissions.
ca-certreq-mail.py
Handles the mail dialogue after certificate request. The SPKAC certificate request and LDIF data is moved from the directory pend_reqs_dir to new_reqs_dir. Set this script in your /etc/aliases, procmailrc or similar to receive mails for the address specified in caCertReqMailAdr.
ca-cycle-pub.py
This script is typically run by the CA admin user via CRON or a similar task manager on a networked system holding the public certificate data. It does several jobs:
  • Publish new certificates and inform user via e-mail where to download his certificate
  • Remove stale certificate requests from pend_reqs_dir.
  • Spool certificate requests and certificate revocation requests to the system holding the CA's private keys. (not implemented yet)
  • Spool certificates and certificate revocation lists from the system holding the CA's private keys. (not implemented yet)
ca-cycle-priv.py
This script is run on the system where the private keys of the CA are stored. It does several jobs:
  • Mark expired certificates in OpenSSL certificate database
  • Generate new CRLs, move old CRLs to archive (not implemented yet)
  • Process certificate requests and certificate revocation requests (not implemented yet)
  • Spool certificate database, issued certificates and CRLs to public WWW and LDAP server (not implemented yet)

cgi-bin/

Several CGI-BIN programs provide comfortable user access to the PKI.

browser-check.py
Checks the SSL and key generation capabilities of a browser. This is very handy to find out if a certain web client has sufficient capabilities for the certification process or your policy.
Up to now this CGI-BIN does only make sense when being invoked on a server running ApacheSSL or Apache with mod_ssl with "SSLOptions +CompatEnvVars" set in httpd.conf.
ca-index.py
Show a clickable table of CA data in the OpenSSL configuration file openssl.cnf.
client-enroll.py
Generate a certification request with your favourite web browser.
cert-query.py
Query the OpenSSL certificate database for searching e-mail certificates.
view-cert.py
Display a certificate or CRL.
get-cert.py
Load a certificate or CRL.
ns-check-rev.py
On-line verification of a certificate
ns-revoke.py
Revoke a certificate with checking of client certificate.
scep.py
Experimental support for Cisco's Simple Certificate Enrollment Protocol (SCEP)

pylib/

misc. modules

htdocs/

pyca-20031118/htdocs/download.html0100644000076400001440000000166007756442727016245 0ustar michaelusers

Copyright and License

© by Michael Ströder, michael@stroeder.com

This software including all modules is Open Source and given away under:

GPL (GNU GENERAL PUBLIC LICENSE) Version 2
The author refuses to give any warranty of any kind.

Package

The files (see list) are provided as gzipped tar ball:

pyca-20031118/htdocs/config.html0100644000076400001440000004706507341245700015672 0ustar michaelusers

This document describes the parameters in the OpenSSL config file necessary for using pyCA.

Table of Contents

  • Module pycacnf.py
  • OpenSSL configuration file (openssl.cnf)
  • Section [ pyca ]
  • CA Sections
  • req Sections
  • policy Sections
  • x509_extensions Sections
  • Example
  • Abstract

    The goal of pyCA is to keep as much configuration data in OpenSSL's configuration file (most times called openssl.cnf) as possible because it is much easier to keep one file up-to-date and consistent. pyCA also tries to reduce administrative tasks and end-user's frustration by generating specific input forms for a certain CA definition (certificate type) to avoid users generating invalid certificate requests which does not fulfill the policy of a certificate authority.

    Although a lot of parameters already known in OpenSSL are used it was necessary to add some new sections and attributes for reaching this goal. These extensions have nothing to do with the OpenSSL-package itself and hopefully will not conflict with new parameters introduced in OpenSSL.

    Module pycacnf.py

    Although pyCA tries to get all configuration parameters from the OpenSSL config file there are two parameters which have to be configured in the Python module pycacnf.py which has to be located together with the CGI-BIN programs and scripts or in the system's Python path:
    • The path of the OpenSSL's config file (e.g. /usr/local/ssl/lib/openssl.cnf) is configured in the variable cnf_filename.
    • If you are not allowed to place Python modules in the system's Python path you can list more directories containing modules in the list variable pylib.
      Or simply set the environment variable PYCALIB to define the name of the directory containing the modules.

    OpenSSL configuration file

    pyCA tries to make use of as many parameters given in the OpenSSL configuration file as possible. Therefore you should set up all sections in a reasonable manner refering to OpenSSL's documentation. pyCA defines some additional parameters which are defined below. All parameter names are handled case-sensitive!

    Pre-compilation

    You can create a pre-compiled binary copy of your configuration file by running pickle-cnf.py to gain better performance. The pathname of the pre-compiled copy is always presumed to be cnf_filename with the suffix .pickle. The Python modules cPickle (if existent) or pickle are used for reading this pre-compiled copy. If this fails the source of the configuration file is read. You have to take care that the pre-compiled copy is up-to-date and reflects your actual configuration by running pickle-cnf.py after every change of the source!

    Section [ ca ]

    The term CA name used in this document specifies the name of a CA definition in the section [ ca ]. Each name of a CA has to be unique and points to a CA section which contains several parameters for this certain CA.

    Section [ pyca ]

    Some parameters only used by pyCA are configured in an own proprietary section named [ pyca ].

    Files and directories

    OpenSSLExec
    Full pathname of the openssl executable (default /usr/local/ssl/bin/openssl).
    CaFixExec
    ignored since 0.5.3 - use the more flexible ca_x509_extfile parameter
    TmpDir
    Directory for temporary files (default /tmp).
    caCertConfirmReqLog
    (since 0.6.6)
    Path to file for log output of ca-certreq-mail.py. The directory must be writeable for the user defined with parameter userMailDaemon.
    caPendCertReqDir
    ignored since 0.5.0 - use the more flexible pend_reqs_dir parameter
    caNewCertReqDir
    ignored since 0.5.0 - use the more flexible new_reqs_dir parameter
    userCAAdmin
    (since 0.5.4)
    Existing username of the user maintaining the CA data. Used for setting the ownership of various files and directories in ca-make.py.
    Example:
    userCAAdmin = caadmin
    userMailDaemon
    (since 0.5.4)
    Existing username of the user running the mail delivery demon. Used for setting the ownership of certificate request spool directories pend_reqs_dir and new_reqs_dir in ca-make.py.
    Example:
    userMailDaemon = daemon
    userWWWRun
    (since 0.5.4)
    Existing username of the user running the web server. Used for setting the ownership of certificate request spool directories pend_reqs_dir and new_reqs_dir in ca-make.py.
    Example:
    userWWWRun = wwwrun
    ErrorLog
    (since 0.5.3)
    Normally errors are written to sys.stderr which is most times written to the error log of the web server. You can redirect Python's sys.stderr to an dedicated error log file by providing a pathname here which is handy in situation where you do not have access to the web error logs. Note that the file has to be writeable by the user the web server runs as (see also userWWWRun).

    Mail-Options

    MailRelay
    The default SMTP mail relay.
    Example:
    MailRelay = mail.domain.my
    caAdminMailAdr
    (since 0.6.2)
    Mail address of the CA's administrator. Is used as default address for some tasks in ca-cycle-pub.py.
    caCertReqMailAdr
    Mail address of the mail dialogue script for certificate requests. If empty, no mail dialogue is initiated.

    CA-Options

    caCertFormat
    Ignored since 0.6.0!
    Specified the format of all stored certificate data. (nice idea but the code gets too messy...)
    PreferDERCerts
    ignored since 0.5.0
    caPendCertReqValid
    Amount of time [h] how long a pending certificate request is stored in pend_reqs_dir without being confirmed by e-mail. Set to zero to disable automatic deletion of stale certificate requests by ca-cycle-pub.py.
    caInternalCertTypes
    List CA names (see section [ ca ] for which certificate requests can only be created from an internal network (see caInternalIPAdr and caInternalDomains).
    The integrity of your PKI should not rely on such mechanisms!
    Example:
    caInternalCertTypes = Member, Admin
    caInternalIPAdr
    List of IP network addresses/-masks which are considered internal (see caInternalCertTypes).
    The integrity of your PKI should not rely on such mechanisms!
    Example:
    caInternalIPAdr = 127.0.0.0/255.0.0.0,10.0.0.0/255.0.0.0
    caInternalDomains
    List of email address domains which are considered internal (see caInternalCertTypes).
    The integrity of your PKI should not rely on such mechanisms!
    caInternalDomains = domain1.my,domain2.my,trusteddomain.other
    caIntermediateCACerts
    (since 0.6.2)
    List CA names (see section [ ca ] for which handling of intermediate CA certs should be provided. E.g. get-cert.py will send the intermediate CA certs as chain together with the downloaded certificate. Example:
    caIntermediateCACerts = Persona,Friends,Member

    WWW-Options

    nsBaseUrl
    Base-URL for the all URL addresses. This is meant as fallback option if the CA-specific attribute nsBaseUrl (see section x509_extensions) is not set.
    nsCAIndexUrl
    Relative URL address of ca-index.py.
    nsEnrollUrl
    Relative URL address of ns-enroll.py.
    nsGetCertUrl
    Relative URL address of get-cert.py
    nsViewCertUrl
    Relative URL address of view-cert.py
    HelpUrl
    Relative URL directory of help texts (e.g. ns-enroll-help.html).
    ScriptMethod
    Preferred HTTP method for submitting form parameters.
    Example:
    ScriptMethod = POST
    htmlBodyParam
    (since 0.4.5)
    Specifies the parameter string placed in the <BODY>-tag in the output of the CGI-BIN-programs
    htmlBodyText, htmlBodyLink, htmlBodyVlink, htmlBodyBgColor, htmlBodyBackground
    ignored since 0.4.5 - use the more flexible htmlBodyParam parameter

    CA Section

    Parameters for the CA definition (separate CA sections referred by the CA's name in section [ ca ]).

    Parameters already known in OpenSSL

    Many standard parameters of the OpenSSL config are used. Refer to the OpenSSL docs for reading about these.
    dir
    Where everything is kept
    certs
    Where the issued certs are kept
    crl_dir
    Where the issued crl are kept
    database
    Certificate database index file.
    new_certs_dir
    Default place for new certs.
    certificate
    The CA certificate
    serial
    The filename containing current serial number.
    crl
    The current CRL
    private_key
    The private key of the CA
    default_crl_days
    how long before next CRL
    policy
    Name of policy section
    x509_extensions
    Name of section with X.509v3 extension attributes

    Special parameters for pyCA

    signedby
    CA name of the CA which issued (or will issue) the CA certificate. This pyCA attribute in the CA definition section allows building CA hierarchies with ca-make.py. Up to now only a two-level hierarchy is supported.
    pend_reqs_dir
    (since 0.5.0)
    Directory for storing certificate requests (default $dir/pendreqs). This directory has to be writeable only for the user/group the web server is run as and readable/writeable for the local mail delivery demon. It has to be readable/writeable for CA administrator's user if you want to enable automatic deletion of stale certificate requests (see script ca-cycle-pub.py and option caPendCertReqValid).
    new_reqs_dir
    (since 0.5.0)
    Directory for storing certificate requests after mail dialogue (default $dir/newreqs). This directory has to be writable only for the mail delivery demon and readable/writeable for the CA administrator's user.
    req
    (since 0.5.0)
    Name of req section to be used when creating certificate requests for this CA definition. If this parameter is not set the section [ req ] is used.
    min_key_size
    (since 0.5.3)
    Minimum required key size depending on CAs policy (default 0). If this is set to a value greater than 0 there is a note displayed in ns-enroll.py right above the <KEYGEN>-tag to inform the user about the required key size of a certain certificate type and the submitted key size is checked.
    ca_reqfile
    (since 0.5.3)
    Pathname of the request config file to be used in ca-make.py for creating the certificate request for the CA certificate.
    ca_x509_extfile
    (since 0.5.3)
    Pathname of the file containing the extended X.509v3 attributes to be used in ca-make.py for signing the CA certificate.

    req Section

    Parts of the input form generated by ns-enroll.py for a specific certificate type is defined by a req section already known in OpenSSL.

    Parameters already known in OpenSSL

    [parameter]
    (since 0.4.2)
    Displayed description for this specific parameter.
    [parameter]_max
    (since 0.4.2)
    Maximum length for this specific parameter.
    [parameter]_default
    (since 0.4.2)
    Default for the specific parameter. If you define a comma-separated list a select list is generated in the input form.

    Special parameters for pyCA

    [parameter]_regex
    (since 0.4.2)
    Regular expression for valid strings.

    policy Section

    ns-enroll.py uses the definitions in the policy section for generating specific input forms depending on the certificate type to prevent users from generating invalid certificate requests.
    For each attribute name you can define one of the following policy options:
    matched
    The [parameter]_default is read from the req section and displayed as fixed input parameter in the input form (<INPUT TYPE=HIDDEN VALUE="[parameter]_default">).
    supplied
    An input field or select list with [parameter]_default is shown and the input field is handled as required.
    optional
    An input field or select list with [parameter]_default. The user is allowed to leave this input field empty.

    x509_extensions Section

    Several X.509v3 extensions defined in the x509_extensions section are used to display URLs or determine the type of certificate usage.
    nsComment
    This is used to display a nice comment in ca-index.py and ns-enroll.py about the certificate usage.
    nsBaseUrl
    This is the base URL for all other URL addresses.
    nsCaRevocationUrl
    This is used in ca-index.py to display a link for downloading the latest certificate revocation list (CRL) of a certificate authority.
    nsRevocationUrl
    Up to now this parameter is not used in pyCA.
    nsCaPolicyUrl
    This is used in ca-index.py and ns-enroll.py to display a link to the certification practice statement of a certificate authority.
    keyUsage
    This is used to determine the certificate usage according to PKIX (not implemented yet).
    nsCertType
    This is used to determine the certificate usage according to Netscape specification.
    pyca-20031118/htdocs/feedback.html0100644000076400001440000000111507264200126016130 0ustar michaelusers

    Your feedback is appreciated! Feel free to send e-mail to feedback@pyca.de.

    Please try to describe errors as precise as possible including your system OS and software involved.

    There is also commercial support available.

    pyca-20031118/htdocs/demo.html0100644000076400001440000000226007232346306015340 0ustar michaelusers

    Demo

    ca-index.py shows CA index of the example configuration file. You can download CA certificates and CRLs of my TestCA and call the application form for generating certificate requests.

    Please make backup copies of your certificate database and key files (e.g. cert7.db and key3.db in your Netscape's profile directory) before playing around with the CA certificates and CRLs.

    Only the Persona CA is meant for playing with client-side key generation with client-enroll.py and sending certificate requests. All other CA types are dedicated to my personal use.

    To all hacker wanna-be's: There are no private keys on this server!

    pyca-20031118/htdocs/related.html0100644000076400001440000000311707232346306016036 0ustar michaelusers

    Related work

    • This work was started during my diploma thesis which might be helpful for german readers to understand the architecture and goals of pyCA.
    • If you're eager using LDAP as certificate repository you might also consider using web2ldap for accessing your LDAP data via WWW.

    Apps

    What to do with client certificates? Here are some examples:
    • You can use client certs for strong user authentication with SSL capable web servers, e.g. ApacheSSL or Apache with mod_ssl.
    • You can use client certs for strong user authentication with some SSL tunnels, e.g. stunnel.
    • Or how about some client-cert based mail-relaying rules for the postfix-MTA patched with Postfix/TLS? Quite helpful for your mobile users!
    • Signed forms
    pyca-20031118/htdocs/changes.html0100644000076400001440000002361007745161740016034 0ustar michaelusers
    Release 0.6.6 (release date unknown)
    • Fixed compability issues with Python 1.6/2.0+/2.1.
    • Fixed compability issues with certificate requests created by Mozilla browser.
    • Added new parameter caCertConfirmReqLog for defining the path name of file for log output of sbin/ca-certreq-mail.py.
    • English translation of help for cgi-bin/client-enroll.py contributed by Roger Buck <rog@saas.nsw.edu.au>.
    • Use env variable PYCALIB to set name of the directory containing the modules.
    • Patches contributed by "Stan O. Barber" <sob@verio.net>.
    • If exception getopt.error is raised in command-line programs the string representation of the exception instance is displayed as error message.
    • VBScript-related fixes contributed by Christian Barmala <christian@barmala.de>.
    • Fixed MIME types sent for M$ IE in cgi-bin/get-cert.py (application/pkix-cert, see RFC 2585).
    • cgi-bin/get-cert.py can be called with path info "extension" specifying the output format (.der, .pem, .b64).
    • URLs pointing to get-cert.py have suffix .crt or .crl for defining a pseudo "file type". This triggers certificate/CRL handling in older versions of M$ IE which do not properly use the MIME-type for determining the file type.
    • Massive modifications to default configuration file shipped with package. The default configuration uses a domainComponent root naming now.
    • Fixed bug concerning validity interval checking when issuing sub CA certificates in ca-make.py.
    • Make use of parameter caAdminMailAdr in ca-certreq-mail.py for forming From: address if CA certificate does not contain Email attribute in subject DN.
    • Fixed case-handling of userCertificate atttibute in certs2ldap.py. The currently used attriute type name is determined and also used in displayed status messages.
    • bin/certs2ldap.py: now handles multi-valued userCertificate atttibutes correctly, can delete revoked or expired certificates, can add new LDAP entries.
    • No LDIF file created by client-enroll.py anymore.
    • If anyone messed up the ownership and permission of CA certificate and key file it's restored at the beginning of ca-make.py while still running as root.
    • Removed input of parameter userpassword in cgi-bin/client-enroll.py since it was never used. Well, the lack of a user administration concept shows here...
    • Text for confirmation e-mail was altered to avoid the message being caught by spam filters looking for string "ignore this" in message body.
    • Correctly evaluate form field browsertype in cgi-bin/client-enroll.py.
    • Applied patch to module vbs contributed by Reiner Keller for fixing compability issues with M$ IE 6.x.
    • Since most people did not configure the CA cert extensions properly some weird settings were removed.
    • Removed german umlauts from all Python and HTML sources.
    Release 0.6.5 (2000-07-30)
    • Bug fixes
    • New script bin/ldap2certs for downloading certs for stunnel and build relay_clientcerts file for Postfix/TLS.
    • bin/certs2ldap.py now deletes revoked and expired certs from LDAP server
    • cgi-bin/ns-revoke.py works now if web server has write access to OpenSSL index.txt (not recommended)
    Release 0.6.4 (2000-07-07)
    • Small bug fixes and cosmetic changes
    Release 0.6.3 (1999-12-27)
    • Added parameter --nocrls to bin/ca2ldif.py
    • Displays SHA-1 fingerprints in view-cert.py and print-cacerts.py now (mainly for users of M$ IE)
    Release 0.6.2 (1999-11-23)
    • new program view-cert.py for displaying certificates
    • ca-revoke.py can issue new CRL immediately
    • Improved support M$ IE (VBScript code for choosing cryptographic provider by Michael Konietzka <ca-project@konietzka.de>)
    • Handling of missing or wrong parameter input in client-enroll.py is much more user-friendly.
    • Input field for browser type (to avoid problems with users coming through proxies).
    • Got rid of scripts in sbin/ importing pycacnf.py by using parameters --config and --pycalib.
      This might require some changes to an installation (provide parameter in mail aliases, CRON jobs etc.)
    • ca-cycle-priv.py can be forced to issue CRLs with parameter --issuecrls
    Release 0.6.1 (1999-10-12)
    • Switched back to own parsing of cert datetime since time.strptime() relys on glibc where strptime is broken!
    • Made certs2ldap.py a little bit more defensive.
    Release 0.6.0 (1999-10-10)
    • Dropped support for configuration parameter caCertFormat!
    • Support for M$ Internet Explorer (VBScript-Code inspired by contributions of contributed by Jordi Floriach <jfloriach@afina.net>)
    • ns-enroll.py is obsoleted by the more general client-enroll.py
    • Complete rewrite of certs2ldap.py, the script for uploading e-mail certificates to a LDAP repository.
    Release 0.5.5 (1999-09-02)
    • Many bugfixes!
    • generate really unique IDs for cert requests
    • complete rewrite of ca2ldif.py
    • Small enhancements in user interface of CGI-BINs.
    Release 0.5.4 (1999-07-17)
    • Minor bugfixes
    • Small enhancements in user interface of CGI-BINs
    • Restrictive ownership/permissions setting of files/directories in ca-make.py
    • Some small handy scripts for doing boring copying of CA certs
    Release 0.5.3 (1999-06-19)
    • upgrade to OpenSSL 0.9.3a necessary
    • Many bugfixes and small enhancements
    • some changes to configuration (might be incompatible to previous versions!!!)
    • Many improvements and fixes to ca-make.py:
      • flexible usage of files containing X.509v3 extensions for CA certs
      • Implemented setting the permissions of various files/dirs.
    • Better performance by reading configuration from pre-compiled (pickled) conf file
    Release 0.5.1 (1999-06-10)
    • upgrade to Python 1.5.2 necessary
    • some bugfixes and code cleaning
    • better documentation
    • some changes to configuration (might be incompatible to 0.4.5!!!)
    • allow defining the request form depending on specific certificate type
    • configuration of preferred certificate format
    • automatic publishing of certificates with informing user by e-mail
    Release 0.4.5 (1999-05-23)
    • some minor bugfixes
    • some rudimental documentation
    • cgi-bin/browser-check.py for testing cryptographic features of browsers
    • usage of ca-fix executable to fix CA certs during ca-make.py
    • usage of DER certs possible to speed up downloading
    • abandoned get-crl.py, get-cert.py does this either
    Release 0.4.4 (1999-05-18)
    Don't remember...
    pyca-20031118/htdocs/news.html0100644000076400001440000000254007702613022015363 0ustar michaelusers
    2002-06-27
    pyca is presented at the crypto projects booth at the Linuxtag 2003, Karlsruhe. Anyone with questions is invited to visit me there: booth G24, see map.
    2002-07-01
    Debian package of pyCA available.
    2002-06-06
    pyCA is presented at the crypto project's booth at the Linuxtag 2002, Karlsruhe. Anyone with questions is invited to visit me there.
    2002-06-03
    New snapshot for download. Some tweaking for the demonstration at Linuxtag. Especially certs2ldap.py was modified (still not pretty though).
    2002-04-22
    New snapshot for download. Some fixes for certs2ldap.py.
    2001-01-20
    Redesign of web site.
    pyca-20031118/cgi-bin/0040755000076400001440000000000007756443113013562 5ustar michaeluserspyca-20031118/cgi-bin/cert-query.py0100755000076400001440000002032307745153146016236 0ustar michaelusers#!/usr/bin/python """ cert-query.py (c) by Michael Stroeder CGI-BIN for querying the OpenSSL certificate DB Outputs empty input form if queried without parameters. """ __version__ = '0.6.6' import sys, os, string, re, \ pycacnf, cgiforms, htmlbase, charset from time import time,localtime,strftime,mktime from pycacnf import opensslcnf, pyca_section from openssl.db import \ empty_DN_dict, \ DB_type,DB_exp_date,DB_rev_date,DB_serial,DB_file,DB_name,DB_number, \ DB_TYPE_REV,DB_TYPE_EXP,DB_TYPE_VAL, \ dbtime2tuple,GetEntriesbyDN,SplitDN nsBaseUrl = pyca_section.get('nsBaseUrl','/') nsGetCertUrl = pyca_section.get('nsGetCertUrl','get-cert.py') nsViewCertUrl = pyca_section.get('nsViewCertUrl','view-cert.py') ScriptMethod = pyca_section.get('ScriptMethod','POST') HelpUrl = pyca_section.get('HelpUrl',nsBaseUrl) searchkeys = ['CN','Email','L','O','OU','ST','C'] optionkeys = ['casesensitive','onlyvalid','emailcerts','servercerts'] ############################################################################## # haeufig gebrauchte Funktionen ############################################################################## # String der Hilfe-URL zu bestimmtem Parameter zurueckgeben def HelpURL(name,text): return '%s' % (HelpUrl,name,charset.iso2html4(text)) # Ausdrucken eines leeren Eingabeformulars def PrintEmptyForm(form,method='POST'): print '\n' % \ (os.environ.get('SCRIPT_NAME','cert-query.py'),method) print '' for i in optionkeys: print '' % (form.field[i][0].inputfield(),HelpURL(form.field[i][0].name,form.field[i][0].text)) print '\n
    %s%s
    \n' for i in searchkeys: print '' % (HelpURL(form.field[i][0].name,form.field[i][0].text),form.field[i][0].inputfield()) print """
    %s:%s
    """ # Ausgabe der Ergebnistabelle def PrintFound(form,found,cellpadding=2,width=100): print '' % (cellpadding,width) # Tabellenueberschriften ausgeben print '' for i in searchkeys: print '' % (form.field[i][0].text) print '' # Tabelleninhalte for ca_name in found.keys(): ca = opensslcnf.getcadata(ca_name) if ca.isservercert(): certtype='server' else: certtype='email' for i in found[ca_name]: # Eine Tabellenzeile ausgeben print '' % (ca_name) if i[DB_type]==DB_TYPE_REV: print '' % ( \ i[DB_serial], nsBaseUrl,nsViewCertUrl,ca_name,certtype,i[DB_serial], strftime('%Y-%m-%d %H:%M',localtime(mktime(dbtime2tuple(i[DB_rev_date])))) ) elif i[DB_type]==DB_TYPE_EXP: print '' % ( \ i[DB_serial], nsBaseUrl,nsViewCertUrl,ca_name,certtype,i[DB_serial], strftime('%Y-%m-%d %H:%M',localtime(mktime(dbtime2tuple(i[DB_exp_date])))) ) elif i[DB_type]==DB_TYPE_VAL: print '' % ( \ i[DB_serial], nsBaseUrl,nsGetCertUrl,ca_name,certtype,i[DB_serial], nsBaseUrl,nsViewCertUrl,ca_name,certtype,i[DB_serial], strftime('%Y-%m-%d %H:%M',localtime(mktime(dbtime2tuple(i[DB_exp_date])))) ) else: raise ValueError dnfield = SplitDN(i[DB_name]) # Spaltenelemente ausgeben for j in searchkeys: if dnfield.has_key(j) and dnfield[j]: if j=="Email": print '' % (dnfield[j],dnfield[j]) else: print '' % charset.asn12html4(dnfield[j]) else: # bei leeren Feldern Leerzeichen, damit Tabelle immer Raender hat print '' print '' print '
    CA nameSerialvalid
    until
    %s
    %s%s Viewrevoked %s%s Viewexpired %s%sLoadView%s%s%s 
    ' return ############################################################################## # Hauptprogramm ############################################################################## # form initialisieren form = cgiforms.formClass(charset='iso-8859-1') # Die gueltigen Inputattribute setzen alphanumregex = '[0-9a-zA-Z\344\366\374\304\326\334\337ß.*?_ -]*' mailadrregex = '[0-9a-zA-Z@.*?=/_ -]*' form.add(cgiforms.formCheckboxClass('casesensitive','case sensitive','yes',0)) form.add(cgiforms.formCheckboxClass('onlyvalid','only valid','yes',1)) form.add(cgiforms.formCheckboxClass('emailcerts','search e-mail certificates','yes',1)) form.add(cgiforms.formCheckboxClass('servercerts','search server certificates','yes',0)) form.add(cgiforms.formInputClass('CN','Common Name',30,alphanumregex)) form.add(cgiforms.formInputClass('Email','E-Mail',40,mailadrregex)) form.add(cgiforms.formInputClass('OU','Organizational Unit',30,alphanumregex)) form.add(cgiforms.formInputClass('O','Organization',30,alphanumregex)) form.add(cgiforms.formInputClass('L','Location',30,alphanumregex)) form.add(cgiforms.formInputClass('ST','State / Province',30,alphanumregex)) form.add(cgiforms.formInputClass('C','Country',2,'[a-zA-Z?]'*2)) # Schon Parameter vorhanden? if not form.contentlength: # Aufruf erfolgte ohne Parameter => # 0. Schritt: leeres Eingabeformular ausgeben htmlbase.PrintHeader('Search certificates') htmlbase.PrintHeading('Search certificates') print """You can search for certificates in the certificate database.

    Just type in substrings or regular expressions as search criteria.""" PrintEmptyForm(form) htmlbase.PrintFooter() sys.exit(0) # Aufruf erfolgte mit Parametern try: form.getparams() except cgiforms.formContentLengthException,e: htmlbase.PrintErrorMsg('Content length invalid.') sys.exit(0) except cgiforms.formParamNameException,e: htmlbase.PrintErrorMsg('Unknown parameter "%s".' % (e.name)) sys.exit(0) except cgiforms.formParamContentException,e: htmlbase.PrintErrorMsg('Content of field "%s" has invalid format.' % (e.text)) sys.exit(0) except cgiforms.formParamStructException,e: htmlbase.PrintErrorMsg('Too many (%d) parameters for field "%s".' % (e.count,e.name)) sys.exit(0) except cgiforms.formParamLengthException,e: htmlbase.PrintErrorMsg('Content too long. Field "%s" has %d characters.' % (e.text,e.length)) sys.exit(0) except: htmlbase.PrintErrorMsg('Unknown exception.') sys.exit(0) # Parameter sind ok # Anfrage zurecht formatieren query = empty_DN_dict for i in searchkeys: query[i] = form.field[i][0].content found ={} old_db_filenames = [] if 'casesensitive' in form.inputkeys: casesensitive = form.field['casesensitive'][0].content=='yes' else: casesensitive = 0 if 'onlyvalid' in form.inputkeys: onlyvalid = form.field['onlyvalid'][0].content=='yes' else: onlyvalid = 1 if 'emailcerts' in form.inputkeys: emailcerts = form.field['emailcerts'][0].content=='yes' else: emailcerts = 0 if 'servercerts' in form.inputkeys: servercerts = form.field['servercerts'][0].content=='yes' else: servercerts = 0 ca_names = opensslcnf.sectionkeys.get('ca',[]) for ca_name in ca_names: ca = opensslcnf.getcadata(ca_name) # Ist der Zertifikattyp 'S/MIME for client use' ? if (emailcerts and ca.isemailcert()) or \ (servercerts and ca.isservercert()): # Stammverzeichnis der CA # Zertifikat-DB schon vorher mal behandelt? if not ca.database in old_db_filenames: old_db_filenames.append(ca.database) if os.path.isfile(ca.database): # Anfrage starten try: found[ca_name] = GetEntriesbyDN( ca.database, query, form.field['casesensitive'][0].content=='yes', form.field['onlyvalid'][0].content=='yes' ) except re.error: htmlbase.PrintErrorMsg('Error parsing regular expression.') sys.exit(0) # Nix gefunden! if not found: htmlbase.PrintErrorMsg('No matching entries found.') sys.exit(0) # Ausgabe des Suchergebnisses htmlbase.PrintHeader('Search results') PrintFound(form,found) htmlbase.PrintFooter() sys.exit(0) pyca-20031118/cgi-bin/pycacnf.py0100755000076400001440000000323707745153146015566 0ustar michaelusers""" pycacnf.py - Read OpenSSL configuration file openssl.cnf (c) by Michael Stroeder """ __version__ = '0.6.6' import os ######################################################################## # Some variables for configuration ######################################################################## # Full pathname of OpenSSL configuration file, # most times named openssl.cnf cnf_filename = '/etc/openssl/openssl.cnf' # List of additional module directories pylib = [ os.environ.get('PYCALIB','/usr/local/pyca/pylib'), '/home/michael/Proj/python/pyca/pylib' ] ######################################################################## # There's nothing to configure below this line ######################################################################## import sys,os # Extend the Python path sys.path.extend(pylib) if os.path.isfile('%s.pickle' % (cnf_filename)): # Try to read OpenSSL's config file from a pickled copy f=open('%s.pickle' % (cnf_filename),'rb') try: # first try to use the faster cPickle module from cPickle import load except ImportError: from pickle import load opensslcnf=load(f) f.close() else: # Read OpenSSL's config file from source import openssl opensslcnf=openssl.cnf.OpenSSLConfigClass(cnf_filename) # Diverse allgemeine Parameter aus der Sektion [ pyca ] uebernehmen pyca_section = opensslcnf.data.get('pyca',{}) import htmlbase htmlbase.bodyPARAM=pyca_section.get('htmlBodyParam','') ErrorLog = pyca_section.get('ErrorLog','') if ErrorLog: # Redirect error log to defined file # FIX ME! File locking for concurrent access needed? sys.stderr.flush() sys.stderr = open(ErrorLog,'a') pyca-20031118/cgi-bin/scep.py0100755000076400001440000000470007745153146015071 0ustar michaelusers#!/usr/bin/python """ scep.py - Cisco System's Simple Certificate Enrollment Protocol (c) by Michael Stroeder CGI-BIN for implementing SCEP see: http://www.cisco.com/warp/public/cc/pd/sqsw/tech/scep_wp.htm """ Version='0.6.6' def ReadCertFromFileObject(f): # Zertifikat aus Dateiobject certfile lesen cert = f.read() rc = f.close() return cert def ReadCertsFromFileNames(pathnames): result = [] for pathname in pathnames: f = open(pathname,'r') result.append(ReadCertFromFileObject(f)) return string.join(result,'') import sys, os, re, string, \ pycacnf, htmlbase, cgiforms, cgihelper, certhelper, openssl from time import time,localtime,strftime,mktime from pycacnf import opensslcnf, pyca_section from openssl.db import \ empty_DN_dict, \ DB_type,DB_exp_date,DB_rev_date,DB_serial,DB_file,DB_name,DB_number, \ DB_TYPE_REV,DB_TYPE_EXP,DB_TYPE_VAL, \ dbtime2tuple,GetEntriesbyDN,SplitDN # Wir lesen rein gar nix von Standardeingabe => gleich dicht machen sys.stdin.close() # Path to openssl executable OpenSSLExec = pyca_section.get('OpenSSLExec','/usr/bin/openssl') form = cgiforms.formClass() form.add( cgiforms.formSelectClass( 'operation', 'Operation', ['GetCACert','PKIOperation'] ) ) form.add( cgiforms.formInputClass( 'message', 'Message', 10000, (r'.*',re.M+re.S) ) ) form.getparams() scep_operation = form.field['operation'][0].content scep_message = form.field['message'][0].content if scep_operation in ['GetCACert','GetCACertChain']: # *** Check parameter message again for being valid FQDN. # *** Set to pre-configured SCEP CA scep_message = 'SCEP' ca = opensslcnf.getcadata(scep_message) # Name der CA pruefen if not opensslcnf.data['ca'].has_key(scep_message): # CA-Definition nicht in openssl-Konfiguration enthalten htmlbase.PrintErrorMsg('Unknown certificate authority "%s".' % scep_message) sys.exit(0) # Does the certificate file exist? if not os.path.isfile(ca.certificate): htmlbase.PrintErrorMsg('CA Certificate of file not found.') sys.exit(0) cert = certhelper.pem2der(open(ca.certificate,'r').read()) sys.stderr.write('%s' % repr(cert)) # Simply write MIME-type and certificate data to stdout sys.stdout.write('Content-type: application/x-x509-ca-cert\n\n') sys.stdout.write(cert) sys.stdout.flush() elif scep_operation=='PKIOperation': open('/tmp/scep_message','wb').write(scep_message) sys.exit(0) pyca-20031118/cgi-bin/get-cert.py0100755000076400001440000001671107745153146015656 0ustar michaelusers#!/usr/bin/python """ get-cert.py (c) by Michael Stroeder CGI-BIN for downloading and installing certificates with Netscape Navigator and M$ Internet Explorer Input: PATH_INFO - Name of CA in openssl.cnf (section [ca] of openssl.cnf) - Type of certificate ('email', 'user', 'ca', 'crl') QUERY_STRING only for ('email', 'user') - Serial number of desired certificate max. 8 digits hexadecimal (32 Bit) Examples: get-cert.py/Persona/ca sends CA certificate of CA "Persona" get-cert.py/Persona/email?01 sends client certificate of CA "Persona" with serial 0x01 get-cert.py/Server/crl sends CRL of CA "Server" get-cert.py/Server/server?01 sends PEM-encoded server certificate of CA "Server" with serial 0x01 """ __version__='0.6.6' def ReadCertFromFileObject(f): # Zertifikat aus Dateiobject certfile lesen cert = f.read() rc = f.close() return cert def ReadCertsFromFileNames(pathnames): result = [] for pathname in pathnames: f = open(pathname,'r') result.append(ReadCertFromFileObject(f)) return string.join(result,'') import sys,os,string,re,pycacnf,htmlbase,cgihelper,certhelper,openssl from time import time,localtime,strftime,mktime from pycacnf import opensslcnf, pyca_section from openssl.db import \ empty_DN_dict, \ DB_type,DB_exp_date,DB_rev_date,DB_serial,DB_file,DB_name,DB_number, \ DB_TYPE_REV,DB_TYPE_EXP,DB_TYPE_VAL, \ dbtime2tuple,GetEntriesbyDN,SplitDN # Wir lesen rein gar nix von Standardeingabe => gleich dicht machen sys.stdin.close() # Path to openssl executable OpenSSLExec = pyca_section.get('OpenSSLExec','/usr/bin/openssl') # Ein paar Umgebungsvariablen auslesen, welche der Apache liefert request_method = os.environ.get('REQUEST_METHOD','') query_string = os.environ.get('QUERY_STRING','') path_info = os.environ.get('PATH_INFO','')[1:] browser_name,browser_version = cgihelper.BrowserType(os.environ.get('HTTP_USER_AGENT','')) # Hier die ueblichen Paranoid-Pruefungen der Parameter if request_method!='GET': # Skript nicht mit GET aufgerufen htmlbase.PrintErrorMsg('Wrong method.') sys.exit(0) # Bezeichnung der Sub-CA und MIME-Typ aus PATH_INFO splitten try: ca_name, cert_typeandformat = string.split(path_info,'/',1) cert_typeandformat=string.lower(cert_typeandformat) except ValueError: htmlbase.PrintErrorMsg('Invalid parameter format.') sys.exit(0) # Name der CA pruefen if not opensslcnf.data['ca'].has_key(ca_name): # CA-Definition nicht in openssl-Konfiguration enthalten htmlbase.PrintErrorMsg('Unknown certificate authority "%s".' % ca_name) sys.exit(0) if re.compile('^(ca|crl|server|user|email)(.(der|pem|b64|crt|crl))*$').match(cert_typeandformat) is None: htmlbase.PrintErrorMsg('Certificate type has invalid format.') sys.exit(0) try: cert_type,cert_format = string.split(cert_typeandformat,'.',1) except ValueError: cert_type,cert_format = cert_typeandformat,'der' if cert_format=='crt' or cert_format=='crl': cert_format='der' if len(query_string)>8: # Seriennummer mit mehr 32 Bit htmlbase.PrintErrorMsg('Serial number too long.') sys.exit(0) if (not cert_type in ['ca','crl']) and (not query_string): # keine Seriennummer htmlbase.PrintErrorMsg('No serial number.') sys.exit(0) # Process request ca = opensslcnf.getcadata(ca_name) if cert_type=='ca': # Does the certificate file exist? if not os.path.isfile(ca.certificate): htmlbase.PrintErrorMsg('CA certificate file not found.') sys.exit(0) cert = open(ca.certificate,'r').read() if browser_name=='MSIE': mimetype = 'application/pkix-cert' else: mimetype = 'application/x-x509-ca-cert' elif cert_type=='crl': # Does the certificate file exist? if not os.path.isfile(ca.crl): htmlbase.PrintErrorMsg('CRL file not found.') sys.exit(0) cert = open(ca.crl,'r').read() if browser_name=='MSIE': mimetype = 'application/pkix-crl' else: mimetype = 'application/x-pkcs7-crl' elif cert_type in ['user','email','server']: if re.compile('^[0-9a-fA-F]+$').match(query_string) is None: # Parameter war keine Hex-Nummer htmlbase.PrintErrorMsg('Serial number not in hexadecimal format.') sys.exit(0) # Abruf eines Zertifikates mittels Seriennummer serialnumber=string.atoi(query_string,16) entry = openssl.db.GetEntrybySerial(ca.database,serialnumber) # Kein entsprechender Eintrag gefunden if not entry: htmlbase.PrintErrorMsg('Certificate not found.') sys.exit(0) # Zertifikat ist ungueltig if entry[DB_type]!=openssl.db.DB_TYPE_VAL: htmlbase.PrintErrorMsg('Certificate invalid.') sys.exit(0) certfilename = os.path.join(ca.certs,'%s.pem' % (entry[DB_serial])) # Does the certificate file exist? if not os.path.isfile(certfilename): htmlbase.PrintErrorMsg('Certificate file not found.') sys.exit(0) # get list of CA names for which intermediate CA certs should be included caIntermediateCACerts = pyca_section.get('caIntermediateCACerts',[]) if type(caIntermediateCACerts)==type(''): # Work around a deficiency in config parser caIntermediateCACerts = [caIntermediateCACerts] if ca_name in caIntermediateCACerts: ca_certfilenames = opensslcnf.getcacertchain(ca_name) for ca_certfilename in ca_certfilenames: if not os.path.isfile(ca_certfilename): htmlbase.PrintErrorMsg('Certificate file of intermediate CA not found.') sys.exit(0) else: ca_certfilenames = [] if cert_type=='server': # Server certificates are downloaded or displayed in browser cert = open(certfilename,'r').read() if cert_format=='der': mimetype = 'application/octet-stream' else: mimetype = 'text/plain' elif cert_type=='email': cert = open(certfilename,'r').read() if browser_name=='MSIE': mimetype = 'application/pkix-cert' else: mimetype = 'application/x-x509-email-cert' elif cert_type=='user': if browser_name=='MSIE': command = '%s crl2pkcs7 -nocrl -certfile %s' % (OpenSSLExec,certfilename) for ca_certfilename in ca_certfilenames: command = command + ' -certfile %s ' % (ca_certfilename) cert = ReadCertFromFileObject(os.popen(command)) else: cert = open(certfilename,'r').read() mimetype = 'application/x-x509-user-cert' else: # Zertifikattyp war nicht gueltig htmlbase.PrintErrorMsg('Invalid certificate type "%s"' % cert_type) sys.exit(0) if browser_name=='MSIE' and cert_type=='user': import vbs, charset htmlbase.PrintHeader('Install certificate') htmlbase.PrintHeading('Install certificate') print 'Certificate of type %s:

    ' % ca_name print 'Subject DN: %s
    Valid until: %s' % ( \ charset.asn12html4(entry[DB_name]), \ strftime('%d.%m.%Y',localtime(mktime(dbtime2tuple(entry[DB_exp_date])))) \ ) vbs.PrintVBSXenrollObject() print '' htmlbase.PrintFooter() else: # Simply write MIME-type and certificate data to stdout sys.stdout.flush() sys.stdout.write('Content-type: %s\n\n' % mimetype) if cert_format=='der': sys.stdout.write(certhelper.pem2der(cert)) elif cert_format=='pem': pem_type = {0:'CERTIFICATE',1:'CRL'}[cert_type=='crl'] sys.stdout.write("""-----BEGIN %s----- %s -----END %s----- """ % (pem_type,certhelper.extract_pem(cert)[0][1],pem_type)) elif cert_format=='b64': sys.stdout.write(certhelper.extract_pem(cert)[0][1]) sys.exit(0) pyca-20031118/cgi-bin/ca-index.py0100755000076400001440000000427007310433627015622 0ustar michaelusers#!/usr/bin/python """ ca-index.py (c) by Michael Stroeder This CGI-BIN program shows a pretty index of the CA definitions in OpenSSL's config file (e.g. named openssl.cnf) """ __version__ = '0.6.6' import os, sys, types, string, pycacnf, openssl, htmlbase from pycacnf import opensslcnf, pyca_section nsGetCertUrl = pyca_section.get('nsGetCertUrl','') nsViewCertUrl = pyca_section.get('nsViewCertUrl','') nsEnrollUrl = pyca_section.get('nsEnrollUrl','') ca_names = opensslcnf.sectionkeys.get('ca',[]) if not ca_names: htmlbase.PrintErrorMsg('No certificate authorities found.') sys.exit(0) htmlbase.PrintHeader('Overview of certificate authorities') htmlbase.PrintHeading('Overview of certificate authorities') print """ """ for ca_name in ca_names: ca = opensslcnf.getcadata(ca_name) if nsEnrollUrl and ca.isclientcert(): nsCertTypeStr = '%s' % (ca.nsBaseUrl,nsEnrollUrl,ca_name,ca.nsCertTypeStr) else: if ca.nsCertTypeStr: nsCertTypeStr = '%s' % (ca.nsCertTypeStr) else: nsCertTypeStr = ' ' if ca.nsCaRevocationUrl: nsCaRevocationUrl='load' % (ca.nsBaseUrl,ca.nsCaRevocationUrl) nsViewRevocationUrl='view' % (ca.nsBaseUrl,nsViewCertUrl,ca_name) else: nsCaRevocationUrl = ' ' nsViewRevocationUrl = ' ' if ca.nsCaPolicyUrl: nsCaPolicyUrl='Policy of %s' % (ca.nsBaseUrl,ca.nsCaPolicyUrl,ca_name) else: nsCaPolicyUrl='-' print """ """ % ( ca_name, ca.nsBaseUrl, nsGetCertUrl, ca_name, ca.nsBaseUrl, nsViewCertUrl, ca_name, nsCaRevocationUrl, nsViewRevocationUrl, nsCertTypeStr, ca.nsComment, nsCaPolicyUrl ) print '
    CA name CA certificate CRL certificate
    types
    Comment View policy
    %s load view %s %s %s %s %s

    ' print 'Search for issued certificates.' htmlbase.PrintFooter() sys.exit(0) pyca-20031118/cgi-bin/client-enroll.py0100755000076400001440000004707207745153146016717 0ustar michaelusers#!/usr/bin/python """ client-enroll.py - certificate enrollment with mainstream web browsers (c) by Michael Stroeder """ Version='0.6.6' ######################################################################## # CGI-BIN for creating certificate requests ######################################################################## import os, sys, types, string, re, socket, \ pycacnf, \ cgiforms, cgihelper, charset, ipadr, htmlbase, certhelper from pycacnf import opensslcnf, pyca_section from charset import iso2utf, iso2html4 from cgihelper import BrowserType,known_browsers,known_browsers_rev ######################################################################## # "Internet"-Funktionen ######################################################################## # Aus einer Mailadresse die Domain herausloesen # Ergebnis ist Teilstring hinter letztem(!) @ # falls kein @ vorhanden, dann Leerstring def DomainAdr(MailAdr=""): splitted=string.split(MailAdr,'@') if len(splitted)>1: return splitted[-1] else: return '' # String der Hilfe-URL zu bestimmtem Parameter zurueckgeben def HelpURL(HelpUrl,name,text): return '%s' % \ (HelpUrl,name,iso2html4(text)) # Print list of possible cert types def PrintCertTypes(ca_names): htmlbase.PrintHeader('Start enrollment for certificate request') htmlbase.PrintHeading('Start enrollment for certificate request') print """This certificate authority issues several types of client certificates.
    Please choose the appropriate certificate type below:

    """ for ca_name in ca_names: ca = opensslcnf.getcadata(ca_name) if ca.isclientcert(): if ca.nsCaPolicyUrl: nsCaPolicyUrlStr = '(view policy)' % (ca.nsBaseUrl,ca.nsCaPolicyUrl) else: nsCaPolicyUrlStr = ' ' print '' % (os.environ.get('SCRIPT_NAME','client-enroll.py'),ca_name,ca_name,ca.nsComment,nsCaPolicyUrlStr) print '
    %s%s%s
    ' htmlbase.PrintFooter() # Ausgabe eines leeren Eingabeformulars def PrintEmptyForm(form,ca_name,scriptmethod='POST'): print '

    ' % (os.environ.get('SCRIPT_NAME','client-enroll.py'),ca_name,scriptmethod) print '' for i in form.keys: for j in form.field[i]: print '' % (HelpURL(HelpUrlBase,j.name,j.text),' *'*j.required,j.inputfield()) print '
    %s%s%s
    ' print '' print '' print '' return def PrintInput(form,cellpadding=5,width=100): print '' % (cellpadding,width) formKeys = form.inputkeys[:] try: formKeys.remove('challenge') except ValueError: pass for i in formKeys: for j in form.field[i]: print '' % \ (HelpURL(HelpUrlBase,j.name,j.text),j.contentprint()) print '
    %s%s
    ' return # Ausgabe eines Formulars zur Schuesselerzeugung def PrintKeygenForm(form,ca_name,ca,browsertype,scriptmethod='POST'): print """Content-type: text/html\n Create key pair and certificate request """ if browsertype=='MSIE': import vbs vbs.PrintVBSXenrollObject() print '' print '' % htmlbase.bodyPARAM htmlbase.PrintHeading('Create key pair and certificate request') print 'Your key pair and certificate request can be generated now.
    ' print 'Please have a look below to check if your input data was correct.

    ' # Print input given by user as readable table and hidden input fields PrintInput(form) print '

    ' % (os.environ.get('SCRIPT_NAME','client-enroll.py'),ca_name,scriptmethod) for i in form.inputkeys: for j in form.field[i]: print '' % (j.name,j.content) # Print hint about minimum key size if ca.min_key_size>0: print """Please note:
    The certificate type %s requires a minimum key size of %d bits! If you are not able to choose a key length equal or greater than %d the certificate authority will refuse to issue a certificate for your certificate request!

    """ % (ca_name,ca.min_key_size,ca.min_key_size) if browsertype=='MSIE': print '

    Key size:

    ' print '
    ' else: print '

    %s:%s

    ' % ( \ HelpURL(HelpUrlBase,form.field['SPKAC'][0].name,form.field['SPKAC'][0].text),\ form.field['SPKAC'][0].inputfield(form.field['challenge'][0].content) \ ) htmlbase.PrintFooter() ######################################################################## # Main ######################################################################## # Read several parameters from config MailRelay = pyca_section.get('MailRelay','localhost') TmpDir = pyca_section.get('TmpDir','/tmp') caCertReqMailAdr = pyca_section.get('caCertReqMailAdr','') caPendCertReqValid = string.atoi(pyca_section.get('caPendCertReqValid','0')) caInternalCertTypes = pyca_section.get('caInternalCertTypes',[]) if type(caInternalCertTypes)!=types.ListType: caInternalCertTypes = [caInternalCertTypes] caInternalIPAdr = pyca_section.get('caInternalIPAdr',['127.0.0.1/255.255.255.255']) if type(caInternalIPAdr)!=types.ListType: caInternalIPAdr = [caInternalIPAdr] caInternalDomains = pyca_section.get('caInternalDomains','') if type(caInternalDomains)!=types.ListType: caInternalDomains = [caInternalDomains] ScriptMethod = pyca_section.get('ScriptMethod','POST') # Read wanted certificate type from PATH_INFO ca_name = os.environ.get('PATH_INFO','')[1:] # Get list of possible certificate type from config ca_names = opensslcnf.sectionkeys.get('ca',[]) # Check for valid certificate type if not ca_names: htmlbase.PrintErrorMsg('No certificate authorities found.') sys.exit(0) if not ca_name: PrintCertTypes(ca_names) sys.exit(0) if not ca_name in ca_names: # CA-Definition nicht in openssl-Konfiguration enthalten htmlbase.PrintErrorMsg('Unknown certificate authority "%s".' % ca_name) sys.exit(0) # Check for "internal" IP address of client if (ca_name in caInternalCertTypes) and \ not ipadr.MatchIPAdrList(os.environ.get('REMOTE_ADDR',''),caInternalIPAdr): htmlbase.PrintErrorMsg('This type of certificate request is restricted to internal hosts!') sys.exit(0) ca = opensslcnf.getcadata(ca_name) HelpUrlBase = '%s%s%s' % ( \ ca.nsBaseUrl, \ pyca_section.get('HelpUrl',''), \ os.path.splitext(os.path.basename(os.environ.get('SCRIPT_NAME','')))[0] \ ) policy_section = opensslcnf.data.get(ca.policy,{}) req_section = opensslcnf.data.get(ca.req,{}) if req_section and req_section.has_key('distinguished_name'): req_distinguished_name_section = opensslcnf.data.get(req_section['distinguished_name'],{}) req_distinguished_name_keys = opensslcnf.sectionkeys.get(req_section['distinguished_name'],[]) else: htmlbase.PrintErrorMsg('Request section for "%s" not found.' % ca_name) sys.exit(0) # Hier Verwendungszweck der Zertifikate pruefen if not ca.isclientcert(): htmlbase.PrintErrorMsg('Certificate authority "%s" does not issue client certificates.' % ca_name) sys.exit(0) # form initialisieren form = cgiforms.formClass(charset='iso-8859-1') # Die gueltigen Inputattribute setzen alphanumregex = r'[0-9a-zA-Z\344\366\374\304\326\334\337ß/\'"._ -]*' # telephoneregex = r'^\+[0-9][0-9]-[0-9]*-[0-9]*' # Check which browser is used http_browsertype,http_browserversion = BrowserType(os.environ.get('HTTP_USER_AGENT','')) key_gen_browsers = {'Microsoft Internet Explorer':('PKCS10','pem'),'Netscape Navigator':('SPKAC','spkac'),'Opera':('SPKAC','spkac')} if not known_browsers.get(http_browsertype,http_browsertype) in key_gen_browsers.keys(): http_browsertype='' form.add(cgiforms.formSelectClass('browsertype','Browser Software',key_gen_browsers.keys(),known_browsers.get(http_browsertype,''),required=1)) form.add(cgiforms.formPasswordClass('challenge','Initial Master Secret',30,alphanumregex,required=1)) # The form is build by looking at a [req] section in openssl.cnf dn_attr_keys = [] dn_attr = {} for i in req_distinguished_name_keys: l = string.split(i,'_') attr_name = string.strip(l[0]) if not attr_name in dn_attr_keys: dn_attr_keys.append(attr_name) dn_attr[attr_name]={'comment':'','max':'40','regex':alphanumregex,'default':''} if len(l)>1: dn_attr[attr_name][l[1]]=req_distinguished_name_section.get(i,'') elif len(l)==1: dn_attr[attr_name]['comment']=req_distinguished_name_section.get(i,attr_name) for i in dn_attr_keys: imaxlength=string.atoi(dn_attr[i].get('max','40')) if imaxlength>40: isize=40 else: isize=imaxlength policy_field = policy_section.get(i,'optional') if policy_field=='match': if type(dn_attr[i]['default'])==types.ListType: dn_attr[i]['default']=dn_attr[i]['default'][0] form.add(cgiforms.formHiddenInputClass(i,dn_attr[i]['comment'],imaxlength,dn_attr[i]['regex'],dn_attr[i]['default'],required=1,show=1)) else: if type(dn_attr[i]['default'])==types.ListType: dn_attr[i]['default'].sort() form.add(cgiforms.formSelectClass(i,dn_attr[i]['comment'],dn_attr[i]['default'],required=policy_field=='supplied')) else: form.add(cgiforms.formInputClass(i,dn_attr[i]['comment'],imaxlength,dn_attr[i]['regex'],dn_attr[i]['default'],required=policy_field=='supplied',size=isize)) # Schon Parameter vorhanden? if not form.contentlength: import time # Aufruf erfolgte ohne Parameter => # 0. Schritt: leeres Eingabeformular ausgeben if not ca.nsComment: ca.nsComment = 'No comment' if ca.nsCaPolicyUrl: nsCommentStr = '
    %s' % (ca.nsBaseUrl,ca.nsCaPolicyUrl,ca.nsComment) else: nsCommentStr = ca.nsComment htmlbase.PrintHeader('Input form for certificate request') htmlbase.PrintHeading('Input form for certificate request') if not http_browsertype: print '

    Your browser type could not be automatically determined.
    Please choose the browser you are using.

    ' print """
    Certificate authority:%s
    Certificate type:%s
    Certificate comment:%s

    Certificates of this type will be valid for %d days, approximately until %s.

    """ % (ca_name, ca.nsCertTypeStr, nsCommentStr, ca.default_days, time.strftime('%Y-%m-%d',time.gmtime(time.time()+86400*ca.default_days)) ) print """You can apply for a certificate by filling out the input form below. Click on the names of the parameters to get further informations about the usage and format restrictions of the input data.

    Required input parameters are marked with *. """ PrintEmptyForm(form,ca_name) htmlbase.PrintFooter() sys.exit(0) # 1. und 2. Schritt haben Schluesselfeld form.add(cgiforms.formInputClass('KeySize','Key Size',100,alphanumregex)) form.add( cgiforms.formInputClass( 'PKCS10', 'PKCS#10 Request', 2000, ( r'[ \w+/=\r\n]+', re.S+re.M) ) ) form.add(cgiforms.formKeygenClass('SPKAC','Public Key and Challenge',6000)) # Aufruf erfolgte mit Parametern try: form.getparams(ignoreemptyparams=1) except cgiforms.formContentLengthException,e: htmlbase.PrintErrorMsg('Content length invalid.') sys.exit(0) except cgiforms.formParamNameException,e: htmlbase.PrintErrorMsg('Unknown parameter "%s".' % (e.name)) sys.exit(0) except cgiforms.formParamsMissing,e: htmlbase.PrintHeader('Error') htmlbase.PrintHeading('Error') print """The following parameter(s) is/are missing:

    • %s

    Required input parameters are marked with *. """ % (string.join(map(lambda x: x[1],e.missing),'

  • ')) for k in ['PKCS10','KeySize','SPKAC']: try: form.keys.remove(k) except ValueError: pass for i in form.inputkeys: form.field[i][0].default=form.field[i][0].content PrintEmptyForm(form,ca_name) htmlbase.PrintFooter() sys.exit(0) except cgiforms.formParamContentException,e: htmlbase.PrintHeader('Error') htmlbase.PrintHeading('Error') print 'Content of field "%s" has invalid format.

    ' % (e.text) form.keys.remove(RequestDataKey) for i in form.inputkeys: form.field[i][0].default=form.field[i][0].content PrintEmptyForm(form,ca_name) htmlbase.PrintFooter() sys.exit(0) except cgiforms.formParamStructException,e: htmlbase.PrintErrorMsg('Too many (%d) parameters for field "%s".' % (e.count,e.name)) sys.exit(0) except cgiforms.formParamLengthException,e: htmlbase.PrintErrorMsg('Content too long. Field "%s" has %d characters.' % (e.text,e.length)) sys.exit(0) if 'browsertype' in form.inputkeys and \ form.field['browsertype'][0].content in key_gen_browsers.keys(): browsertype = known_browsers_rev[form.field['browsertype'][0].content] else: browsertype = http_browsertype RequestDataKey,request_filenamesuffix = key_gen_browsers[known_browsers[browsertype]] ############################################################################## # Zusaetzliche Ueberpruefungen diverser Parameter ############################################################################## if 'commonName' in form.inputkeys: commonName = form.field.get('commonName',[''])[0].content else: commonName = '' if 'emailAddress' in form.inputkeys: emailAddress = form.field.get('emailAddress',[''])[0].content else: emailAddress = '' # Check for "internal" mail domain if (ca_name in caInternalCertTypes) and \ not (DomainAdr(emailAddress) in caInternalDomains): htmlbase.PrintErrorMsg('This type of certificate request is restricted to internal address domains!') sys.exit(0) if not (RequestDataKey in form.inputkeys and form.field[RequestDataKey][0].content): # Aufruf erfolgte mit Parametern ohne Schluessel => # 1. Schritt: Eingegebene Daten anzeigen und # Benutzer zur Schluesselerzeugung auffordern PrintKeygenForm(form,ca_name,ca,browsertype) sys.exit(0) # Aufruf erfolgte mit Parametern inkl. Schluessel => # 2. Schritt: Forminhalt bearbeiten # Check the required key length if min_key_size was defined if ca.min_key_size>0: # FIX ME!!! # This is a very primitive and falsy key length checking!!! # Only useful for SPKAC minbytes={512:200,768:300,1024:400} if minbytes.has_key(ca.min_key_size) and len(form.field[RequestDataKey][0].content)The certificate type %s requires a minimum key size of %d bits!' % (ca_name,ca.min_key_size)) sys.exit(0) # Zufaellige ChallengeID erzeugen und daraus eindeutige, noch nicht # existierende Dateinamen mittels MD5-Hash basteln import random, md5, binascii # Zufaellige ID fuer Antwort vom Benutzer caChallengeId = md5.new('%d' % random.randint(0,99999999)) formKeys = form.inputkeys[:] for i in formKeys: for j in form.field[i]: caChallengeId.update(j.content) # ca_name und ChallengeId fuer Mail-Subjects und Dateinamen camailSubject = 'cert-req-%s.%s.%s' % (RequestDataKey,ca_name,string.replace(binascii.b2a_base64(caChallengeId.digest()),'/','_')[:-1]) request_filename = os.path.join(ca.pend_reqs_dir,'%s.%s' % (camailSubject,request_filenamesuffix)) if os.path.exists(request_filename): # Versuch nicht existierenden Dateinamen zu basteln schlug fehl. # Duerfte eigentlich nicht passieren. htmlbase.PrintErrorMsg('Error generating a random ID or creating temporary files.') sys.exit(0) ############################################################################## # Request erzeugen ############################################################################## request_file = open(request_filename,'w') if RequestDataKey=='PKCS10': request_file.write("""-----BEGIN CERTIFICATE REQUEST----- %s -----END CERTIFICATE REQUEST----- """ % (form.field['PKCS10'][0].content)) elif RequestDataKey=='SPKAC': # FIX ME! This won't work with additional parameters of [ new_oids ] section # CertRequestKeys = ['countryName','stateOrProvinceName','localityName','organizationName','organizationalUnitName','commonName','initials','uid','emailAddress','SPKAC'] CertRequestKeys = filter( lambda i: not i in ['challenge','browsertype'], form.keys ) for i in CertRequestKeys: if (i in form.inputkeys) and form.field[i][0].content: request_file.write('%s = %s\n' % (i,form.field[i][0].content)) request_file.close() os.chmod(request_filename,0444) ############################################################################## # Send a nice e-mail with random ID to user to initiate mail dialogue ############################################################################## if caCertReqMailAdr and emailAddress: import smtplib, mimify if commonName: to_addr = '%s <%s>' % (mimify.mime_encode_header(commonName),emailAddress) else: to_addr = '%s' % (emailAddress) # Mailbody erzeugen mail_msg = """From: %s To: %s Subject: %s Someone (maybe you) has sent a certificate request to our certificate authority. Please answer this e-mail with the same subject if this was really you and the data below is correct. If someone abused your name / e-mail address simply forget about this message and delete it. %s ------------- Identity Information ------------- """ % (caCertReqMailAdr,\ to_addr,\ camailSubject,\ (caPendCertReqValid>0)*(' The certificate\nrequest will be removed automatically after %d hours.' % caPendCertReqValid) ) # Hier den eigentlichen Cert-Req an Mailbody anhaengen formKeys = form.keys[:] for unwantedkey in ['challenge','browsertype',RequestDataKey]: try: formKeys.remove(unwantedkey) except ValueError: pass mail_msg_paramlist = [] for i in formKeys: if (i in form.inputkeys) and form.field[i][0].content: mail_msg_paramlist.append('%s = %s' % (i,form.field[i][0].content)) mail_msg = '%s%s' % (mail_msg,string.join(mail_msg_paramlist,'\n')) try: smtpconn=smtplib.SMTP(MailRelay) smtpconn.set_debuglevel(0) try: smtpconn.sendmail(caCertReqMailAdr,[to_addr],mail_msg) except: htmlbase.PrintErrorMsg( 'Unable to send an e-mail to %s!
    Please provide your correct and valid %s or ask your system administrator.' % \ ( charset.escapeHTML(to_addr), HelpURL(HelpUrlBase,'emailAddress','e-mail address') ) ) sys.exit(0) smtpconn.quit() except socket.error: htmlbase.PrintErrorMsg('Unable to contact default mail server!') sys.exit(0) # Schliesslich und endlich... htmlbase.PrintHeader('Certificate request stored.') htmlbase.PrintHeading('Certificate request is stored.') print """Your certificate request was stored for further processing.""" if caCertReqMailAdr and emailAddress: print """

    You will get an e-mail message with a random ID. Please answer this e-mail to confirm your certificate request.""" if caPendCertReqValid: print """Otherwise your certificate request will be removed automatically after %d hour(s).

    """ % (caPendCertReqValid) print '

    Once again the data you gave to us:

    ' PrintInput(form) htmlbase.PrintFooter() sys.exit(0) pyca-20031118/cgi-bin/ns-revoke.py0100755000076400001440000001002707745153146016047 0ustar michaelusers#!/usr/bin/python """ ns-revoke.py (c) by Michael Stroeder CGI-BIN for revoking client certificates Input: PATH_INFO - Name of CA in openssl.cnf (section [ca] of openssl.cnf) QUERY_STRING - Serial number of certificate to revoke max. 8 digits hexadecimal (32 Bit) Example: ns-revoke.py/Persona?01 revokes client certificate with serial 0x01 of CA "Persona" The following checks are made to avoid denial of service attacks: - The client software must provide the client certificate. - The issuer of the client and the server certificates must match """ Version='0.6.6' import sys, os, string, re, pycacnf, htmlbase, openssl, cgissl, certhelper from pycacnf import opensslcnf, pyca_section # Wir lesen rein gar nix von Standardeingabe => gleich dicht machen sys.stdin.close() # Path to openssl executable openssl.bin_filename = pyca_section.get('OpenSSLExec','/usr/bin/openssl') # Ein paar Umgebungsvariablen auslesen, welche der Apache liefert request_method = os.environ.get('REQUEST_METHOD','') query_string = os.environ.get('QUERY_STRING','') script_name = os.environ.get('SCRIPT_NAME','') path_info = os.environ.get('PATH_INFO','')[1:] rm = (re.compile('[0-9a-fA-F]+(&yes)*')).match(query_string) # Hier die ueblichen Paranoid-Pruefungen der Parameter if request_method!='GET': # Skript nicht mit GET aufgerufen htmlbase.PrintErrorMsg('Wrong method.') sys.exit(0) # Angabe der CA pruefen ca_name = os.environ.get('PATH_INFO','')[1:] if not ca_name: htmlbase.PrintErrorMsg('No certificate authority.') sys.exit(0) # Name der CA pruefen if not opensslcnf.data['ca'].has_key(ca_name): # CA-Definition nicht in openssl-Konfiguration enthalten htmlbase.PrintErrorMsg('Unknown certificate authority "%s".' % ca_name) sys.exit(0) ca = opensslcnf.getcadata(ca_name) # Abruf eines Zertifikates mittels Seriennummer try: serial,yes = string.split(query_string,'_') except ValueError: serial = query_string serialnumber=string.atoi(serial,16) ca_db = openssl.db.OpenSSLcaDatabaseClass(ca.database) entry = ca_db.GetEntrybySerial(serialnumber) # Kein entsprechender Eintrag gefunden if not entry: htmlbase.PrintErrorMsg('Certificate not found.') sys.exit(0) # Zertifikat ist ungueltig if entry[openssl.db.DB_type]!=openssl.db.DB_TYPE_VAL: htmlbase.PrintErrorMsg('Certificate invalid.') sys.exit(0) certfilename = os.path.join(ca.certs,'%s.pem' % (entry[openssl.db.DB_serial])) # Does the certificate file exist? if not os.path.isfile(certfilename): htmlbase.PrintErrorMsg('Certificate file not found.') sys.exit(0) # Kein Zertifikat mit angegebener Nummer gefunden if entry==[]: htmlbase.PrintErrorMsg('Certificate not found.') sys.exit(0) if entry[openssl.db.DB_type]!=openssl.db.DB_TYPE_VAL: htmlbase.PrintErrorMsg('Certificate invalid.') sys.exit(0) ssl_env = cgissl.GetAllSSLEnviron() if not ssl_env.has_key('SSL_CLIENT_S_DN'): htmlbase.PrintErrorMsg('No client certificate present.') sys.exit(0) cacert = openssl.cert.X509CertificateClass(ca.certificate) #if ssl_env['SSL_CLIENT_I_DN']!=ssl_env['SSL_SERVER_I_DN']: # htmlbase.PrintErrorMsg('Wrong issuer of client certificate.') # sys.exit(0) if ssl_env['SSL_CLIENT_S_DN']!=entry[openssl.db.DB_name]: htmlbase.PrintErrorMsg('Wrong client certificate.') sys.exit(0) cert = openssl.cert.X509CertificateClass(certfilename) if query_string[-4:]!='_yes': htmlbase.PrintHeader('Confirmation of certificate revocation.') print """The following certificate will be revoked: %s Are you really sure that you want to revoke your certificate? The following reasons can make revoking necessary:

    • Your private key was compromised (stolen, the password was sniffed etc.)
    • The content of the certificate attributes has become wrong.
    YES """ % (cert.htmlprint(),script_name,ca_name,serialnumber) htmlbase.PrintFooter() sys.exit(0) ca_db.Revoke(serialnumber) htmlbase.PrintHeader('Revoked certificate.') print 'The following certificate was revoked by you: %s' % (cert.htmlprint()) sys.exit(0) pyca-20031118/cgi-bin/browser-check.py0100755000076400001440000000747507342231312016672 0ustar michaelusers#!/usr/bin/python """ browser-check.py (c) by Michael Stroeder CGI-BIN to check cryptographic abilities of a WWW browser/server combination The SSL data only works with the environment of ApacheSSL. """ Version='0.6.6' ######################################################################## # Some variables to configure the basic behaviour ######################################################################## # Do not list the environment vars listed here hidden_envvars = [ 'DOCUMENT_ROOT','SCRIPT_NAME','SCRIPT_FILENAME','PATH', 'SERVER_SOFTWARE','SSLEAY_VERSION','SERVER_SIGNATURE' ] # Specifies a list of the acceptable symmetric key ciphers # See also http://www.apache-ssl.org/ and the ApacheSSL # run-time directives SSLBanCipher, SSLRequireCipher, SSLRequiredCiphers sec_sslacceptedciphers = [ 'IDEA-CBC-SHA', 'RC4-MD5', 'RC4-SHA', 'IDEA-CBC-MD5', 'DES-CBC3-SHA', 'DH-DSS-DES-CBC3-SHA', 'DH-RSA-DES-CBC3-SHA', 'EDH-DSS-DES-CBC3-SHA', 'EDH-RSA-DES-CBC3-SHA', 'ADH-RC4-MD5', 'ADH-DES-CBC3-SHA', 'FZA-RC4-SHA', 'RC2-CBC-MD5', 'DES-CBC3-MD5' ] ######################################################################## # There's nothing to configure below this line ######################################################################## import sys,os,string,time,re,urllib import pycacnf,htmlbase,charset ############################################################################### # Umgebungs-Variablen ############################################################################### sys.stdin.close() gmt=time.time()-3600*time.daylight+time.timezone htmlbase.PrintHeader('Cryptographic Browser Check') htmlbase.PrintHeading('Cryptographic Browser Check') htmlbase.PrintHeading('SSL',2) if os.environ.has_key('HTTPS'): htmlbase.PrintHeading('SSL symmetric cipher',3) print 'You connected with cipher %s, key size %s Bit, secret key size %s Bit.

    ' % ( os.environ['SSL_CIPHER'], os.environ['HTTPS_KEYSIZE'], os.environ['HTTPS_SECRETKEYSIZE'] ) htmlbase.PrintHeading('Client Certificate',3) ssl_client_dn = os.environ.get('SSL_CLIENT_DN','') if ssl_client_dn: ssl_client_idn = os.environ.get('SSL_CLIENT_I_DN','') if not ssl_client_idn: ssl_client_idn = os.environ.get('SSL_CLIENT_IDN','') print 'Your client sent the following certificate:
    %s%s

    ' % ( string.join(string.split(charset.t612html4(ssl_client_dn[1:]),'/'),'
    '), string.join(string.split(charset.t612html4(ssl_client_idn[1:]),'/'),'
    ') ) else: print 'Your client did not send a certificate or the server did not request a client certificate.' else: print 'This was not a SSL connection at all.' htmlbase.PrintHeading('Test Key Generation',2) query_string=os.environ.get('QUERY_STRING','') if query_string: spkac_rm=re.compile('^SPKAC=.*').match(query_string) if spkac_rm and spkac_rm.string==query_string: spkac_req=urllib.unquote_plus(query_string[6:]) print 'Your client submitted the following SPKAC request (%d Bytes):

    %s
    ' % (len(spkac_req),spkac_req) else: print 'The format of the submitted SPKAC request was wrong.' else: print """
    Key length:
    """ htmlbase.PrintHeading('Environment Variables') print '' env_keys=os.environ.keys() hidden_envvars.append('QUERY_STRING') for env in hidden_envvars: try: env_keys.remove(env) except ValueError: pass env_keys.sort() for env in env_keys: if env[0:4]!='SSL_': print '' % (env,os.environ[env]) print '
    %s%s
    ' htmlbase.PrintFooter() sys.exit(0) pyca-20031118/cgi-bin/ns-check-rev.py0100755000076400001440000000462507230415625016422 0ustar michaelusers#!/usr/bin/python """ ns-check-rev.py (c) by Michael Stroeder CGI-BIN for On-line checking of certificates - a handler for URL in Netscape extension nsRevocationUrl. Have look at a x509_extensions-section and the attributes nsBaseUrl and nsRevocationUrl Input: PATH_INFO - Name of CA in openssl.cnf (section [ca] of openssl.cnf) QUERY_STRING - Serial number of desired certificate max. 8 digits hexadecimal (32 Bit) Examples: ns-check-rev.py/Persona?537A checks if certificate number 0x537A issued of CA "Persona" is valid Output: Content-type: application/x-netscape-revocation 0 if certificate is valid <=> V in index.txt 1 if certificate is invalid """ Version='0.6.6' import sys, os, string, re, pycacnf, htmlbase, openssl from pycacnf import opensslcnf, pyca_section # Ein paar Umgebungsvariablen auslesen, welche der Apache liefert request_method = os.environ['REQUEST_METHOD'] query_string = os.environ['QUERY_STRING'] ca_name = os.environ.get('PATH_INFO','')[1:] # Wir lesen rein gar nix von Standardeingabe => gleich dicht machen sys.stdin.close() # Hier die ueblichen Paranoid-Pruefungen der Parameter rm = (re.compile('[0-9a-fA-F]+')).match(query_string) if (request_method!='GET') or \ (len(query_string)>8) or \ not rm or \ rm.group(0)!=query_string: # Skript nicht mit GET aufgerufen # Seriennummer mit mehr 32 Bit # Parameter war keine Hex-Nummer # => Kommentarloses Ende sys.exit(0) if not ca_name: htmlbase.PrintErrorMsg('No certificate authority.') sys.exit(0) if not opensslcnf.data['ca'].has_key(ca_name): # CA-Definition nicht in openssl-Konfiguration enthalten htmlbase.PrintErrorMsg('Unknown certificate authority "%s"!' % ca_name) sys.exit(0) ca_section=opensslcnf.data[opensslcnf.data['ca'][ca_name]] ca_dir = ca_section.get('dir','') ca_database = string.replace(ca_section.get('database','$dir/index.txt'),'$dir',ca_dir) # Hex-String in Integer wandeln serialnumber=string.atoi(query_string,16) # Eintrag suchen lassen entry = openssl.db.GetEntrybySerial(ca_database,serialnumber) # Header schreiben print 'Content-type: application/x-netscape-revocation\n' # Kein Zertifikat mit angegebener Nummer gefunden if not entry: print 1 sys.exit(0) # Zertifikat gueltig <=> type-Feld ist 'V' print not (entry and openssl.db.IsValid(entry)) #if entry[openssl.db.DB_type]==openssl.db.DB_TYPE_VAL: # print 0 #else: # print 1 sys.exit(0) pyca-20031118/cgi-bin/view-cert.py0100755000076400001440000001475107745153146016053 0ustar michaelusers#!/usr/bin/python """ view-cert.py (c) by Michael Stroeder CGI-BIN for viewing certificates with Netscape Navigator and M$ Internet Explorer Input: PATH_INFO - Name of CA in openssl.cnf (section [ca] of openssl.cnf) - Type of certificate ('email', 'user', 'ca', 'crl') QUERY_STRING only for ('email', 'user') - Serial number of desired certificate max. 8 digits hexadecimal (32 Bit) Examples: view-cert.py/Persona/ca displays CA certificate of CA "Persona" view-cert.py/Persona/email?01 displays client certificate of CA "Persona" with serial 0x01 view-cert.py/Server/crl displays CRL of CA "Server" view-cert.py/Server/server?01 displays server certificate of CA "Server" with serial 0x01 """ __version__ = '0.6.6' import sys,os,string,re,pycacnf,htmlbase,openssl,charset from time import time,localtime,strftime,mktime from pycacnf import opensslcnf, pyca_section from openssl.db import \ empty_DN_dict, \ DB_type,DB_exp_date,DB_rev_date,DB_serial,DB_file,DB_name,DB_number, \ DB_TYPE_REV,DB_TYPE_EXP,DB_TYPE_VAL, \ dbtime2tuple,GetEntriesbyDN,SplitDN # Wir lesen rein gar nix von Standardeingabe => gleich dicht machen sys.stdin.close() # Path to openssl executable openssl.bin_filename = pyca_section.get('OpenSSLExec','/usr/bin/openssl') # Ein paar Umgebungsvariablen auslesen, welche der Apache liefert request_method = os.environ.get('REQUEST_METHOD','') query_string = os.environ.get('QUERY_STRING','') path_info = os.environ.get('PATH_INFO','')[1:] nsBaseUrl = pyca_section.get('nsBaseUrl','/') nsGetCertUrl = pyca_section.get('nsGetCertUrl','get-cert.py') nsViewCertUrl = pyca_section.get('nsViewCertUrl','view-cert.py') # Hier die ueblichen Paranoid-Pruefungen der Parameter if request_method!='GET': # Skript nicht mit GET aufgerufen htmlbase.PrintErrorMsg('Wrong method.') sys.exit(0) # Bezeichnung der Sub-CA und MIME-Typ aus PATH_INFO splitten try: ca_name, cert_typeandformat = string.split(path_info,'/',1) cert_typeandformat=string.lower(cert_typeandformat) except ValueError: htmlbase.PrintErrorMsg('Invalid parameter format.') sys.exit(0) # Name der CA pruefen if not opensslcnf.data['ca'].has_key(ca_name): # CA-Definition nicht in openssl-Konfiguration enthalten htmlbase.PrintErrorMsg('Unknown certificate authority "%s".' % ca_name) sys.exit(0) if re.compile('^(ca|crl|server|user|email)(.(der|pem|b64|crt|crl))*$').match(cert_typeandformat) is None: htmlbase.PrintErrorMsg('Certificate type has invalid format.') sys.exit(0) try: cert_type,cert_format = string.split(cert_typeandformat,'.',1) except ValueError: cert_type,cert_format = cert_typeandformat,'der' if cert_format=='crt' or cert_format=='crl': cert_format='der' if len(query_string)>8: # Seriennummer mit mehr 32 Bit htmlbase.PrintErrorMsg('Serial number too long.') sys.exit(0) if (not cert_type in ['ca','crl']) and (not query_string): # keine Seriennummer htmlbase.PrintErrorMsg('No serial number.') sys.exit(0) # Process request ca = opensslcnf.getcadata(ca_name) if cert_type=='ca': # Abruf des CA-Zertifikates certfilename = ca.certificate elif cert_type=='crl': certfilename = ca.crl elif cert_type in ['user','email','server']: if re.compile('^[0-9a-fA-F]+$').match(query_string) is None: # Parameter war keine Hex-Nummer htmlbase.PrintErrorMsg('Serial number not in hexadecimal format.') sys.exit(0) # Abruf eines Zertifikates mittels Seriennummer serialnumber=string.atoi(query_string,16) entry = openssl.db.GetEntrybySerial(ca.database,serialnumber) # Kein entsprechender Eintrag gefunden if not entry: htmlbase.PrintErrorMsg('Certificate not found.') sys.exit(0) certfilename = os.path.join(ca.certs,'%s.pem' % (entry[DB_serial])) else: # Zertifikattyp war nicht gueltig htmlbase.PrintErrorMsg('Invalid certificate type "%s"' % cert_type) sys.exit(0) # Does the certificate file exist? if not os.path.isfile(certfilename): htmlbase.PrintErrorMsg('Certificate file not found.') sys.exit(0) if cert_type=='crl': htmlbase.PrintHeader('View CRL') htmlbase.PrintHeading('View CRL') crl = openssl.cert.CRLClass(certfilename) issuerdatalist = [] for attr in openssl.cert.X509v1_certattrlist: issuerdatalist.append(string.strip(charset.asn12html4(crl.issuer.get(attr,'')))) print """
    This CRL was issued by:
    %s
    last updated:
    %s
    next update:
    %s

    Download CRL


    """ % ( \
            string.join(issuerdatalist,'
    '), crl.lastUpdate,crl.nextUpdate, nsBaseUrl,nsGetCertUrl,ca_name ) sys.stdout.flush() os.system('%s crl -inform PEM -in "%s" -noout -text' %(openssl.bin_filename,ca.crl)) sys.stdout.flush() print '
    ' htmlbase.PrintFooter() elif cert_type=='ca': htmlbase.PrintHeader('View CA Certificate') htmlbase.PrintHeading('View CA Certificate') cert = openssl.cert.X509CertificateClass(certfilename) print """ %s

    Download certificate


    """ % (cert.htmlprint(),nsBaseUrl,nsGetCertUrl,ca_name)
    
      sys.stdout.flush()
      os.system('%s x509 -inform PEM -in "%s" -noout -text' %(openssl.bin_filename,certfilename))
      sys.stdout.flush()
    
      print '
    ' htmlbase.PrintFooter() elif cert_type in ['user','email','server']: htmlbase.PrintHeader('View Certificate') htmlbase.PrintHeading('View Certificate') cert = openssl.cert.X509CertificateClass(certfilename) if entry[DB_type]==openssl.db.DB_TYPE_VAL: statusstr = 'Certificate is valid.' elif entry[DB_type]==openssl.db.DB_TYPE_REV: statusstr = 'Certificate revoked since %s.' % (strftime('%Y-%m-%d %H:%M',localtime(mktime(dbtime2tuple(entry[DB_rev_date]))))) elif entry[DB_type]==openssl.db.DB_TYPE_EXP: statusstr = 'Certificate expired.' print """

    Current status:
    %s

    %s """ % (statusstr,cert.htmlprint()) print """

    Download certificate   View issuer certificate


    """ % ( \
    	nsBaseUrl,nsGetCertUrl,ca_name,cert_type,entry[DB_serial],
    	nsBaseUrl,nsViewCertUrl,ca_name
          )
      sys.stdout.flush()
      os.system('%s x509 -inform PEM -in "%s" -noout -text' %(openssl.bin_filename,certfilename))
      sys.stdout.flush()
      print '
    ' htmlbase.PrintFooter() sys.exit(0)