2015-10-04 23:02:04 +02:00
|
|
|
# -*- coding: utf8 -*-
|
|
|
|
'''
|
|
|
|
Manage X.509 certificate life cycle
|
|
|
|
===================================
|
|
|
|
|
|
|
|
This state is useful for managing X.509 certificates' life cycles.
|
|
|
|
|
2016-09-24 21:51:02 +02:00
|
|
|
Copyright (c) 2014, 2016 Jan Dittberner <jan@dittberner.info>
|
2015-10-04 23:02:04 +02:00
|
|
|
'''
|
|
|
|
|
2016-09-24 21:51:02 +02:00
|
|
|
from cryptography import x509
|
|
|
|
from cryptography.hazmat.backends import default_backend
|
2015-10-04 23:02:04 +02:00
|
|
|
from datetime import datetime
|
|
|
|
import os
|
|
|
|
|
|
|
|
|
|
|
|
def _error(ret, err_msg):
|
|
|
|
ret['result'] = False
|
|
|
|
ret['comment'] = err_msg
|
|
|
|
return ret
|
|
|
|
|
|
|
|
|
|
|
|
def valid_certificate(
|
|
|
|
name, mindays=14, keyfile=None,
|
|
|
|
checkchain=False, trustedcerts=None):
|
|
|
|
'''
|
|
|
|
Checks whether the given certificate file is valid.
|
|
|
|
|
|
|
|
name
|
|
|
|
The name of the certificate file to check
|
|
|
|
mindays
|
|
|
|
Mark the certificate as invalid if it is valid for less then this many
|
|
|
|
days
|
|
|
|
'''
|
|
|
|
ret = {
|
|
|
|
'name': name,
|
|
|
|
'changes': {},
|
|
|
|
'result': None,
|
|
|
|
'comment': ''}
|
|
|
|
if not os.path.isfile(name):
|
|
|
|
return _error(
|
|
|
|
ret, 'certificate file {0} does not exist'.format(name))
|
2016-09-24 21:51:02 +02:00
|
|
|
with open(name) as pemfile:
|
|
|
|
try:
|
|
|
|
cert = x509.load_pem_x509_certificate(pemfile.read(),
|
|
|
|
default_backend())
|
|
|
|
except Exception as e:
|
|
|
|
return _error(
|
|
|
|
ret, 'error loading certificate {0}: {1}'.format(name, e))
|
|
|
|
notafter = cert.not_valid_after
|
|
|
|
delta = notafter - datetime.utcnow()
|
2015-10-04 23:02:04 +02:00
|
|
|
if delta.days < mindays:
|
|
|
|
return _error(
|
|
|
|
ret,
|
|
|
|
'certificate {0} is only valid for {1} more day(s)'.format(
|
|
|
|
name, delta.days))
|
|
|
|
# TODO: check keyfile match
|
|
|
|
# TODO: check trust chain
|
|
|
|
ret['comment'] = (
|
|
|
|
'certificate {0} is ok and still valid for {1} days'.format(
|
|
|
|
name, delta.days))
|
|
|
|
ret['result'] = True
|
|
|
|
return ret
|