Skip to content

Instantly share code, notes, and snippets.

@jtl999
Forked from rraptorr/ct-submit.py
Last active May 28, 2016 08:17
Show Gist options
  • Save jtl999/0be7102d189636963f1da1919def45ec to your computer and use it in GitHub Desktop.
Save jtl999/0be7102d189636963f1da1919def45ec to your computer and use it in GitHub Desktop.
Simple Certificate Transparency certificate submission client
#!/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