jaraco.httplib2 / certs.py

#!/usr/bin/env python
#
#  Parse Mozilla's certdata.txt and extract CA Root Certificates into PEM
#  format.
#
#  certdata.txt can be found in Mozilla's source tree:
#  /mozilla/security/nss/lib/ckfw/builtins/certdata.txt
#
# This script is based loosely on the bash/ruby script found at
#  http://curl.haxx.se/docs/parse-certs.txt

import re
import os
import urllib2
import itertools
import io
import textwrap

target = 'python2/httplib2/cacerts'
cert_source_url = ('http://mxr.mozilla.org/seamonkey/source/'
	'security/nss/lib/ckfw/builtins/certdata.txt?raw=1')

def get_certdata():
	return urllib2.urlopen(cert_source_url)

comment_pattern = re.compile('^\s*#')
def is_comment(line):
	return comment_pattern.match(line)

def get_cert_sections(lines):
	"""
	The file is broken into sections separated by dual newlines.
	"""
	data = ''.join(lines)
	return data.split('\n\n')

def get_certs(stream):
	"""
	Given a stream of data (such as from an open file or urlopen), pick
	out the certificates as Cert objects.
	"""
	sections = get_cert_sections(itertools.ifilterfalse(is_comment, stream))
	pattern = re.compile(
		r'CKA_LABEL (?P<encoding>\w+) "(?P<label>.*?)"$' # the label
		'.*?' # other stuff
		r'CKA_VALUE MULTILINE_OCTAL$(?P<value>.+)END',
		flags = re.MULTILINE | re.DOTALL,
	)
	for section in sections:
		matchobj = pattern.search(section)
		if matchobj:
			yield Cert(**matchobj.groupdict())

def oct_repl(matchobj):
	val = eval('0'+matchobj.group(1))
	return chr(val)

class Cert(object):
	def __init__(self, label, value, **kwargs):
		self.label = label
		value = value.replace('\n', '')
		value = re.sub(r'\\(\d+)', oct_repl, value)
		self.value = value

	def __str__(self):
		lines = ["-----BEGIN CERTIFICATE-----\n"]
		content = self.value.encode('base-64')
		lines.append(content)
		lines.append(lines[0].replace('BEGIN', 'END'))
		return ''.join(lines)

def gen_certs():
	# save each cert to a separate file in target
	certs = get_certs(get_certdata())
	if not os.path.exists(target):
		os.path.makedirs(target)
	for cert in certs:
		filename = cert.label + '.cer'
		filename = filename.replace('/', '-')
		with open(os.path.join(target, filename), 'w') as file:
			file.write(str(cert))

def gen_pem():
	# save all certs to a single file
	certs = get_certs(get_certdata())
	outstream = io.BytesIO()
	for cert in certs:
		outstream.write(cert.label + '\n')
		outstream.write('='*len(cert.label) + '\n\n')
		outstream.write(str(cert))
		outstream.write('\n')
	with open('python2/httplib2/cacerts.txt', 'wb') as f:
		f.write(header)
		f.write('\n')
		f.write(outstream.getvalue())

def as_comment(blob):
	"""
	Take a chunk of text and prepend comments.
	"""
	lines = io.StringIO(blob)
	return ''.join('# ' + line for line in lines)

header = as_comment(textwrap.dedent(u"""
	Certifcate Authority certificates for validating SSL connections.

	This file contains PEM format certificates generated from
	{cert_source_url}

	***** BEGIN LICENSE BLOCK *****
	Version: MPL 1.1/GPL 2.0/LGPL 2.1

	The contents of this file are subject to the Mozilla Public License Version
	1.1 (the "License"); you may not use this file except in compliance with
	the License. You may obtain a copy of the License at
	http://www.mozilla.org/MPL/

	Software distributed under the License is distributed on an "AS IS" basis,
	WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
	for the specific language governing rights and limitations under the
	License.

	The Original Code is the Netscape security libraries.

	The Initial Developer of the Original Code is
	Netscape Communications Corporation.
	Portions created by the Initial Developer are Copyright (C) 1994-2000
	the Initial Developer. All Rights Reserved.

	Contributor(s):

	Alternatively, the contents of this file may be used under the terms of
	either the GNU General Public License Version 2 or later (the "GPL"), or
	the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
	in which case the provisions of the GPL or the LGPL are applicable instead
	of those above. If you wish to allow use of your version of this file only
	under the terms of either the GPL or the LGPL, and not to allow others to
	use your version of this file under the terms of the MPL, indicate your
	decision by deleting the provisions above and replace them with the notice
	and other provisions required by the GPL or the LGPL. If you do not delete
	the provisions above, a recipient may use your version of this file under
	the terms of any one of the MPL, the GPL or the LGPL.

	***** END LICENSE BLOCK *****
	""").format(**vars()).lstrip())

if __name__ == '__main__':
	gen_pem()
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.