6d164aedd7
https://www.ccadb.org/resources Our normal root certs come from mozilla, but mozilla does not evaluate code signing. Currently code signing is only used my Microsoft .net, so we need to get code signing certs from Microsoft's code signing list. The certs in this list will only show up in the code signing lists or in the general list with only code signing set.
321 lines
10 KiB
Python
321 lines
10 KiB
Python
#!/usr/bin/python
|
|
# vim:set et sw=4:
|
|
#
|
|
# certdata2pem.py - splits certdata.txt into multiple files
|
|
#
|
|
# Copyright (C) 2009 Philipp Kern <pkern@debian.org>
|
|
# Copyright (C) 2013 Kai Engert <kaie@redhat.com>
|
|
#
|
|
# This program is free software; you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation; either version 2 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program; if not, write to the Free Software
|
|
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301,
|
|
# USA.
|
|
|
|
import base64
|
|
import os.path
|
|
import re
|
|
import sys
|
|
import textwrap
|
|
import subprocess
|
|
import getopt
|
|
import asn1
|
|
from cryptography import x509
|
|
from cryptography.hazmat.primitives import hashes
|
|
|
|
objects = []
|
|
|
|
pemcerts = []
|
|
|
|
certdata='./certdata.txt'
|
|
pem='./cert.pem'
|
|
output='./certdata_out.txt'
|
|
trust='CKA_TRUST_CODE_SIGNING'
|
|
merge_label="Non-Mozilla Object Signing Only Certificate"
|
|
|
|
trust_types = {
|
|
"CKA_TRUST_SERVER_AUTH",
|
|
"CKA_TRUST_EMAIL_PROTECTION",
|
|
"CKA_TRUST_CODE_SIGNING"
|
|
}
|
|
|
|
attribute_types = {
|
|
"CKA_CLASS" : "CK_OBJECT_CLASS",
|
|
"CKA_TOKEN" : "CK_BBOOL",
|
|
"CKA_PRIVATE" : "CK_BBOOL",
|
|
"CKA_MODIFIABLE" : "CK_BBOOL",
|
|
"CKA_LABEL" : "UTF8",
|
|
"CKA_CERTIFICATE_TYPE" : "CK_CERTIFICATE_TYPE",
|
|
"CKA_SUBJECT" : "MULTILINE_OCTAL",
|
|
"CKA_ID" : "UTF8",
|
|
"CKA_CERT_SHA1_HASH" : "MULTILINE_OCTAL",
|
|
"CKA_CERT_MD5_HASH" : "MULTILINE_OCTAL",
|
|
"CKA_ISSUER" : "MULTILINE_OCTAL",
|
|
"CKA_SERIAL_NUMBER" : "MULTILINE_OCTAL",
|
|
"CKA_VALUE" : "MULTILINE_OCTAL",
|
|
"CKA_NSS_MOZILLA_CA_POLICY" : "CK_BBOOL",
|
|
"CKA_NSS_SERVER_DISTRUST_AFTER" : "Distrust",
|
|
"CKA_NSS_EMAIL_DISTRUST_AFTER" : "Distrust",
|
|
"CKA_TRUST_SERVER_AUTH" : "CK_TRUST",
|
|
"CKA_TRUST_EMAIL_PROTECTION" : "CK_TRUST",
|
|
"CKA_TRUST_CODE_SIGNING" : "CK_TRUST",
|
|
"CKA_TRUST_STEP_UP_APPROVED" : "CK_BBOOL"
|
|
}
|
|
|
|
def printable_serial(obj):
|
|
return ".".join([str(x) for x in obj['CKA_SERIAL_NUMBER']])
|
|
|
|
def getSerial(cert):
|
|
encoder = asn1.Encoder()
|
|
encoder.start()
|
|
encoder.write(cert.serial_number)
|
|
return encoder.output()
|
|
|
|
def dumpOctal(f,value):
|
|
for i in range(len(value)) :
|
|
if i % 16 == 0 :
|
|
f.write("\n")
|
|
f.write("\\%03o"%int.from_bytes(value[i:i+1],sys.byteorder))
|
|
f.write("\nEND\n")
|
|
|
|
# in python 3.8 this can be replaces with return byteval.hex(':',1)
|
|
def formatHex(byteval) :
|
|
string=byteval.hex()
|
|
string_out=""
|
|
for i in range(0,len(string)-2,2) :
|
|
string_out += string[i:i+2] + ':'
|
|
string_out += string[-2:]
|
|
return string_out
|
|
|
|
try:
|
|
opts, args = getopt.getopt(sys.argv[1:],"c:o:p:t:l:",)
|
|
except getopt.GetoptError as err:
|
|
print(err)
|
|
print(sys.argv[0] + ' [-c certdata] [-p pem] [-o certdata_target] [-t trustvalue] [-l merge_label]')
|
|
print('-c certdata certdata file to merge to (default="'+certdata+'")');
|
|
print('-p pem pem file with CAs to merge from (default="'+pem+'")');
|
|
print('-o certdata_target resulting output file (default="'+output+'")');
|
|
print('-t trustvalue what these CAs are trusted for (default="'+trust+'")');
|
|
print('-l merge_label what label CAs that aren\'t in certdata (default="'+merge_label+'")');
|
|
sys.exit(2)
|
|
|
|
for opt, arg in opts:
|
|
if opt == '-c' :
|
|
certdata = arg
|
|
elif opt == '-p' :
|
|
pem = arg
|
|
elif opt == '-o' :
|
|
output = arg
|
|
elif opt == '-t' :
|
|
trust = arg
|
|
elif opt == '-l' :
|
|
merge_label = arg
|
|
|
|
# read the pem file
|
|
in_cert, certvalue = False, ""
|
|
for line in open(pem, 'r'):
|
|
if not in_cert:
|
|
if line.find("BEGIN CERTIFICATE") != -1:
|
|
in_cert = True;
|
|
continue
|
|
# Ignore comment lines and blank lines.
|
|
if line.startswith('#'):
|
|
continue
|
|
if len(line.strip()) == 0:
|
|
continue
|
|
if line.find("END CERTIFICATE") != -1 :
|
|
pemcerts.append(certvalue);
|
|
certvalue = "";
|
|
in_cert = False;
|
|
continue
|
|
certvalue += line;
|
|
|
|
|
|
# read the certdata.txt file
|
|
in_data, in_multiline, in_obj = False, False, False
|
|
field, ftype, value, binval, obj = None, None, None, bytearray(), dict()
|
|
header, comment = "", ""
|
|
for line in open(certdata, 'r'):
|
|
# Ignore the file header.
|
|
if not in_data:
|
|
header += line
|
|
if line.startswith('BEGINDATA'):
|
|
in_data = True
|
|
continue
|
|
# Ignore comment lines.
|
|
if line.startswith('#'):
|
|
comment += line
|
|
continue
|
|
|
|
# Empty lines are significant if we are inside an object.
|
|
if in_obj and len(line.strip()) == 0:
|
|
# collect all the inline comments in this object
|
|
obj['Comment'] += comment
|
|
comment = ""
|
|
objects.append(obj)
|
|
obj = dict()
|
|
in_obj = False
|
|
continue
|
|
if len(line.strip()) == 0:
|
|
continue
|
|
if in_multiline:
|
|
if not line.startswith('END'):
|
|
if ftype == 'MULTILINE_OCTAL':
|
|
line = line.strip()
|
|
for i in re.finditer(r'\\([0-3][0-7][0-7])', line):
|
|
integ = int(i.group(1), 8)
|
|
binval.extend((integ).to_bytes(1, sys.byteorder))
|
|
obj[field] = binval
|
|
else:
|
|
value += line
|
|
obj[field] = value
|
|
continue
|
|
in_multiline = False
|
|
continue
|
|
if line.startswith('CKA_CLASS'):
|
|
in_obj = True
|
|
obj['Comment'] = comment
|
|
comment = ""
|
|
line_parts = line.strip().split(' ', 2)
|
|
if len(line_parts) > 2:
|
|
field, ftype = line_parts[0:2]
|
|
value = ' '.join(line_parts[2:])
|
|
elif len(line_parts) == 2:
|
|
field, ftype = line_parts
|
|
value = None
|
|
else:
|
|
raise NotImplementedError('line_parts < 2 not supported.\n' + line)
|
|
if ftype == 'MULTILINE_OCTAL':
|
|
in_multiline = True
|
|
value = ""
|
|
binval = bytearray()
|
|
continue
|
|
obj[field] = value
|
|
if len(list(obj.items())) > 0:
|
|
objects.append(obj)
|
|
|
|
# now merge the results
|
|
for certval in pemcerts:
|
|
certder = base64.b64decode(certval)
|
|
cert = x509.load_der_x509_certificate(certder)
|
|
certhashsha1 = cert.fingerprint(hashes.SHA1())
|
|
certhashmd5 = cert.fingerprint(hashes.MD5())
|
|
try:
|
|
label=cert.subject.get_attributes_for_oid(x509.oid.NameOID.COMMON_NAME)[0].value
|
|
except:
|
|
try:
|
|
label=cert.subject.get_attributes_for_oid(x509.oid.NameOID.ORGANIZATION_UNIT_NAME)[0].value
|
|
except:
|
|
try:
|
|
label=cert.subject.get_attributes_for_oid(x509.oid.NameOID.ORGANIZATION_NAME)[0].value
|
|
except:
|
|
label="Unknown Certificate"
|
|
|
|
|
|
found = False
|
|
# see if it exists in certdata.txt
|
|
for obj in objects:
|
|
# we only need to check the trust objects, because
|
|
# that is the object we would modify if it exists
|
|
if obj['CKA_CLASS'] != 'CKO_NSS_TRUST':
|
|
continue
|
|
# explicitly distrusted certs don't have a hash value
|
|
if not 'CKA_CERT_SHA1_HASH' in obj:
|
|
continue
|
|
if obj['CKA_CERT_SHA1_HASH'] != certhashsha1:
|
|
continue
|
|
obj[trust] = 'CKT_NSS_TRUSTED_DELEGATOR'
|
|
found = True
|
|
print('Found "'+label+'"');
|
|
break
|
|
if found :
|
|
continue
|
|
# append this certificate
|
|
obj=dict()
|
|
time='%a %b %d %H:%M:%S %Y'
|
|
comment = '# ' + merge_label + '\n# %s "'+label+'"\n'
|
|
comment += '# Issuer: ' + cert.issuer.rfc4514_string() + '\n'
|
|
comment += '# Serial Number:'
|
|
sn=cert.serial_number
|
|
if sn < 0x100000:
|
|
comment += ' %d (0x%x)\n'%(sn,sn)
|
|
else:
|
|
comment += formatHex(sn.to_bytes((sn.bit_length()+7)//8,"big")) + '\n'
|
|
comment += '# Subject: ' + cert.subject.rfc4514_string() + '\n'
|
|
comment += '# Not Valid Before: ' + cert.not_valid_before.strftime(time) + '\n'
|
|
comment += '# Not Valid After: ' + cert.not_valid_after.strftime(time) + '\n'
|
|
comment += '# Fingerprint (MD5): ' + formatHex(certhashmd5) + '\n'
|
|
comment += '# Fingerprint (SHA1): ' + formatHex(certhashsha1) + '\n'
|
|
obj['Comment']= comment%"Certificate"
|
|
obj['CKA_CLASS'] = 'CKO_CERTIFICATE'
|
|
obj['CKA_TOKEN'] = 'CK_TRUE'
|
|
obj['CKA_PRIVATE'] = 'CK_FALSE'
|
|
obj['CKA_MODIFIABLE'] = 'CK_FALSE'
|
|
obj['CKA_LABEL'] = '"' + label + '"'
|
|
obj['CKA_CERTIFICATE_TYPE'] = 'CKC_X_509'
|
|
obj['CKA_SUBJECT'] = cert.subject.public_bytes()
|
|
obj['CKA_ID'] = '"0"'
|
|
obj['CKA_ISSUER'] = cert.issuer.public_bytes()
|
|
obj['CKA_SERIAL_NUMBER'] = getSerial(cert)
|
|
obj['CKA_VALUE'] = certder
|
|
obj['CKA_NSS_MOZILLA_CA_POLICY'] = 'CK_FALSE'
|
|
obj['CKA_NSS_SERVER_DISTRUST_AFTER'] = 'CK_FALSE'
|
|
obj['CKA_NSS_EMAIL_DISTRUST_AFTER'] = 'CK_FALSE'
|
|
objects.append(obj)
|
|
|
|
# append the trust values
|
|
obj=dict()
|
|
obj['Comment']= comment%"Trust for"
|
|
obj['CKA_CLASS'] = 'CKO_TRUST'
|
|
obj['CKA_TOKEN'] = 'CK_TRUE'
|
|
obj['CKA_PRIVATE'] = 'CK_FALSE'
|
|
obj['CKA_MODIFIABLE'] = 'CK_FALSE'
|
|
obj['CKA_LABEL'] = '"' + label + '"'
|
|
obj['CKA_CERT_SHA1_HASH'] = certhashsha1
|
|
obj['CKA_CERT_MD5_HASH'] = certhashmd5
|
|
obj['CKA_ISSUER'] = cert.issuer.public_bytes()
|
|
obj['CKA_SERIAL_NUMBER'] = getSerial(cert)
|
|
for t in list(trust_types):
|
|
if t == trust:
|
|
obj[t] = 'CKT_NSS_TRUSTED_DELEGATOR'
|
|
else:
|
|
obj[t] = 'CKT_NSS_MUST_VERIFY_TRUST'
|
|
obj['CKA_TRUST_STEP_UP_APPROVED'] = 'CK_FALSE'
|
|
objects.append(obj)
|
|
print('Added "'+label+'"');
|
|
|
|
# now dump the results
|
|
f = open(output, 'w')
|
|
f.write(header)
|
|
for obj in objects:
|
|
if 'Comment' in obj:
|
|
f.write(obj['Comment'])
|
|
else:
|
|
print("Object with no comment!!")
|
|
print(obj)
|
|
for field in list(attribute_types.keys()):
|
|
if not field in obj:
|
|
continue
|
|
ftype = attribute_types[field];
|
|
if ftype == 'Distrust':
|
|
if obj[field] == 'CK_FALSE':
|
|
ftype = 'CK_BBOOL'
|
|
else:
|
|
ftype = 'MULTILINE_OCTAL'
|
|
f.write("%s %s"%(field,ftype));
|
|
if ftype == 'MULTILINE_OCTAL':
|
|
dumpOctal(f,obj[field])
|
|
else:
|
|
f.write(" %s\n"%obj[field])
|
|
f.write("\n")
|
|
f.close
|