cryptoservices/browserootcertificates/certconvert.py
changeset 20 445614b8b140
parent 16 5ed53bb58874
child 21 7e3f204e6c81
child 22 1afc808f187d
equal deleted inserted replaced
16:5ed53bb58874 20:445614b8b140
     1 #
       
     2 # Copyright (c) 2001-2009 Nokia Corporation and/or its subsidiary(-ies).
       
     3 # All rights reserved.
       
     4 # This component and the accompanying materials are made available
       
     5 # under the terms of the License "Eclipse Public License v1.0"
       
     6 # which accompanies this distribution, and is available
       
     7 # at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     8 #
       
     9 # Initial Contributors:
       
    10 # Nokia Corporation - initial contribution.
       
    11 #
       
    12 # Contributors:
       
    13 #
       
    14 # Description: 
       
    15 # Script to convert the Mozilla certificate store into the store format Symbian OS understands.
       
    16 #
       
    17 # Mozilla certificate store and its associated license is available at
       
    18 # http://mxr.mozilla.org/mozilla/source/security/nss/lib/ckfw/builtins/certdata.txt?raw=1
       
    19 #
       
    20 #
       
    21 
       
    22 import string, getopt, sys, subprocess, glob, os
       
    23 
       
    24 #
       
    25 # Default input files
       
    26 #
       
    27 inFileMozillaCerts = "certdata.txt"
       
    28 inFileTrustMapping = "trustmapping.txt"
       
    29 
       
    30 #
       
    31 # Output path (don't change this!) and other temp files
       
    32 #
       
    33 outPath = ".\\"
       
    34 outFileCaCerts = "cacerts_text.txt"
       
    35 outFileCertClients = "certclients_text.txt"
       
    36 outCertAppOutput = "output.txt"
       
    37 
       
    38 #
       
    39 # Constants
       
    40 #
       
    41 ERROR_NONE = 0
       
    42 ERROR_EOF = -1
       
    43 ERROR_GENERAL = -2
       
    44 
       
    45 #
       
    46 # Class CertRecord
       
    47 #
       
    48 class CertRecord:
       
    49     def __init__(self, file):
       
    50         self.file = file
       
    51         # Read over the first CKA_CLASS record
       
    52         value = ""
       
    53         self.ReadTokenValue("CKA_CLASS")
       
    54         # Can we assert if value != "CKO_NETSCAPE_BUILTIN_ROOT_LIST"
       
    55 
       
    56     # Read and parse next record, return 0 if no more records exist
       
    57     def Next(self):
       
    58         # Read next certificate token
       
    59         err, value = self.ReadTokenValue("CKA_CLASS")
       
    60         if (err == ERROR_EOF):
       
    61             return err
       
    62         if (err != ERROR_NONE or value != "CKO_CERTIFICATE"):
       
    63             return err
       
    64 
       
    65         # Read the cert label
       
    66         err, self.certLabel = self.ReadTokenValue("CKA_LABEL")
       
    67         if (err != ERROR_NONE):
       
    68             return err
       
    69 
       
    70         # Read the cert type
       
    71         err, self.certType = self.ReadTokenValue("CKA_CERTIFICATE_TYPE")
       
    72         if (err != ERROR_NONE):
       
    73             return err
       
    74 
       
    75         # Read the cert serial number
       
    76         err, self.certSerialNum = self.ReadTokenValue("CKA_SERIAL_NUMBER")
       
    77         if (err != ERROR_NONE):
       
    78             return err
       
    79 
       
    80         # Read the actual cert data (DER encoded)
       
    81         err, self.certData = self.ReadTokenValue("CKA_VALUE")
       
    82         if (err != ERROR_NONE):
       
    83             return err
       
    84 
       
    85         # Read the trust details
       
    86         err, value = self.ReadTokenValue("CKA_CLASS")
       
    87         if (err != ERROR_NONE or value != "CKO_NETSCAPE_TRUST"):
       
    88             return err
       
    89 
       
    90         # Read the trust label and match it with cert label
       
    91         err, self.trustLabel = self.ReadTokenValue("CKA_LABEL")
       
    92         if (err != ERROR_NONE or self.trustLabel != self.certLabel):
       
    93             print "Certificate and Trust label mismatch or not found for cert " + self.certLabel
       
    94             return err
       
    95 
       
    96         # Read the SHA1 hash (aka thumbprint)
       
    97         err, self.trustSha1Hash = self.ReadTokenValue("CKA_CERT_SHA1_HASH")
       
    98 
       
    99         # Read the trust serial number and match it with cert serial number
       
   100         err, self.trustSerialNum = self.ReadTokenValue("CKA_SERIAL_NUMBER")
       
   101         if (err != ERROR_NONE or self.trustSerialNum != self.certSerialNum):
       
   102             print "Warning: Certificate and Trust serial number mismatch or not found for cert " + self.certLabel
       
   103 
       
   104         # Read the trust list. This has a variable token so can't use ReadTokenValue method
       
   105         err, self.trustTrustList = self.ReadTrustValues()
       
   106         if (err != ERROR_NONE):
       
   107             return err
       
   108 
       
   109         return ERROR_NONE
       
   110 
       
   111     def ReadTrustValues(self):
       
   112         # Keep reading lines till token "CKA_TRUST_STEP_UP_APPROVED" found
       
   113         trustList = []
       
   114         for line in self.file:
       
   115             line = line.rstrip('\n')
       
   116             fields = line.split(" ")
       
   117             if (len(fields) == 0):
       
   118                 continue
       
   119             if (fields[0] == "CKA_TRUST_STEP_UP_APPROVED"):
       
   120                 # Done reading trust settings
       
   121                 return ERROR_NONE, trustList
       
   122                 break
       
   123             if (fields[1] == "CK_TRUST"):
       
   124                 if ((fields[2] == "CKT_NETSCAPE_TRUSTED_DELEGATOR")):
       
   125                     trustList.append(fields[0].strip())
       
   126             else:
       
   127                 # Something is wrong
       
   128                 print "Error reading trust settings. " + line
       
   129                 return ERROR_GENERAL, []
       
   130 
       
   131         # End of file?
       
   132         if (line == ""):
       
   133             return ERROR_EOF, ""
       
   134         print "Error in ReadTrustValues(). Token ('CKA_TRUST_STEP_UP_APPROVED') not found!"
       
   135         return ERROR_GENERAL, ""
       
   136 
       
   137     def ReadTokenValue(self, token):
       
   138         # Keep reading lines till token found
       
   139         for line in self.file:
       
   140             line = line.rstrip('\n')
       
   141             fields = line.split(" ")
       
   142             if (len(fields) == 0):
       
   143                 continue
       
   144             if (fields[0] == token):
       
   145                 if (fields[1] != "MULTILINE_OCTAL"):
       
   146                     value = " ".join(fields[2:])
       
   147                     return ERROR_NONE, value
       
   148                 else:
       
   149                     # Read multiline octal value till END
       
   150                     value=""
       
   151                     for nextline in self.file:
       
   152                         nextline = nextline.rstrip('\n')
       
   153                         if (nextline == "END"):
       
   154                             break
       
   155                         if (nextline != ""):
       
   156                             # Convert string of octal to binary data
       
   157                             # There must be an easier way than this!
       
   158                             octalWordList = nextline.split("\\")
       
   159                             for octalWord in octalWordList:
       
   160                                 if (octalWord != ""):
       
   161                                     value = value + chr(int(octalWord, 8))
       
   162                         else:
       
   163                             print "ReadTokenValue(" + token + ") awaiting END. Unexpected end of file!"
       
   164                             return ERROR_EOF, ""
       
   165                     return ERROR_NONE, value
       
   166 
       
   167         #print "ReadTokenValue(" + token + "). Token not found!"
       
   168         return ERROR_EOF, ""
       
   169 
       
   170 #
       
   171 # Global function ReadTrustMapping()
       
   172 #
       
   173 def ReadTrustMapping(file):
       
   174     trustMapping = []
       
   175     for line in file:
       
   176         line = line.rstrip('\n')
       
   177         if (line == "" or line[0] == "#"):
       
   178             continue
       
   179         fields = line.split(",")
       
   180         if (len(fields) == 0):
       
   181             continue
       
   182         if ((len(fields) % 2) != 1):
       
   183             print "Error in file '%s' in line '%s'\n" % inFileTrustMapping % line
       
   184             return GENERAL_ERROR, [[]]
       
   185         mozTrust = fields[0].strip()
       
   186         for index in range(1, len(fields), 2):
       
   187             appUID = fields[index].strip()
       
   188             appName = fields[index + 1].strip()
       
   189             trustMapping.append([mozTrust, appUID, appName])
       
   190     return ERROR_NONE, trustMapping
       
   191 
       
   192 #
       
   193 # Global function ReadCommandlineArgs()
       
   194 #
       
   195 def ReadCommandlineArgs(argv):
       
   196     try:
       
   197         flags, args = getopt.getopt(argv[1:], "hm:t:", ["help", "mozilla=", "trust="])
       
   198     except getopt.GetoptError, err:
       
   199         # Print usage
       
   200         print str(err) + "\n"
       
   201         PrintUsage()
       
   202         sys.exit(-1)
       
   203     for flag, arg in flags:
       
   204         if flag in ("-h", "--help"):
       
   205             PrintUsage()
       
   206             sys.exit()
       
   207         elif flag in ("-m", "--mozilla"):
       
   208             globals()["inFileMozillaCerts"] = arg
       
   209         elif flag in ("-t", "--trust"):
       
   210             globals()["inFileTrustMapping"] = arg
       
   211     print "certconvert - This script converts the Mozilla certificate store into Symbian OS certificate store."
       
   212     print "\nInput Mozilla store file: %s" % globals()["inFileMozillaCerts"]
       
   213     print "Input trust mapping: %s" % globals()["inFileTrustMapping"]
       
   214 
       
   215 #
       
   216 #
       
   217 #
       
   218 def PrintUsage():
       
   219     print "certconvert - This script converts the Mozilla certificate store into Symbian OS certificate store."
       
   220     print "It uses certapp for the conversion so certapp must be in the path."
       
   221     print "Usage: certconvert [-h] | [-m <file> -t <file>] [-o <outpath>]"
       
   222     print "where:"
       
   223     print "-h | --help\tshows this help"
       
   224     print "-m | --mozilla\tis used to specify the Mozilla certificate store input file."
       
   225     print "\t\tIf not specified default is taken as 'certdata.txt'."
       
   226     print "-t | --trust\tis used to specify the input trust mapping input file."
       
   227     print "\t\tThis file maps the trust settings from the Mozilla store to "
       
   228     print "\t\tSymbian's applications and uids."
       
   229     print "\t\tIf not specified default is taken as 'trustmapping.txt'."
       
   230 
       
   231 #
       
   232 # Main starts here
       
   233 #
       
   234 
       
   235 # Read and process command line arguments
       
   236 ReadCommandlineArgs(sys.argv)
       
   237 
       
   238 # First read the trust mappings file
       
   239 print "Reading trust mapping file...",
       
   240 file = open(inFileTrustMapping, "r")
       
   241 err, trustMapping = ReadTrustMapping(file)
       
   242 if (err != ERROR_NONE):
       
   243     print "\nError reading trust mapping file!\n"
       
   244     sys.exit(-1)
       
   245 file.close()
       
   246 print "done."
       
   247 
       
   248 print "Reading Mozilla certificate store and processing certificates",
       
   249 inFileMoz=open(inFileMozillaCerts, "r")
       
   250 record = CertRecord(inFileMoz)
       
   251 inRecNum = outRecNum = 0
       
   252 while (record.Next() == ERROR_NONE):
       
   253     inRecNum = inRecNum + 1
       
   254     #print "Read record %d: %s" % (inRecNum, record.certLabel)
       
   255     # Do filtering of records (if any)
       
   256     
       
   257     outRecNum = outRecNum + 1
       
   258     # Create the human readable filecertstore entry
       
   259     if (outRecNum == 1):
       
   260         if (os.path.exists(outPath) == False):
       
   261             os.makedirs(outPath)
       
   262         if (os.path.exists(outPath + "\\certs") == False):
       
   263             os.makedirs(outPath + "\\certs")
       
   264         outFileSym = open(outPath + outFileCaCerts, "w")
       
   265         outFileSym.write("StartCertStoreEntries\n")
       
   266 
       
   267     outFileSym.write("\t# Entry %d\n" % outRecNum)
       
   268 
       
   269     # Write out the SHA1 hash of the certificate (to make it easier to compare certs)
       
   270     # Convert to hex
       
   271     sha1hash = ""
       
   272     #octalWordList = record.trustSha1Hash.split("\\")
       
   273     for index in range(0, len(record.trustSha1Hash)):
       
   274         hexdigits = hex(ord(record.trustSha1Hash[index]))[2:]
       
   275         hexdigits = hexdigits.zfill(2)
       
   276         sha1hash = sha1hash + hexdigits + " "
       
   277     outFileSym.write("\t# Thumbprint(hex) %s\n" % sha1hash)
       
   278 
       
   279     outFileSym.write("\tStartEntry " + record.certLabel + "\n")
       
   280     outFileSym.write("\t\tDeletable true\n")
       
   281     outFileSym.write("\t\tFormat EX509Certificate\n")
       
   282     outFileSym.write("\t\tCertOwnerType ECACertificate\n")
       
   283     outFileSym.write("\t\tSubjectKeyId auto\n")
       
   284     outFileSym.write("\t\tIssuerKeyId auto\n")
       
   285 
       
   286     # Write out trust details
       
   287     outFileSym.write("\t\tStartApplicationList\n")
       
   288     for trust in record.trustTrustList:
       
   289         # Look for the mapping
       
   290         for mapping in trustMapping:
       
   291             if (trust == mapping[0]):
       
   292                 # Found a mapping. Add it and keep on looking since
       
   293                 # there could be more than one app mapping
       
   294                 outFileSym.write('\t\t\tApplication "' + mapping[2] + '"\n');
       
   295     outFileSym.write("\t\tEndApplicationList\n")
       
   296     outFileSym.write("\t\tTrusted true\n")
       
   297     certFileName = "certs\\\\cert%04d" % outRecNum + ".der"
       
   298     outFileSym.write('\t\tDataFileName "' + certFileName + '"\n')
       
   299     outFileSym.write("\tEndEntry\n\n")
       
   300     # Write the certificate file
       
   301     outFileCert = open(outPath + certFileName, "wb")
       
   302     outFileCert.write(record.certData)
       
   303     outFileCert.close()
       
   304     print ".",
       
   305 
       
   306 if (outRecNum > 0):
       
   307     outFileSym.write("EndCertStoreEntries\n")
       
   308     outFileSym.close()
       
   309 print "done."
       
   310 
       
   311 # Finally create the app to uid mapping file for Symbian OS
       
   312 if (outRecNum > 0):
       
   313     outFileSym = open(outPath + outFileCertClients, "w")
       
   314     outFileSym.write("StartClientInfo\n")
       
   315     for index in range(0, len(trustMapping)):
       
   316         outFileSym.write("\t#Entry %d\n" % (index + 1))
       
   317         outFileSym.write("\t\tUid %s\n" % trustMapping[index][1])
       
   318         outFileSym.write('\t\tName "%s"\n' % trustMapping[index][2])
       
   319     outFileSym.write("EndClientInfo\n")
       
   320     outFileSym.close()
       
   321 inFileMoz.close()
       
   322 
       
   323 print "Invoking certapp tool to create the Symbian certificate store...",
       
   324 certappCmd = "certapp" + \
       
   325              " --in --hca=" + outPath + outFileCaCerts + " --hcc=" + outPath + outFileCertClients + \
       
   326              " --out --bca=" + outPath + "cacerts.dat" + " --bcc=" + outPath + "certclients.dat"
       
   327 
       
   328 dummyFile = open(outPath + outCertAppOutput, "w")
       
   329 p = subprocess.Popen(certappCmd, 0, None, None, dummyFile, dummyFile)
       
   330 retcode = p.wait()
       
   331 dummyFile.close()
       
   332 
       
   333 if (retcode != 0):
       
   334     print "\ncertapp returned error code: %d" % retcode
       
   335     print certappCmd
       
   336     print "For details see file " + outPath + outCertAppOutput
       
   337     print "Leaving temp files untouched for debugging"
       
   338 else:
       
   339     print "done."
       
   340     print "Cleaning up temp files...",
       
   341     files = glob.glob(outPath + "certs\\*")
       
   342     for file in files:
       
   343        os.remove(file)
       
   344     os.rmdir(outPath + "certs")
       
   345     os.remove(outPath + outFileCaCerts)
       
   346     os.remove(outPath + outFileCertClients)
       
   347     os.remove(outPath + outCertAppOutput)
       
   348     print "done."
       
   349     print "Done. Read %d" % inRecNum + " certificates. Written %d" % outRecNum + " certificates.\n"