-
-
Save jtl999/0be7102d189636963f1da1919def45ec to your computer and use it in GitHub Desktop.
Simple Certificate Transparency certificate submission client
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python | |
# coding: utf-8 | |
import sys | |
import os | |
import argparse, json, base64, struct | |
import urllib2 | |
from datetime import datetime | |
import re | |
# updated 2016-05-28 | |
LOGS = [ | |
'https://ct.googleapis.com/aviator', | |
'https://ct.googleapis.com/pilot', | |
'https://ct.googleapis.com/rocketeer', | |
'https://ct.googleapis.com/submariner', | |
'https://ct1.digicert-ct.com/log', # only accepts certificates issued by some CAs | |
'https://ct.izenpe.com', # only accepts certificates issued by some CAs | |
'https://ct.ws.symantec.com', | |
'https://ctlog.api.venafi.com', | |
'https://vega.ws.symantec.com', | |
'https://plausible.ct.nordu.net', | |
'https://ctlog.wosign.com', | |
'https://ctserver.cnnic.cn', | |
'https://ct.gdca.com.cn', | |
'https://ct.startssl.com', | |
'https://ct.izenpe.eus', | |
'https://deneb.ws.symantec.com', | |
] | |
parser = argparse.ArgumentParser( | |
description='Certificate Transparency certificate submission client.', | |
epilog='Please note that some logs will be accepted only certificates issued by some CAs. All received SCTs are not verified.') | |
parser.add_argument('pem', | |
type=argparse.FileType('r'), | |
help='PEM files forming a certificate chain (with or without root)', | |
nargs='+') | |
parser.add_argument('-o', | |
dest='output', | |
type=argparse.FileType('w'), | |
help='output raw TLS extension data with all the SCTs') | |
parser.add_argument('-l', | |
dest='ctLogServer', | |
action='append', | |
type=str, | |
help='CT log sever name') | |
parser.add_argument('-j', | |
dest='saveJson', | |
action='store_true', | |
help='save response of CT log server as JSON text format') | |
parser.add_argument('-q', | |
dest='quiet', | |
action='store_true', | |
help='not show any messages') | |
parser.add_argument('-d', | |
dest='outdir', | |
action='store', | |
type=str, | |
help='output directory SCTs are saved individual files per SCT') | |
def printInfo(message): | |
if args.quiet: | |
return | |
print message | |
def writeFile(filename, data): | |
try: | |
f = open(filename, "w") | |
except Exception as e: | |
print "Error: Can't open file=%s: %s" %(filename, str(e.args)) | |
return -1 | |
try: | |
f.write(data) | |
except Exception as e: | |
print "Error: Can't write file=%s: %s" %(filename, str(e.args)) | |
return -1 | |
try: | |
f.close() | |
except Exception as e: | |
print "Error: Can't close file=%s: %s" %(filename, str(e.args)) | |
return -1 | |
return 0 | |
#### | |
args = parser.parse_args() | |
if args.ctLogServer: | |
LOGS = args.ctLogServer | |
if args.outdir: | |
if not os.path.isdir(args.outdir): | |
print "Error: outdir is not directory: %s" % args.outdir | |
sys.exit(1) | |
chain = [] | |
for pem in args.pem: | |
for line in pem.readlines(): | |
line = line.strip() | |
if len(line) == 0: | |
continue | |
if line == '-----BEGIN CERTIFICATE-----': | |
cert = [] | |
continue | |
elif line == '-----END CERTIFICATE-----': | |
b64 = ''.join(cert) | |
chain.append(b64) | |
continue | |
else: | |
cert.append(line) | |
jsonRequest = json.dumps({'chain': chain}) | |
scts = [] | |
errors = 0 | |
for log in LOGS: | |
printInfo("Request to %s" % log) | |
if args.outdir: | |
fileBaseName = "%s/%s" %(args.outdir, re.sub(r'^.*?://', '', log).replace('/', '_')) | |
printInfo(" File: %s" %(fileBaseName)) | |
try: | |
request = urllib2.Request(url = log + '/ct/v1/add-chain', data=jsonRequest) | |
request.add_header('Content-Type', 'application/json') | |
response = urllib2.urlopen(request) | |
jsonResponse = response.read() | |
except Exception as e: | |
print " unable to submit certificate to log %s, Error: %s" % (log, str(e)) | |
errors += 1 | |
continue | |
if args.outdir and args.saveJson: | |
writeFile(fileBaseName + ".json", jsonResponse); | |
printInfo(" Saved JSON: %s" %(fileBaseName + ".json")) | |
sct = json.loads(jsonResponse) | |
printInfo(" version: %d" % sct['sct_version']) | |
printInfo(" log ID: %s" % sct['id']) | |
printInfo(" timestamp: %d (%s)" % (sct['timestamp'], datetime.fromtimestamp(sct['timestamp'] / 1000))) | |
printInfo(" extensions: %s" % sct['extensions']) | |
printInfo(" signature: %s" % sct['signature']) | |
logId = base64.b64decode(sct['id']) | |
timestamp = sct['timestamp'] | |
extensions = base64.b64decode(sct['extensions']) | |
signature = base64.b64decode(sct['signature']) | |
sct = struct.pack('> B 32s Q H '+str(len(extensions))+'s '+str(len(signature))+'s', 0, logId, timestamp, len(extensions), extensions, signature) | |
scts.append(sct) | |
printInfo(" SCT (%d bytes): %s" % (len(sct), base64.b64encode(sct))) | |
if args.outdir: | |
if writeFile(fileBaseName + ".sct", sct): | |
errors += 1 | |
if args.output: | |
size = 0 | |
for sct in scts: | |
size += 2 + len(sct) | |
args.output.write(struct.pack('>H', size)) | |
for sct in scts: | |
args.output.write(struct.pack('>H '+str(len(sct))+'s', len(sct), sct)) | |
args.output.close() | |
sys.exit(errors) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment