Skip to content

Instantly share code, notes, and snippets.

@davidedg
Forked from rraptorr/ct-submit.py
Last active March 30, 2018 23:54
Show Gist options
  • Save davidedg/cae8a5de94f281dc3c04fa8f97503f3e to your computer and use it in GitHub Desktop.
Save davidedg/cae8a5de94f281dc3c04fa8f97503f3e to your computer and use it in GitHub Desktop.
Simple Certificate Transparency certificate submission client
#!/usr/bin/python
import sys
import argparse, json, base64, struct
import urllib2
from datetime import datetime
## https://crt.sh/monitored-logs
LOGS = {
'rocketeer': 'https://ct.googleapis.com/rocketeer',
'nimbus2018': 'https://ct.cloudflare.com/logs/nimbus2018',
'sabre': 'https://sabre.ct.comodo.com',
'mammoth': 'https://mammoth.ct.comodo.com',
}
parser = argparse.ArgumentParser(description='Certificate Transparency submission client')
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 (compatible with haproxy)')
parser.add_argument('-O', dest='output_dir', help='output individual SCTs to a directory (compatible with nginx-ct module)')
args = parser.parse_args()
chain = []
cert = None
for pem in args.pem:
for line in pem.readlines():
line = line.strip()
if len(line) == 0:
continue
if line == '-----BEGIN CERTIFICATE-----':
cert = []
elif line == '-----END CERTIFICATE-----':
b64 = ''.join(cert)
chain.append(b64)
cert = None
elif cert != None:
cert.append(line)
if len(chain) == 0:
print("no certificates found")
sys.exit(1)
jsonRequest = json.dumps({'chain': chain})
scts = []
for log in sorted(LOGS.iterkeys()):
print("sending request to %s" % LOGS[log])
request = urllib2.Request(url = LOGS[log] + '/ct/v1/add-chain', data=jsonRequest)
request.add_header('Content-Type', 'application/json')
try:
response = urllib2.urlopen(request)
jsonResponse = response.read()
except urllib2.HTTPError as e:
if e.code >= 400 and e.code < 500:
print(" unable to submit certificate to log, HTTP error %d %s: %s" % (e.code, e.reason, e.read()))
else:
print(" unable to submit certificate to log, HTTP error %d %s" % (e.code, e.reason))
continue
except urllib2.URLError as e:
print(" unable to submit certificate to log, error %s" % e.reason)
continue
sct = json.loads(jsonResponse)
print(" version: %d" % sct['sct_version'])
print(" log ID: %s" % sct['id'])
print(" timestamp: %d (%s)" % (sct['timestamp'], datetime.fromtimestamp(sct['timestamp'] / 1000)))
print(" extensions: %s" % sct['extensions'])
print(" 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((log, sct))
print(" SCT (%d bytes): %s" % (len(sct), base64.b64encode(sct)))
if args.output:
size = 0
for log, sct in scts:
size += 2 + len(sct)
args.output.write(struct.pack('>H', size))
for log, sct in scts:
args.output.write(struct.pack('>H '+str(len(sct))+'s', len(sct), sct))
args.output.close()
if args.output_dir:
for log, sct in scts:
with open(args.output_dir + '/' + log + '.sct', 'w') as f:
f.write(sct)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment