Skip to content

Instantly share code, notes, and snippets.

@rraptorr
Last active April 3, 2023 10:19
Show Gist options
  • Save rraptorr/2efaaf21caaf6574e8ff to your computer and use it in GitHub Desktop.
Save rraptorr/2efaaf21caaf6574e8ff 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
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)
@JoeLemaire
Copy link

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
Copy link
Author

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

@purnima-vb
Copy link

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
Copy link
Author

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