Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Simple Certificate Transparency certificate submission client
#!/usr/bin/python
import sys
import argparse, json, base64, struct
import urllib2
from datetime import datetime
LOGS = {
'icarus': 'https://ct.googleapis.com/icarus',
'pilot': 'https://ct.googleapis.com/pilot',
'rocketeer': 'https://ct.googleapis.com/rocketeer',
'skydiver': 'https://ct.googleapis.com/skydiver',
'digicert1': 'https://ct1.digicert-ct.com/log',
'digicert2': 'https://ct2.digicert-ct.com/log',
'symantec': 'https://ct.ws.symantec.com',
'vega': 'https://vega.ws.symantec.com',
'sirius': 'https://sirius.ws.symantec.com',
'sabre': 'https://sabre.ct.comodo.com',
'mammoth': 'https://mammoth.ct.comodo.com',
'cnnic': 'https://ctserver.cnnic.cn',
}
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)
@pierky

This comment has been minimized.

Copy link

commented Nov 3, 2015

@isbasex

This comment has been minimized.

Copy link

commented Oct 1, 2016

It is time to remove wosign

@JoeLemaire

This comment has been minimized.

Copy link

commented Mar 1, 2018

When I run this script, I receive this error:

[user@RP /etc/haproxy]# ./ct-submit.py ssl/domain_com.pem -o ssl/domain_com.pem.sctl
Traceback (most recent call last):
File "./ct-submit.py", line 54, in
cert.append(line)
NameError: name 'cert' is not defined

Any thoughts?

@rraptorr

This comment has been minimized.

Copy link
Owner Author

commented Mar 11, 2018

It seems that domain_com.pem file does not contain any line with "-----BEGIN CERTIFICATE-----".

@purnima-vb

This comment has been minimized.

Copy link

commented Sep 18, 2018

This script gives good info. Thanks.
I am trying to generate a cert having SCT embedded. Using this script I tried to create SCT but it results in below errors :

  1. Passing this hexdump (sct.encode('hex') errors to Incorrect Property Value for certificate extension with id : 2 and OID
  2. When tried to do it using GUI, org.ejbca.core.model.ca.certextensions.CertificateExtentionConfigurationException: Illegal value of certificate extension with id : 2 and OID : 1.3.6.1.4.1.11129.2.4.2"

Any help is much appreaciated.

@rraptorr

This comment has been minimized.

Copy link
Owner Author

commented Nov 16, 2018

EJBCA is a commercial software which I have no access to. You would probably be better by contacting their support.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.