sl@0: # sl@0: # Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). sl@0: # All rights reserved. sl@0: # This component and the accompanying materials are made available sl@0: # under the terms of the License "Eclipse Public License v1.0" sl@0: # which accompanies this distribution, and is available sl@0: # at the URL "http://www.eclipse.org/legal/epl-v10.html". sl@0: # sl@0: # Initial Contributors: sl@0: # Nokia Corporation - initial contribution. sl@0: # sl@0: # Contributors: sl@0: # sl@0: # Description: sl@0: # Script to convert the Mozilla certificate store into the store format Symbian OS understands. sl@0: # sl@0: # Mozilla certificate store and its associated license is available at sl@0: # http://mxr.mozilla.org/mozilla/source/security/nss/lib/ckfw/builtins/certdata.txt?raw=1 sl@0: # sl@0: # sl@0: sl@0: import string, getopt, sys, subprocess, glob, os sl@0: sl@0: # sl@0: # Default input files sl@0: # sl@0: inFileMozillaCerts = "certdata.txt" sl@0: inFileTrustMapping = "trustmapping.txt" sl@0: sl@0: # sl@0: # Output path (don't change this!) and other temp files sl@0: # sl@0: outPath = ".\\" sl@0: outFileCaCerts = "cacerts_text.txt" sl@0: outFileCertClients = "certclients_text.txt" sl@0: outCertAppOutput = "output.txt" sl@0: sl@0: # sl@0: # Constants sl@0: # sl@0: ERROR_NONE = 0 sl@0: ERROR_EOF = -1 sl@0: ERROR_GENERAL = -2 sl@0: sl@0: # sl@0: # Class CertRecord sl@0: # sl@0: class CertRecord: sl@0: def __init__(self, file): sl@0: self.file = file sl@0: # Read over the first CKA_CLASS record sl@0: value = "" sl@0: self.ReadTokenValue("CKA_CLASS") sl@0: # Can we assert if value != "CKO_NETSCAPE_BUILTIN_ROOT_LIST" sl@0: sl@0: # Read and parse next record, return 0 if no more records exist sl@0: def Next(self): sl@0: # Read next certificate token sl@0: err, value = self.ReadTokenValue("CKA_CLASS") sl@0: if (err == ERROR_EOF): sl@0: return err sl@0: if (err != ERROR_NONE or value != "CKO_CERTIFICATE"): sl@0: return err sl@0: sl@0: # Read the cert label sl@0: err, self.certLabel = self.ReadTokenValue("CKA_LABEL") sl@0: if (err != ERROR_NONE): sl@0: return err sl@0: sl@0: # Read the cert type sl@0: err, self.certType = self.ReadTokenValue("CKA_CERTIFICATE_TYPE") sl@0: if (err != ERROR_NONE): sl@0: return err sl@0: sl@0: # Read the cert serial number sl@0: err, self.certSerialNum = self.ReadTokenValue("CKA_SERIAL_NUMBER") sl@0: if (err != ERROR_NONE): sl@0: return err sl@0: sl@0: # Read the actual cert data (DER encoded) sl@0: err, self.certData = self.ReadTokenValue("CKA_VALUE") sl@0: if (err != ERROR_NONE): sl@0: return err sl@0: sl@0: # Read the trust details sl@0: err, value = self.ReadTokenValue("CKA_CLASS") sl@0: if (err != ERROR_NONE or value != "CKO_NETSCAPE_TRUST"): sl@0: return err sl@0: sl@0: # Read the trust label and match it with cert label sl@0: err, self.trustLabel = self.ReadTokenValue("CKA_LABEL") sl@0: if (err != ERROR_NONE or self.trustLabel != self.certLabel): sl@0: print "Certificate and Trust label mismatch or not found for cert " + self.certLabel sl@0: return err sl@0: sl@0: # Read the SHA1 hash (aka thumbprint) sl@0: err, self.trustSha1Hash = self.ReadTokenValue("CKA_CERT_SHA1_HASH") sl@0: sl@0: # Read the trust serial number and match it with cert serial number sl@0: err, self.trustSerialNum = self.ReadTokenValue("CKA_SERIAL_NUMBER") sl@0: if (err != ERROR_NONE or self.trustSerialNum != self.certSerialNum): sl@0: print "Warning: Certificate and Trust serial number mismatch or not found for cert " + self.certLabel sl@0: sl@0: # Read the trust list. This has a variable token so can't use ReadTokenValue method sl@0: err, self.trustTrustList = self.ReadTrustValues() sl@0: if (err != ERROR_NONE): sl@0: return err sl@0: sl@0: return ERROR_NONE sl@0: sl@0: def ReadTrustValues(self): sl@0: # Keep reading lines till token "CKA_TRUST_STEP_UP_APPROVED" found sl@0: trustList = [] sl@0: for line in self.file: sl@0: line = line.rstrip('\n') sl@0: fields = line.split(" ") sl@0: if (len(fields) == 0): sl@0: continue sl@0: if (fields[0] == "CKA_TRUST_STEP_UP_APPROVED"): sl@0: # Done reading trust settings sl@0: return ERROR_NONE, trustList sl@0: break sl@0: if (fields[1] == "CK_TRUST"): sl@0: if ((fields[2] == "CKT_NETSCAPE_TRUSTED_DELEGATOR")): sl@0: trustList.append(fields[0].strip()) sl@0: else: sl@0: # Something is wrong sl@0: print "Error reading trust settings. " + line sl@0: return ERROR_GENERAL, [] sl@0: sl@0: # End of file? sl@0: if (line == ""): sl@0: return ERROR_EOF, "" sl@0: print "Error in ReadTrustValues(). Token ('CKA_TRUST_STEP_UP_APPROVED') not found!" sl@0: return ERROR_GENERAL, "" sl@0: sl@0: def ReadTokenValue(self, token): sl@0: # Keep reading lines till token found sl@0: for line in self.file: sl@0: line = line.rstrip('\n') sl@0: fields = line.split(" ") sl@0: if (len(fields) == 0): sl@0: continue sl@0: if (fields[0] == token): sl@0: if (fields[1] != "MULTILINE_OCTAL"): sl@0: value = " ".join(fields[2:]) sl@0: return ERROR_NONE, value sl@0: else: sl@0: # Read multiline octal value till END sl@0: value="" sl@0: for nextline in self.file: sl@0: nextline = nextline.rstrip('\n') sl@0: if (nextline == "END"): sl@0: break sl@0: if (nextline != ""): sl@0: # Convert string of octal to binary data sl@0: # There must be an easier way than this! sl@0: octalWordList = nextline.split("\\") sl@0: for octalWord in octalWordList: sl@0: if (octalWord != ""): sl@0: value = value + chr(int(octalWord, 8)) sl@0: else: sl@0: print "ReadTokenValue(" + token + ") awaiting END. Unexpected end of file!" sl@0: return ERROR_EOF, "" sl@0: return ERROR_NONE, value sl@0: sl@0: #print "ReadTokenValue(" + token + "). Token not found!" sl@0: return ERROR_EOF, "" sl@0: sl@0: # sl@0: # Global function ReadTrustMapping() sl@0: # sl@0: def ReadTrustMapping(file): sl@0: trustMapping = [] sl@0: for line in file: sl@0: line = line.rstrip('\n') sl@0: if (line == "" or line[0] == "#"): sl@0: continue sl@0: fields = line.split(",") sl@0: if (len(fields) == 0): sl@0: continue sl@0: if ((len(fields) % 2) != 1): sl@0: print "Error in file '%s' in line '%s'\n" % inFileTrustMapping % line sl@0: return GENERAL_ERROR, [[]] sl@0: mozTrust = fields[0].strip() sl@0: for index in range(1, len(fields), 2): sl@0: appUID = fields[index].strip() sl@0: appName = fields[index + 1].strip() sl@0: trustMapping.append([mozTrust, appUID, appName]) sl@0: return ERROR_NONE, trustMapping sl@0: sl@0: # sl@0: # Global function ReadCommandlineArgs() sl@0: # sl@0: def ReadCommandlineArgs(argv): sl@0: try: sl@0: flags, args = getopt.getopt(argv[1:], "hm:t:", ["help", "mozilla=", "trust="]) sl@0: except getopt.GetoptError, err: sl@0: # Print usage sl@0: print str(err) + "\n" sl@0: PrintUsage() sl@0: sys.exit(-1) sl@0: for flag, arg in flags: sl@0: if flag in ("-h", "--help"): sl@0: PrintUsage() sl@0: sys.exit() sl@0: elif flag in ("-m", "--mozilla"): sl@0: globals()["inFileMozillaCerts"] = arg sl@0: elif flag in ("-t", "--trust"): sl@0: globals()["inFileTrustMapping"] = arg sl@0: print "certconvert - This script converts the Mozilla certificate store into Symbian OS certificate store." sl@0: print "\nInput Mozilla store file: %s" % globals()["inFileMozillaCerts"] sl@0: print "Input trust mapping: %s" % globals()["inFileTrustMapping"] sl@0: sl@0: # sl@0: # sl@0: # sl@0: def PrintUsage(): sl@0: print "certconvert - This script converts the Mozilla certificate store into Symbian OS certificate store." sl@0: print "It uses certapp for the conversion so certapp must be in the path." sl@0: print "Usage: certconvert [-h] | [-m -t ] [-o ]" sl@0: print "where:" sl@0: print "-h | --help\tshows this help" sl@0: print "-m | --mozilla\tis used to specify the Mozilla certificate store input file." sl@0: print "\t\tIf not specified default is taken as 'certdata.txt'." sl@0: print "-t | --trust\tis used to specify the input trust mapping input file." sl@0: print "\t\tThis file maps the trust settings from the Mozilla store to " sl@0: print "\t\tSymbian's applications and uids." sl@0: print "\t\tIf not specified default is taken as 'trustmapping.txt'." sl@0: sl@0: # sl@0: # Main starts here sl@0: # sl@0: sl@0: # Read and process command line arguments sl@0: ReadCommandlineArgs(sys.argv) sl@0: sl@0: # First read the trust mappings file sl@0: print "Reading trust mapping file...", sl@0: file = open(inFileTrustMapping, "r") sl@0: err, trustMapping = ReadTrustMapping(file) sl@0: if (err != ERROR_NONE): sl@0: print "\nError reading trust mapping file!\n" sl@0: sys.exit(-1) sl@0: file.close() sl@0: print "done." sl@0: sl@0: print "Reading Mozilla certificate store and processing certificates", sl@0: inFileMoz=open(inFileMozillaCerts, "r") sl@0: record = CertRecord(inFileMoz) sl@0: inRecNum = outRecNum = 0 sl@0: while (record.Next() == ERROR_NONE): sl@0: inRecNum = inRecNum + 1 sl@0: #print "Read record %d: %s" % (inRecNum, record.certLabel) sl@0: # Do filtering of records (if any) sl@0: sl@0: outRecNum = outRecNum + 1 sl@0: # Create the human readable filecertstore entry sl@0: if (outRecNum == 1): sl@0: if (os.path.exists(outPath) == False): sl@0: os.makedirs(outPath) sl@0: if (os.path.exists(outPath + "\\certs") == False): sl@0: os.makedirs(outPath + "\\certs") sl@0: outFileSym = open(outPath + outFileCaCerts, "w") sl@0: outFileSym.write("StartCertStoreEntries\n") sl@0: sl@0: outFileSym.write("\t# Entry %d\n" % outRecNum) sl@0: sl@0: # Write out the SHA1 hash of the certificate (to make it easier to compare certs) sl@0: # Convert to hex sl@0: sha1hash = "" sl@0: #octalWordList = record.trustSha1Hash.split("\\") sl@0: for index in range(0, len(record.trustSha1Hash)): sl@0: hexdigits = hex(ord(record.trustSha1Hash[index]))[2:] sl@0: hexdigits = hexdigits.zfill(2) sl@0: sha1hash = sha1hash + hexdigits + " " sl@0: outFileSym.write("\t# Thumbprint(hex) %s\n" % sha1hash) sl@0: sl@0: outFileSym.write("\tStartEntry " + record.certLabel + "\n") sl@0: outFileSym.write("\t\tDeletable true\n") sl@0: outFileSym.write("\t\tFormat EX509Certificate\n") sl@0: outFileSym.write("\t\tCertOwnerType ECACertificate\n") sl@0: outFileSym.write("\t\tSubjectKeyId auto\n") sl@0: outFileSym.write("\t\tIssuerKeyId auto\n") sl@0: sl@0: # Write out trust details sl@0: outFileSym.write("\t\tStartApplicationList\n") sl@0: for trust in record.trustTrustList: sl@0: # Look for the mapping sl@0: for mapping in trustMapping: sl@0: if (trust == mapping[0]): sl@0: # Found a mapping. Add it and keep on looking since sl@0: # there could be more than one app mapping sl@0: outFileSym.write('\t\t\tApplication "' + mapping[2] + '"\n'); sl@0: outFileSym.write("\t\tEndApplicationList\n") sl@0: outFileSym.write("\t\tTrusted true\n") sl@0: certFileName = "certs\\\\cert%04d" % outRecNum + ".der" sl@0: outFileSym.write('\t\tDataFileName "' + certFileName + '"\n') sl@0: outFileSym.write("\tEndEntry\n\n") sl@0: # Write the certificate file sl@0: outFileCert = open(outPath + certFileName, "wb") sl@0: outFileCert.write(record.certData) sl@0: outFileCert.close() sl@0: print ".", sl@0: sl@0: if (outRecNum > 0): sl@0: outFileSym.write("EndCertStoreEntries\n") sl@0: outFileSym.close() sl@0: print "done." sl@0: sl@0: # Finally create the app to uid mapping file for Symbian OS sl@0: if (outRecNum > 0): sl@0: outFileSym = open(outPath + outFileCertClients, "w") sl@0: outFileSym.write("StartClientInfo\n") sl@0: for index in range(0, len(trustMapping)): sl@0: outFileSym.write("\t#Entry %d\n" % (index + 1)) sl@0: outFileSym.write("\t\tUid %s\n" % trustMapping[index][1]) sl@0: outFileSym.write('\t\tName "%s"\n' % trustMapping[index][2]) sl@0: outFileSym.write("EndClientInfo\n") sl@0: outFileSym.close() sl@0: inFileMoz.close() sl@0: sl@0: print "Invoking certapp tool to create the Symbian certificate store...", sl@0: certappCmd = "certapp" + \ sl@0: " --in --hca=" + outPath + outFileCaCerts + " --hcc=" + outPath + outFileCertClients + \ sl@0: " --out --bca=" + outPath + "cacerts.dat" + " --bcc=" + outPath + "certclients.dat" sl@0: sl@0: dummyFile = open(outPath + outCertAppOutput, "w") sl@0: p = subprocess.Popen(certappCmd, 0, None, None, dummyFile, dummyFile) sl@0: retcode = p.wait() sl@0: dummyFile.close() sl@0: sl@0: if (retcode != 0): sl@0: print "\ncertapp returned error code: %d" % retcode sl@0: print certappCmd sl@0: print "For details see file " + outPath + outCertAppOutput sl@0: print "Leaving temp files untouched for debugging" sl@0: else: sl@0: print "done." sl@0: print "Cleaning up temp files...", sl@0: files = glob.glob(outPath + "certs\\*") sl@0: for file in files: sl@0: os.remove(file) sl@0: os.rmdir(outPath + "certs") sl@0: os.remove(outPath + outFileCaCerts) sl@0: os.remove(outPath + outFileCertClients) sl@0: os.remove(outPath + outCertAppOutput) sl@0: print "done." sl@0: print "Done. Read %d" % inRecNum + " certificates. Written %d" % outRecNum + " certificates.\n"