Last active
November 26, 2024 08:13
-
-
Save rraptorr/2efaaf21caaf6574e8ff to your computer and use it in GitHub Desktop.
Simple Certificate Transparency certificate submission client
This file contains 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 | |
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) |
It is time to remove wosign
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?
It seems that domain_com.pem file does not contain any line with "-----BEGIN CERTIFICATE-----".
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 :
- Passing this hexdump (sct.encode('hex') errors to Incorrect Property Value for certificate extension with id : 2 and OID
- 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.
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
Thanks!
Referenced on HTTPS + CT SCT TLS extension on my blog.