Skip to content
This repository has been archived by the owner on Jul 13, 2023. It is now read-only.

Commit

Permalink
feat: add a new client_certs endpoint config option
Browse files Browse the repository at this point in the history
requires whitelisted TLS client certificates for connections to the
endpoint except to health/log_check handlers

closes #498
  • Loading branch information
pjenvey committed Sep 28, 2016
1 parent 5463ecd commit 58e0cbb
Show file tree
Hide file tree
Showing 17 changed files with 580 additions and 38 deletions.
4 changes: 3 additions & 1 deletion .coveragerc
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
[report]
omit = *noseplugin*
omit =
*noseplugin*
autopush/tests/certs/makecerts.py
show_missing = true
32 changes: 32 additions & 0 deletions autopush/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,35 @@ def write_error(self, code, **kwargs):
self.log.failure("Error in handler: %s" % code,
client_info=self._client_info)
self.finish()

def authenticate_peer_cert(self):
"""Authenticate the client per the configured client_certs.
Aborts the request w/ a 401 on failure.
"""
cert = self.request.connection.transport.getPeerCertificate()
# VERIFY_FAIL_IF_NO_PEER_CERT ensures this never fails
# otherwise something is very broken
assert cert, "Expected a TLS peer cert (VERIFY_FAIL_IF_NO_PEER_CERT)"

cert_signature = cert.digest('sha256')
cn = cert.get_subject().CN
auth = self.ap_settings.client_certs.get(cert_signature)
if auth is not None:
# TLS authenticated
self._client_info['tls_auth'] = auth
self._client_info['tls_auth_sha256'] = cert_signature
self._client_info['tls_auth_cn'] = cn
return

self.log.warn("Failed TLS auth",
tls_failed_sha256=cert_signature,
tls_failed_cn=cn,
client_info=self._client_info)
self.set_status(401)
# "Transport mode" isn't standard, inspired by:
# http://www6.ietf.org/mail-archive/web/tls/current/msg05589.html
self.set_header('WWW-Authenticate',
'Transport mode="tls-client-certificate"')
self.finish()
2 changes: 2 additions & 0 deletions autopush/endpoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,8 @@ def initialize(self, ap_settings):

def prepare(self):
"""Common request preparation"""
if self.ap_settings.enable_tls_auth:
self.authenticate_peer_cert()
if self.ap_settings.cors:
self.set_header("Access-Control-Allow-Origin", "*")
self.set_header("Access-Control-Allow-Methods",
Expand Down
4 changes: 4 additions & 0 deletions autopush/log_check.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ def initialize(self, ap_settings):
self.ap_settings = ap_settings
self._client_info = self._init_info()

def authenticate_peer_cert(self):
"""LogCheck skips authentication checks"""
pass

@cyclone.web.asynchronous
def get(self, err_type=None):
"""HTTP GET
Expand Down
60 changes: 46 additions & 14 deletions autopush/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ def add_shared_args(parser):
default="", env_var="SSL_CERT")
parser.add_argument('--ssl_dh_param',
help="SSL DH Param file (openssl dhparam 1024)",
type=str, default="", env_var="SSL_DH_PARAM")
type=str, default=None, env_var="SSL_DH_PARAM")
parser.add_argument('--router_tablename', help="DynamoDB Router Tablename",
type=str, default="router", env_var="ROUTER_TABLENAME")
parser.add_argument('--storage_tablename',
Expand Down Expand Up @@ -297,6 +297,9 @@ def _parse_endpoint(sysargs, use_files=True):
parser.add_argument('--auth_key', help='Bearer Token source key',
type=str, default=[], env_var='AUTH_KEY',
action="append")
parser.add_argument('--client_certs',
help="Allowed TLS client certificates",
type=str, env_var='CLIENT_CERTS', default="{}")

add_shared_args(parser)

Expand Down Expand Up @@ -342,6 +345,34 @@ def make_settings(args, **kwargs):
"max_data": args.max_data,
"collapsekey": args.gcm_collapsekey,
"senderIDs": sender_ids}

client_certs = None
# endpoint only
if getattr(args, 'client_certs', None):
try:
client_certs_arg = json.loads(args.client_certs)
except (ValueError, TypeError):
log.critical(format="Invalid JSON specified for client_certs")
return
if client_certs_arg:
if not args.ssl_key:
log.critical(format="client_certs specified without SSL "
"enabled (no ssl_key specified)")
return
client_certs = {}
for name, sigs in client_certs_arg.iteritems():
if not isinstance(sigs, list):
log.critical(
format="Invalid JSON specified for client_certs")
return
for sig in sigs:
sig = sig.upper()
if (not name or not utils.CLIENT_SHA256_RE.match(sig) or
sig in client_certs):
log.critical(format="Invalid client_certs argument")
return
client_certs[sig] = name

if args.fcm_enabled:
# Create a common gcmclient
if not args.fcm_auth:
Expand Down Expand Up @@ -383,6 +414,7 @@ def make_settings(args, **kwargs):
resolve_hostname=args.resolve_hostname,
wake_timeout=args.wake_timeout,
ami_id=ami_id,
client_certs=client_certs,
**kwargs
)

Expand Down Expand Up @@ -472,21 +504,20 @@ def connection_main(sysargs=None, use_files=True):

# Start the WebSocket listener.
if args.ssl_key:
context_factory = AutopushSSLContextFactory(args.ssl_key,
args.ssl_cert)
if args.ssl_dh_param:
context_factory.getContext().load_tmp_dh(args.ssl_dh_param)

context_factory = AutopushSSLContextFactory(
args.ssl_key,
args.ssl_cert,
dh_file=args.ssl_dh_param)
reactor.listenSSL(args.port, site_factory, context_factory)
else:
reactor.listenTCP(args.port, site_factory)

# Start the internal routing listener.
if args.router_ssl_key:
context_factory = AutopushSSLContextFactory(args.router_ssl_key,
args.router_ssl_cert)
if args.ssl_dh_param:
context_factory.getContext().load_tmp_dh(args.ssl_dh_param)
context_factory = AutopushSSLContextFactory(
args.router_ssl_key,
args.router_ssl_cert,
dh_file=args.ssl_dh_param)
reactor.listenSSL(args.router_port, site, context_factory)
else:
reactor.listenTCP(args.router_port, site)
Expand Down Expand Up @@ -558,10 +589,11 @@ def endpoint_main(sysargs=None, use_files=True):

# start the senderIDs refresh timer
if args.ssl_key:
context_factory = AutopushSSLContextFactory(args.ssl_key,
args.ssl_cert)
if args.ssl_dh_param:
context_factory.getContext().load_tmp_dh(args.ssl_dh_param)
context_factory = AutopushSSLContextFactory(
args.ssl_key,
args.ssl_cert,
dh_file=args.ssl_dh_param,
require_peer_certs=settings.enable_tls_auth)
reactor.listenSSL(args.port, site, context_factory)
else:
reactor.listenTCP(args.port, site)
Expand Down
3 changes: 3 additions & 0 deletions autopush/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ def __init__(self,
bear_hash_key=None,
preflight_uaid="deadbeef00000000deadbeef000000000",
ami_id=None,
client_certs=None,
):
"""Initialize the Settings object
Expand Down Expand Up @@ -153,6 +154,8 @@ def __init__(self,
self.endpoint_hostname,
endpoint_port
)
self.enable_tls_auth = client_certs is not None
self.client_certs = client_certs

# Database objects
self.router_table = get_router_table(router_tablename,
Expand Down
23 changes: 23 additions & 0 deletions autopush/ssl.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@

class AutopushSSLContextFactory(ssl.DefaultOpenSSLContextFactory):
"""A SSL context factory"""

def __init__(self, *args, **kwargs):
self.dh_file = kwargs.pop('dh_file', None)
self.require_peer_certs = kwargs.pop('require_peer_certs', False)
ssl.DefaultOpenSSLContextFactory.__init__(self, *args, **kwargs)

def cacheContext(self):
"""Setup the main context factory with custom SSL settings"""
if self._context is None:
Expand All @@ -54,4 +60,21 @@ def cacheContext(self):
ctx.use_certificate_chain_file(self.certificateFileName)
ctx.use_privatekey_file(self.privateKeyFileName)

if self.dh_file:
ctx.load_tmp_dh(self.dh_file)

if self.require_peer_certs:
# Require peer certs but only for use by
# RequestHandlers
ctx.set_verify(
SSL.VERIFY_PEER |
SSL.VERIFY_FAIL_IF_NO_PEER_CERT |
SSL.VERIFY_CLIENT_ONCE,
self._allow_peer)

self._context = ctx

def _allow_peer(self, conn, cert, errno, depth, preverify_ok):
# skip verification: we only care about whitelisted signatures
# on file
return True
52 changes: 52 additions & 0 deletions autopush/tests/certs/client1.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQD04ukuoV6TrmLb
+3j9pm9bXHto65RfJ+SIFNRiIcrYlVfhlutYMi4pWZxHR0lFmt8nVxnj0V/ftMTV
L9bmuoLrsasoIcHjCxWqn/wcb9E2tyVCiN0KubJu/1+pSEJ6VKBr7sVK36a/h/xo
E3HW06HplCBGepqwSn5V+PmXA5liSxrIPDT3WclcdJvH5ZbzUVcGRu2oHiGFOVNK
b3I+yovIXCXCn47mgAkitGPREfXWp714ySkOoQgkeTeWl7tV9bGDA7kpuJibfPb+
TM8/fbGk++SzB86Gqrp2ue+pYEL/tNcg3BRrB0dQbwIaH9VjmSfmtWJOGBTEW1xx
x5JsgHidAgMBAAECggEBAIdrJ4mawOMnzxFZCboynGfIR5Jom77XH6BE7IFrsHF+
fH+KZpB6B17kZ/Besl0kXHyzxORfdwYNP7+oWc1znExcDor9x+sWyR92owLSrr30
H02gw6NXtx18aNkC1YgyXhfxjPZvoRVPTLv87LnghCvXttVinUIZn61JJjRlUB+y
1aSwJWtxh59vnu7kzkoQDp5mP18L02+8XuFv3L8V61v23n/tfY/zc5YZpMtJ3tOs
Uls2HhYurfuVE6QELkmiSPfJnm8QMr2J81ZSL0o/h4kxI7jDB3nrbiBYa3KD8nZ5
5lmS4w7XLtZ7Rkv3RmLNO62dvhQXZxQnvCisLFU6sf0CgYEA/hm/ZNH8Pv18rAIr
zf3mmYwr8dUaAgzD8BgtKgN9V09cUTd7qSFmawyqk8nktB41ZD64uu+8gwT837O2
JyRyP9/ozecWcghayHfrS+z4+i9bJIqxI6/QmiJ65EI7Ljti4JoEOm5olGIwA1lk
jZP5Tm2a7fiyGPLRZ4dqUXWesT8CgYEA9reH3gBCbdj1gQlHZjNFvCq8u5OlKRoE
P7rwSoN9OMEQFn/3H6I2NUTpiByaje9v/v8w4Rbeq4UkCXEczME3P870BiW+tgr3
ZaRbKIVZ2Qr5O5AxT+oCgoSPF9761SIKWBiRcvERoS5/cKCGB+sQICXNw8DZj4wf
v/nwCjslgyMCgYBEEHuPMxxhdx81KCO5uwBRMxX2YoHj+K1nm+JFNcgWYiC1dKpA
RL0dgbgTfGoxwUHGB3MOR/d0FRrzhT0OwRmFeKHwvazqgMhomI7DuMd8pMDCShBn
Ico7726BxCf8G1ZCGZ92U2raDG8WBpUDw5ZtZriwdASo4CotlD1rcpk+mwKBgBQP
rmOV39Dw0F2ytHSR/LylOP2Dru1dqTTJbZqRgJAp2rYJp72RwhioxtiDgunBq3iv
pXjYFDkcNWbzJKVdnLF6kYsibJR+5ckFCUiNN1YXt1ZpjijyXUvhnYUSY5ELGI47
STBwe7+AeWpeEyf3rDMA/+9H8iji+v3wQ92BG7bDAoGBAOxNWpwMc36PYJmHrgHf
FIv9GFCc4DjeZlDTF+bd5tJYjqrAK0qogHI79/8UwDv68GMOrBcpt7kxZFu/gUHp
GMi1jbx0PqHxbyXVS+GfF2q69/qPX48j8z4ASVmZ5XsnT/5xkE9x/yEsPdPZ4vkk
ATD9T6QLQvQ2ubEpm+ZJix2/
-----END PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
MIIEHDCCAwQCEA8o3mpIRk6WlCo4LCgxAOwwDQYJKoZIhvcNAQEFBQAwgb0xCzAJ
BgNVBAYTAlRSMQ8wDQYDVQQIDAbDh29ydW0xFDASBgNVBAcMC0JhxZ9tYWvDp8Sx
MRIwEAYDVQQDDAlsb2NhbGhvc3QxFTATBgNVBAoMDE1vemlsbGEgVGVzdDE2MDQG
A1UECwwtQXV0b3B1c2ggVGVzdCBhdXRvcHVzaC90ZXN0cy9jZXJ0cy9zZXJ2ZXIu
cGVtMSQwIgYJKoZIhvcNAQkBFhVvdHRvLnB1c2hAZXhhbXBsZS5jb20wIBcNMTYw
OTI3MTYzMzM1WhgPMjExNjA5MDMxNjMzMzVaMIG+MQswCQYDVQQGEwJUUjEPMA0G
A1UECAwGw4dvcnVtMRQwEgYDVQQHDAtCYcWfbWFrw6fEsTESMBAGA1UEAwwJbG9j
YWxob3N0MRUwEwYDVQQKDAxNb3ppbGxhIFRlc3QxNzA1BgNVBAsMLkF1dG9wdXNo
IFRlc3QgYXV0b3B1c2gvdGVzdHMvY2VydHMvY2xpZW50MS5wZW0xJDAiBgkqhkiG
9w0BCQEWFW90dG8ucHVzaEBleGFtcGxlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQAD
ggEPADCCAQoCggEBAPTi6S6hXpOuYtv7eP2mb1tce2jrlF8n5IgU1GIhytiVV+GW
61gyLilZnEdHSUWa3ydXGePRX9+0xNUv1ua6guuxqyghweMLFaqf/Bxv0Ta3JUKI
3Qq5sm7/X6lIQnpUoGvuxUrfpr+H/GgTcdbToemUIEZ6mrBKflX4+ZcDmWJLGsg8
NPdZyVx0m8fllvNRVwZG7ageIYU5U0pvcj7Ki8hcJcKfjuaACSK0Y9ER9danvXjJ
KQ6hCCR5N5aXu1X1sYMDuSm4mJt89v5Mzz99saT75LMHzoaquna576lgQv+01yDc
FGsHR1BvAhof1WOZJ+a1Yk4YFMRbXHHHkmyAeJ0CAwEAAaMYMBYwFAYDVR0RBA0w
C4IJbG9jYWxob3N0MA0GCSqGSIb3DQEBBQUAA4IBAQBbp23xGfgNBTcMKSA/Yku6
YAddlJGKuszFwMztQr6/9Uz+Qf6+/rrej9OkpwPE7sE7lixgeyHLysPSOZvYW610
rx3eW/E+u84+SM0FyMlM1eEL6Xr0UyZNz0odNzvygKV2jfZ35YhuME8nvJq23MFk
fI9ghZdLFk+mPgZQxmf2uavAToO6vq3afp80F4U5wTNrxQGKS9f4M202IFE6Lpkn
td5nHta5z5FuDcdPOGsZW4OhdDXbNzSxb+GAdgOlpwBliMo9Cw7lbDDi3QvjpqXM
w+ecbugf8ixAjj/30ROeHXW1Zamm1tRlX1O6wvAq2MpLEvZTVaGKdTaxQoxQPzr1
-----END CERTIFICATE-----
1 change: 1 addition & 0 deletions autopush/tests/certs/client1_sha256.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
6C:DC:75:4D:0E:D1:25:B8:F4:46:E0:FC:66:2E:03:49:EB:37:33:82:19:94:2B:CE:CF:4F:E3:E8:AA:5E:81:1A
53 changes: 53 additions & 0 deletions autopush/tests/certs/client2.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDQ25kt31KETmsU
ux1rc974Yo0OpWsj1Es6TMZ94TmNSk+eZvctB+wS4a2wh2QHuR6XYyuRgTvIapIf
zb/qyNzfcZ+TuJKnsXxu7gBXWqSMxpaZd1pc0cRllwh2GlwIC0ARl8d3O//iP+36
uIqhM1iTrd9833WDSV6it+8xDgBGgb3B+ZeenLqXJUqqhBeWh2ERZHhe3APad5UA
1vF9nSQTiRtAK8dMS+HvBugC37Yhvcgp+sFu64iDTjYi+DkOjZNVeLw9WH2XxMqR
sx34VkAZw4F8xkay5sgjydzaoP+7HxNvclQDMaq/u7pUpP9lsZmPvbsfqKGv2D5l
bkCbzWWVAgMBAAECggEBAKyrwPph1XC4/GKJSAtcIo0rvP7M18UpcIBklP3hRJmB
RE3rRpMeJ5h8qAJ4DMUt0RLL1GtZcrmBEgnlKrPLGIBLCekxAV5OqFd1wSZ3M++H
B18dg8GVU0/CDCbIKComUvO4jhoPqr+8pt1P0JzxPFvrtgchH6BI+kqA0um1b5jZ
jwkVyUc/IdCR8jz0pYrdWn5MSq3ZK+Lr7XG2ODJimmEz0dNfnJa3i3gwee7OWPPn
IpdPPDdxxTQd13y+cD20aaUcan0HEhQ6kUsHXASyAuepunOFOq4UZDU9vHnaROyR
D4DDyzgJVLN5N4LXh+o9wRvqB9k/P+hDF/A0GU34fwkCgYEA+yQTk9f103ZKM/yz
sA3ichQ9wMTUFiVwI3RY74/HWnzA9qxaSj7NX3FhdK6qiXylROAwjEW9ILNGctGT
04Xv5cRQbKe8p4i6yXG61HdXm/D1YLsJKvQd7whNLP1AXF/SMofV6E5uQbnX9PQM
MORK7aT20/vnGyTRs3jKUAJWN3sCgYEA1OYW/ZAMz+cNtNojXEM974Kfo7gQBzOq
G5i9k12QVDwj1laYyAsfJxEeFXzvLp5i12DgSy9LlTpj9JhSS8EfJBr/vG2LXDfv
HO13y6dGZ1Vp9hhiRw5iGdH4HCZpAb69Jjf27ew/vE7quKX9degjl3g7inOeexoU
LXOxwe1qwi8CgYB8RvMFM1ZryVqY9VE6KvTG/Ss97GkDeI1Qji/AhMbjCV838jxQ
B1n8BBB0/EZZ+PuT5NlBYPVhbDXNddaQUvRPIGGoEy1xPmEodIY+w7vv6EKVFplH
zzvM4K/INp6V17kd1khNSBqZncy3Y9lwjFhj10Fpz3si3IqFJJ4BD9b4ZwKBgCEC
H5RmriXZy/07SPo4DrVAymGG2y1SrFAlCVd8zTDSNjg4Ku3xE35qIADy4t6Wffqo
sX3WsmBLsk2tBC1snthpOzdKwK2mmnMguk8f+0FwM8KNG0erCji4nkA3EFbN7OOt
D6Lp2yPmFGxWiAqs2D/Wy1x2+p5Zd8FoS6omlkPPAoGALMb+pgatVrGLIDyu9NU/
BJm67Y5OJD3rY8i2ilNSIqtbccXrReI37ECqUqqF6xCbcKNia64xLStBO2GjnxKw
GGFFMhqCINSuEPPMd2WRvI7H1ikP50N9MRpIagGBc1ijL00aRL9iagXErhOyNE6L
QydIFD/VYHcQyBo+aPZpvMQ=
-----END PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
MIIEHTCCAwUCEQCWoHEjhedCvp2PN2Sw1xZ1MA0GCSqGSIb3DQEBBQUAMIG9MQsw
CQYDVQQGEwJUUjEPMA0GA1UECAwGw4dvcnVtMRQwEgYDVQQHDAtCYcWfbWFrw6fE
sTESMBAGA1UEAwwJbG9jYWxob3N0MRUwEwYDVQQKDAxNb3ppbGxhIFRlc3QxNjA0
BgNVBAsMLUF1dG9wdXNoIFRlc3QgYXV0b3B1c2gvdGVzdHMvY2VydHMvc2VydmVy
LnBlbTEkMCIGCSqGSIb3DQEJARYVb3R0by5wdXNoQGV4YW1wbGUuY29tMCAXDTE2
MDkyNzE2MzMzNloYDzIxMTYwOTAzMTYzMzM2WjCBvjELMAkGA1UEBhMCVFIxDzAN
BgNVBAgMBsOHb3J1bTEUMBIGA1UEBwwLQmHFn21ha8OnxLExEjAQBgNVBAMMCWxv
Y2FsaG9zdDEVMBMGA1UECgwMTW96aWxsYSBUZXN0MTcwNQYDVQQLDC5BdXRvcHVz
aCBUZXN0IGF1dG9wdXNoL3Rlc3RzL2NlcnRzL2NsaWVudDIucGVtMSQwIgYJKoZI
hvcNAQkBFhVvdHRvLnB1c2hAZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUA
A4IBDwAwggEKAoIBAQDQ25kt31KETmsUux1rc974Yo0OpWsj1Es6TMZ94TmNSk+e
ZvctB+wS4a2wh2QHuR6XYyuRgTvIapIfzb/qyNzfcZ+TuJKnsXxu7gBXWqSMxpaZ
d1pc0cRllwh2GlwIC0ARl8d3O//iP+36uIqhM1iTrd9833WDSV6it+8xDgBGgb3B
+ZeenLqXJUqqhBeWh2ERZHhe3APad5UA1vF9nSQTiRtAK8dMS+HvBugC37Yhvcgp
+sFu64iDTjYi+DkOjZNVeLw9WH2XxMqRsx34VkAZw4F8xkay5sgjydzaoP+7HxNv
clQDMaq/u7pUpP9lsZmPvbsfqKGv2D5lbkCbzWWVAgMBAAGjGDAWMBQGA1UdEQQN
MAuCCWxvY2FsaG9zdDANBgkqhkiG9w0BAQUFAAOCAQEAWHoQ2+ChM9QylCLoOrLA
r2r4NT/C1RErRt+h4hfIJXPc+zrBs232N7gWqUfpwnsqGu4f+98ro5o63l7m/Fi3
dAUW8WjDzeMyQ8ygWHQFD9MZYyUHrvwPiw0DNyMiZ329SWsvAOB/wNCr2fRrd2KO
zdGnV0yHZD9GdffDwvxyKVAoKRFtiIZjBqgoWmSDPeN3evYDNcQvJXPadcId8426
aOZJtEsGZG9CtocrwyV0VufWt41mvUcS6LzPJYeKWbOslguv7CQ10gIFSogS79s9
nE+TleQFff3H0Qt2CBlAA9txLwma8ha9SS9xL/UGWZlbzwJlbdEbJATgaHvrD9Vn
Vw==
-----END CERTIFICATE-----
63 changes: 63 additions & 0 deletions autopush/tests/certs/makecerts.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# encoding: utf-8

import os
import uuid

from OpenSSL.crypto import (
FILETYPE_PEM, PKey, TYPE_RSA, X509, X509Extension,
dump_certificate, dump_privatekey)


def make_cert(filename, cacert=None, cakey=None):
key = PKey()
key.generate_key(TYPE_RSA, 2048)

cert = X509()
subject = cert.get_subject()

subject.C = b"TR"
subject.ST = b"Çorum"
subject.L = b"Başmakçı"
subject.CN = b"localhost"
subject.O = b"Mozilla Test"
subject.OU = b"Autopush Test %s" % filename
subject.emailAddress = b"[email protected]"
subjectAltName = X509Extension(b'subjectAltName', False, b'DNS:localhost')
cert.add_extensions([subjectAltName])

cert.set_serial_number(uuid.uuid4().int)
cert.gmtime_adj_notBefore(0)
cert.gmtime_adj_notAfter(60 * 60 * 24 * 365 * 100)

cert.set_pubkey(key)

if not cacert:
# self sign
cacert = cert
cakey = key
cert.set_issuer(cacert.get_subject())
cert.sign(cakey, 'sha1')

with open(filename, 'wb') as fp:
fp.write(dump_privatekey(FILETYPE_PEM, key))
fp.write(dump_certificate(FILETYPE_PEM, cert))
return cert, key


def main():
certsdir = os.path.dirname(__file__)

# server.pem is self signed
server, serverkey = make_cert(os.path.join(certsdir, "server.pem"))

client1, _ = make_cert(os.path.join(certsdir, "client1.pem"),
cacert=server, cakey=serverkey)
make_cert(os.path.join(certsdir, "client2.pem"),
cacert=server, cakey=serverkey)

with open(os.path.join(certsdir, "client1_sha256.txt"), 'w') as fp:
fp.write(client1.digest('sha256'))


if __name__ == '__main__':
main()
Loading

0 comments on commit 58e0cbb

Please sign in to comment.