]>
Commit | Line | Data |
---|---|---|
f0aa99fb AF |
1 | #!/usr/bin/python |
2 | # vim:set et sw=4: | |
3 | # | |
4 | # certdata2pem.py - splits certdata.txt into multiple files | |
5 | # | |
6 | # Copyright (C) 2009 Philipp Kern <pkern@debian.org> | |
7 | # Copyright (C) 2013 Kai Engert <kaie@redhat.com> | |
8 | # | |
9 | # This program is free software; you can redistribute it and/or modify | |
10 | # it under the terms of the GNU General Public License as published by | |
11 | # the Free Software Foundation; either version 2 of the License, or | |
12 | # (at your option) any later version. | |
13 | # | |
14 | # This program is distributed in the hope that it will be useful, | |
15 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
17 | # GNU General Public License for more details. | |
18 | # | |
19 | # You should have received a copy of the GNU General Public License | |
20 | # along with this program; if not, write to the Free Software | |
21 | # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, | |
22 | # USA. | |
23 | ||
24 | import base64 | |
25 | import os.path | |
26 | import re | |
27 | import sys | |
28 | import textwrap | |
29 | import urllib | |
30 | ||
31 | objects = [] | |
32 | ||
33 | def printable_serial(obj): | |
34 | return ".".join(map(lambda x:str(ord(x)), obj['CKA_SERIAL_NUMBER'])) | |
35 | ||
36 | # Dirty file parser. | |
37 | in_data, in_multiline, in_obj = False, False, False | |
38 | field, type, value, obj = None, None, None, dict() | |
39 | for line in open('certdata.txt', 'r'): | |
40 | # Ignore the file header. | |
41 | if not in_data: | |
42 | if line.startswith('BEGINDATA'): | |
43 | in_data = True | |
44 | continue | |
45 | # Ignore comment lines. | |
46 | if line.startswith('#'): | |
47 | continue | |
48 | # Empty lines are significant if we are inside an object. | |
49 | if in_obj and len(line.strip()) == 0: | |
50 | objects.append(obj) | |
51 | obj = dict() | |
52 | in_obj = False | |
53 | continue | |
54 | if len(line.strip()) == 0: | |
55 | continue | |
56 | if in_multiline: | |
57 | if not line.startswith('END'): | |
58 | if type == 'MULTILINE_OCTAL': | |
59 | line = line.strip() | |
60 | for i in re.finditer(r'\\([0-3][0-7][0-7])', line): | |
61 | value += chr(int(i.group(1), 8)) | |
62 | else: | |
63 | value += line | |
64 | continue | |
65 | obj[field] = value | |
66 | in_multiline = False | |
67 | continue | |
68 | if line.startswith('CKA_CLASS'): | |
69 | in_obj = True | |
70 | line_parts = line.strip().split(' ', 2) | |
71 | if len(line_parts) > 2: | |
72 | field, type = line_parts[0:2] | |
73 | value = ' '.join(line_parts[2:]) | |
74 | elif len(line_parts) == 2: | |
75 | field, type = line_parts | |
76 | value = None | |
77 | else: | |
78 | raise NotImplementedError, 'line_parts < 2 not supported.\n' + line | |
79 | if type == 'MULTILINE_OCTAL': | |
80 | in_multiline = True | |
81 | value = "" | |
82 | continue | |
83 | obj[field] = value | |
84 | if len(obj.items()) > 0: | |
85 | objects.append(obj) | |
86 | ||
87 | # Build up trust database. | |
88 | trustmap = dict() | |
89 | for obj in objects: | |
90 | if obj['CKA_CLASS'] != 'CKO_NSS_TRUST': | |
91 | continue | |
92 | key = obj['CKA_LABEL'] + printable_serial(obj) | |
93 | trustmap[key] = obj | |
94 | print " added trust", key | |
95 | ||
96 | # Build up cert database. | |
97 | certmap = dict() | |
98 | for obj in objects: | |
99 | if obj['CKA_CLASS'] != 'CKO_CERTIFICATE': | |
100 | continue | |
101 | key = obj['CKA_LABEL'] + printable_serial(obj) | |
102 | certmap[key] = obj | |
103 | print " added cert", key | |
104 | ||
105 | def obj_to_filename(obj): | |
106 | label = obj['CKA_LABEL'][1:-1] | |
107 | label = label.replace('/', '_')\ | |
108 | .replace(' ', '_')\ | |
109 | .replace('(', '=')\ | |
110 | .replace(')', '=')\ | |
111 | .replace(',', '_') | |
112 | label = re.sub(r'\\x[0-9a-fA-F]{2}', lambda m:chr(int(m.group(0)[2:], 16)), label) | |
113 | serial = printable_serial(obj) | |
114 | return label + ":" + serial | |
115 | ||
116 | trust_types = { | |
117 | "CKA_TRUST_DIGITAL_SIGNATURE": "digital-signature", | |
118 | "CKA_TRUST_NON_REPUDIATION": "non-repudiation", | |
119 | "CKA_TRUST_KEY_ENCIPHERMENT": "key-encipherment", | |
120 | "CKA_TRUST_DATA_ENCIPHERMENT": "data-encipherment", | |
121 | "CKA_TRUST_KEY_AGREEMENT": "key-agreement", | |
122 | "CKA_TRUST_KEY_CERT_SIGN": "cert-sign", | |
123 | "CKA_TRUST_CRL_SIGN": "crl-sign", | |
124 | "CKA_TRUST_SERVER_AUTH": "server-auth", | |
125 | "CKA_TRUST_CLIENT_AUTH": "client-auth", | |
126 | "CKA_TRUST_CODE_SIGNING": "code-signing", | |
127 | "CKA_TRUST_EMAIL_PROTECTION": "email-protection", | |
128 | "CKA_TRUST_IPSEC_END_SYSTEM": "ipsec-end-system", | |
129 | "CKA_TRUST_IPSEC_TUNNEL": "ipsec-tunnel", | |
130 | "CKA_TRUST_IPSEC_USER": "ipsec-user", | |
131 | "CKA_TRUST_TIME_STAMPING": "time-stamping", | |
132 | "CKA_TRUST_STEP_UP_APPROVED": "step-up-approved", | |
133 | } | |
134 | ||
135 | legacy_trust_types = { | |
136 | "LEGACY_CKA_TRUST_SERVER_AUTH": "server-auth", | |
137 | "LEGACY_CKA_TRUST_CODE_SIGNING": "code-signing", | |
138 | "LEGACY_CKA_TRUST_EMAIL_PROTECTION": "email-protection", | |
139 | } | |
140 | ||
141 | legacy_to_real_trust_types = { | |
142 | "LEGACY_CKA_TRUST_SERVER_AUTH": "CKA_TRUST_SERVER_AUTH", | |
143 | "LEGACY_CKA_TRUST_CODE_SIGNING": "CKA_TRUST_CODE_SIGNING", | |
144 | "LEGACY_CKA_TRUST_EMAIL_PROTECTION": "CKA_TRUST_EMAIL_PROTECTION", | |
145 | } | |
146 | ||
147 | openssl_trust = { | |
148 | "CKA_TRUST_SERVER_AUTH": "serverAuth", | |
149 | "CKA_TRUST_CLIENT_AUTH": "clientAuth", | |
150 | "CKA_TRUST_CODE_SIGNING": "codeSigning", | |
151 | "CKA_TRUST_EMAIL_PROTECTION": "emailProtection", | |
152 | } | |
153 | ||
154 | for tobj in objects: | |
155 | if tobj['CKA_CLASS'] == 'CKO_NSS_TRUST': | |
156 | key = tobj['CKA_LABEL'] + printable_serial(tobj) | |
157 | print "producing trust for " + key | |
158 | trustbits = [] | |
159 | distrustbits = [] | |
160 | openssl_trustflags = [] | |
161 | openssl_distrustflags = [] | |
162 | legacy_trustbits = [] | |
163 | legacy_openssl_trustflags = [] | |
164 | for t in trust_types.keys(): | |
165 | if tobj.has_key(t) and tobj[t] == 'CKT_NSS_TRUSTED_DELEGATOR': | |
166 | trustbits.append(t) | |
167 | if t in openssl_trust: | |
168 | openssl_trustflags.append(openssl_trust[t]) | |
169 | if tobj.has_key(t) and tobj[t] == 'CKT_NSS_NOT_TRUSTED': | |
170 | distrustbits.append(t) | |
171 | if t in openssl_trust: | |
172 | openssl_distrustflags.append(openssl_trust[t]) | |
173 | ||
174 | for t in legacy_trust_types.keys(): | |
175 | if tobj.has_key(t) and tobj[t] == 'CKT_NSS_TRUSTED_DELEGATOR': | |
176 | real_t = legacy_to_real_trust_types[t] | |
177 | legacy_trustbits.append(real_t) | |
178 | if real_t in openssl_trust: | |
179 | legacy_openssl_trustflags.append(openssl_trust[real_t]) | |
180 | if tobj.has_key(t) and tobj[t] == 'CKT_NSS_NOT_TRUSTED': | |
181 | raise NotImplementedError, 'legacy distrust not supported.\n' + line | |
182 | ||
183 | fname = obj_to_filename(tobj) | |
184 | try: | |
185 | obj = certmap[key] | |
186 | except: | |
187 | obj = None | |
188 | ||
189 | if obj != None: | |
190 | fname += ".crt" | |
191 | else: | |
192 | fname += ".p11-kit" | |
193 | ||
194 | is_legacy = 0 | |
195 | if tobj.has_key('LEGACY_CKA_TRUST_SERVER_AUTH') or tobj.has_key('LEGACY_CKA_TRUST_EMAIL_PROTECTION') or tobj.has_key('LEGACY_CKA_TRUST_CODE_SIGNING'): | |
196 | is_legacy = 1 | |
197 | if obj == None: | |
198 | raise NotImplementedError, 'found legacy trust without certificate.\n' + line | |
199 | legacy_fname = "legacy-default/" + fname | |
200 | f = open(legacy_fname, 'w') | |
201 | f.write("# alias=%s\n"%tobj['CKA_LABEL']) | |
202 | f.write("# trust=" + " ".join(legacy_trustbits) + "\n") | |
203 | if legacy_openssl_trustflags: | |
204 | f.write("# openssl-trust=" + " ".join(legacy_openssl_trustflags) + "\n") | |
205 | f.write("-----BEGIN CERTIFICATE-----\n") | |
206 | f.write("\n".join(textwrap.wrap(base64.b64encode(obj['CKA_VALUE']), 64))) | |
207 | f.write("\n-----END CERTIFICATE-----\n") | |
208 | f.close() | |
209 | if tobj.has_key('CKA_TRUST_SERVER_AUTH') or tobj.has_key('CKA_TRUST_EMAIL_PROTECTION') or tobj.has_key('CKA_TRUST_CODE_SIGNING'): | |
210 | fname = "legacy-disable/" + fname | |
211 | else: | |
212 | continue | |
213 | ||
214 | f = open(fname, 'w') | |
215 | if obj != None: | |
216 | f.write("# alias=%s\n"%tobj['CKA_LABEL']) | |
217 | f.write("# trust=" + " ".join(trustbits) + "\n") | |
218 | f.write("# distrust=" + " ".join(distrustbits) + "\n") | |
219 | if openssl_trustflags: | |
220 | f.write("# openssl-trust=" + " ".join(openssl_trustflags) + "\n") | |
221 | if openssl_distrustflags: | |
222 | f.write("# openssl-distrust=" + " ".join(openssl_distrustflags) + "\n") | |
223 | f.write("-----BEGIN CERTIFICATE-----\n") | |
224 | f.write("\n".join(textwrap.wrap(base64.b64encode(obj['CKA_VALUE']), 64))) | |
225 | f.write("\n-----END CERTIFICATE-----\n") | |
226 | else: | |
227 | f.write("[p11-kit-object-v1]\n") | |
228 | f.write("label: "); | |
229 | f.write(tobj['CKA_LABEL']); | |
230 | f.write("\n") | |
231 | f.write("class: certificate\n") | |
232 | f.write("certificate-type: x-509\n") | |
233 | f.write("issuer: \""); | |
234 | f.write(urllib.quote(tobj['CKA_ISSUER'])); | |
235 | f.write("\"\n") | |
236 | f.write("serial-number: \""); | |
237 | f.write(urllib.quote(tobj['CKA_SERIAL_NUMBER'])); | |
238 | f.write("\"\n") | |
239 | if (tobj['CKA_TRUST_SERVER_AUTH'] == 'CKT_NSS_NOT_TRUSTED') or (tobj['CKA_TRUST_EMAIL_PROTECTION'] == 'CKT_NSS_NOT_TRUSTED') or (tobj['CKA_TRUST_CODE_SIGNING'] == 'CKT_NSS_NOT_TRUSTED'): | |
240 | f.write("x-distrusted: true\n") | |
241 | f.write("\n\n") | |
242 | f.close() | |
243 | print " -> written as '%s', trust = %s, openssl-trust = %s, distrust = %s, openssl-distrust = %s" % (fname, trustbits, openssl_trustflags, distrustbits, openssl_distrustflags) |