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" |
|