def lookup(self, order_name): """ sohw order details based on ordername """ self.logger.debug('Order.show({0})'.format(order_name)) order_dic = {} tmp_dic = self.info(order_name) if tmp_dic: if 'status' in tmp_dic: order_dic['status'] = tmp_dic['status'] if 'expires' in tmp_dic: order_dic['expires'] = uts_to_date_utc(tmp_dic['expires']) if 'notbefore' in tmp_dic: if tmp_dic['notbefore'] != 0: order_dic['notBefore'] = uts_to_date_utc( tmp_dic['notbefore']) if 'notafter' in tmp_dic: if tmp_dic['notafter'] != 0: order_dic['notAfter'] = uts_to_date_utc( tmp_dic['notafter']) if 'identifiers' in tmp_dic: order_dic['identifiers'] = json.loads(tmp_dic['identifiers']) authz_list = self.dbstore.authorization_lookup( 'order__name', order_name, ['name']) if authz_list: order_dic["authorizations"] = [] for authz in authz_list: if 'name' in authz: order_dic["authorizations"].append('{0}{1}{2}'.format( self.server_name, self.path_dic['authz_path'], authz['name'])) return order_dic
def _lookup(self, order_name): """ sohw order details based on ordername """ self.logger.debug('Order._lookup({0})'.format(order_name)) order_dic = {} tmp_dic = self._info(order_name) if tmp_dic: if 'status' in tmp_dic: order_dic['status'] = tmp_dic['status'] if 'expires' in tmp_dic: order_dic['expires'] = uts_to_date_utc(tmp_dic['expires']) if 'notbefore' in tmp_dic: if tmp_dic['notbefore'] != 0: order_dic['notBefore'] = uts_to_date_utc( tmp_dic['notbefore']) if 'notafter' in tmp_dic: if tmp_dic['notafter'] != 0: order_dic['notAfter'] = uts_to_date_utc( tmp_dic['notafter']) if 'identifiers' in tmp_dic: order_dic['identifiers'] = json.loads(tmp_dic['identifiers']) try: authz_list = self.dbstore.authorization_lookup( 'order__name', order_name, ['name', 'status__name']) except BaseException as err_: self.logger.critical( 'acme2certifier database error in Order._lookup(): {0}'. format(err_)) authz_list = [] if authz_list: order_dic["authorizations"] = [] # collect status of different authorizations in list validity_list = [] for authz in authz_list: if 'name' in authz: order_dic["authorizations"].append('{0}{1}{2}'.format( self.server_name, self.path_dic['authz_path'], authz['name'])) if 'status__name' in authz: if authz['status__name'] == 'valid': validity_list.append(True) else: validity_list.append(False) # update orders status from pending to ready if validity_list and 'status' in order_dic: if False not in validity_list and order_dic[ 'status'] == 'pending': self._update({'name': order_name, 'status': 'ready'}) self.logger.debug('Order._lookup() ended') return order_dic
def _convert_data(self, cert_list): """ convert data from uts to real date """ self.logger.debug('Housekeeping._convert_dates()') for cert in cert_list: expire_list = ('order.expires', 'authorization.expires', 'challenge.expires') for ele in expire_list: if ele in cert and cert[ele]: cert[ele] = uts_to_date_utc(cert[ele], '%Y-%m-%d %H:%M:%S') # set uts to 0 if we do not have them in dictionary if 'certificate.issue_uts' not in cert or 'certificate.expire_uts' not in cert: cert['certificate.issue_uts'] = 0 cert['certificate.expire_uts'] = 0 # if uts is zero we try to get the dates from certificate if cert['certificate.issue_uts'] == 0 or cert[ 'certificate.expire_uts'] == 0: # cover cases without certificate in dict if 'certificate.cert_raw' in cert: (issue_uts, expire_uts) = cert_dates_get(self.logger, cert['certificate.cert_raw']) cert['certificate.issue_uts'] = issue_uts cert['certificate.expire_uts'] = expire_uts else: cert['certificate.issue_uts'] = 0 cert['certificate.expire_uts'] = 0 if cert['certificate.issue_uts'] > 0 and cert[ 'certificate.expire_uts'] > 0: cert['certificate.issue_date'] = uts_to_date_utc( cert['certificate.issue_uts'], '%Y-%m-%d %H:%M:%S') cert['certificate.expire_date'] = uts_to_date_utc( cert['certificate.expire_uts'], '%Y-%m-%d %H:%M:%S') else: cert['certificate.issue_date'] = '' cert['certificate.expire_date'] = '' # add serial number if 'certificate.cert_raw' in cert: try: cert['certificate.serial'] = cert_serial_get( self.logger, cert['certificate.cert_raw']) except BaseException: cert['certificate.serial'] = '' return cert_list
def _info(self, challenge_name): """ get challenge details """ self.logger.debug('Challenge._info({0})'.format(challenge_name)) try: challenge_dic = self.dbstore.challenge_lookup( 'name', challenge_name, vlist=('type', 'token', 'status__name', 'validated')) except BaseException as err_: self.logger.critical( 'acme2certifier database error in Challenge._info(): {0}'. format(err_)) challenge_dic = {} if 'status' in challenge_dic and challenge_dic['status'] == 'valid': if 'validated' in challenge_dic: # convert validated timestamp to RFC3339 format - if it fails remove key from dictionary try: challenge_dic['validated'] = uts_to_date_utc( challenge_dic['validated']) except BaseException: challenge_dic.pop('validated') else: if 'validated' in challenge_dic: challenge_dic.pop('validated') self.logger.debug('Challenge._info({0}) ended'.format(challenge_name)) return challenge_dic
def revoke(self, content): """ revoke request """ self.logger.debug('Certificate.revoke()') response_dic = {} # check message (code, message, detail, _protected, payload, account_name) = self.message.check(content) if code == 200: if 'certificate' in payload: (code, error) = self._revocation_request_validate(account_name, payload) if code == 200: # revocation starts here # revocation reason is stored in error variable rev_date = uts_to_date_utc(uts_now()) with self.cahandler(self.debug, self.logger) as ca_handler: (code, message, detail) = ca_handler.revoke(payload['certificate'], error, rev_date) else: message = error detail = None else: # message could not get decoded code = 400 message = 'urn:ietf:params:acme:error:malformed' detail = 'certificate not found' # prepare/enrich response status_dic = {'code': code, 'message' : message, 'detail' : detail} response_dic = self.message.prepare_response(response_dic, status_dic) self.logger.debug('Certificate.revoke() ended with: {0}'.format(response_dic)) return response_dic
def revoke(self, cert, rev_reason='unspecified', rev_date=uts_to_date_utc(uts_now())): """ revoke certificate """ self.logger.debug('CAhandler.revoke({0}: {1})'.format( rev_reason, rev_date)) # lookup REST-PATH of issuing CA ca_dic = self._ca_get_properties('name', self.ca_name) if 'href' in ca_dic: # get serial from pem file serial = cert_serial_get(self.logger, cert) if serial: # get certificate information via rest by search for ca+ serial cert_dic = self._cert_get_properties(serial, ca_dic['href']) if 'certificates' in cert_dic: if len(cert_dic['certificates'] ) > 0 and 'href' in cert_dic['certificates'][0]: # revoke the cert data = { 'newStatus': 'revoked', 'crlReason': rev_reason, 'invalidityDate': rev_date } cert_dic = self._api_post( cert_dic['certificates'][0]['href'] + '/status', data) if 'status' in cert_dic: # code = cert_dic['status'] code = 400 message = 'urn:ietf:params:acme:error:alreadyRevoked' if 'message' in cert_dic: detail = cert_dic['message'] else: detail = 'no details' else: code = 200 message = None detail = None else: code = 404 message = 'urn:ietf:params:acme:error:serverInternal' detail = 'Cert path could not be found' else: code = 404 message = 'urn:ietf:params:acme:error:serverInternal' detail = 'Cert could not be found' else: code = 404 message = 'urn:ietf:params:acme:error:serverInternal' detail = 'failed to get serial number from cert' else: code = 404 message = 'urn:ietf:params:acme:error:serverInternal' detail = 'CA could not be found' return (code, message, detail)
def _store_cert(self, ca_id, cert_name, serial, cert, name_hash, issuer_hash): """ store certificate to database """ self.logger.debug('CAhandler._store_cert()') # insert certificate into item table insert_date = uts_to_date_utc(uts_now(), '%Y%m%d%H%M%SZ') item_dic = {'type': 3, 'comment': 'from acme2certifier', 'source': 2, 'date': insert_date, 'name': cert_name} row_id = self._item_insert(item_dic) # insert certificate to cert table cert_dic = {'item': row_id, 'serial': serial, 'issuer': ca_id, 'ca': 0, 'cert': cert, 'iss_hash': issuer_hash, 'hash': name_hash} row_id = self._cert_insert(cert_dic) self.logger.debug('CAhandler._store_cert() ended')
def add(self, payload, aname): """ add order request to database """ self.logger.debug('Order.add({0})'.format(aname)) error = None auth_dic = {} order_name = generate_random_string(self.logger, 12) expires = uts_now() + self.expiry if 'identifiers' in payload: data_dic = {'status': 2, 'expires': expires, 'account': aname} data_dic['name'] = order_name data_dic['identifiers'] = json.dumps(payload['identifiers']) #if 'notBefore' in payload: # data_dic['notbefore'] = payload['notBefore'] #if 'notAfter' in payload: # data_dic['notafter'] = payload['notAfter'] # check identifiers error = self.identifiers_check(payload['identifiers']) # change order status if needed if error: data_dic['status'] = 1 # add order to db oid = self.dbstore.order_add(data_dic) if not error: if oid: error = None for auth in payload['identifiers']: # generate name auth_name = generate_random_string(self.logger, 12) # store to return to upper func auth_dic[auth_name] = auth.copy() auth['name'] = auth_name auth['order'] = oid auth['status'] = 'pending' self.dbstore.authorization_add(auth) else: error = 'urn:ietf:params:acme:error:malformed' else: error = 'urn:ietf:params:acme:error:unsupportedIdentifier' # print(auth_dic) return (error, order_name, auth_dic, uts_to_date_utc(expires))
def _authz_info(self, url): """ return authzs information """ self.logger.debug('Authorization._authz_info({0})'.format(url)) authz_name = url.replace('{0}{1}'.format(self.server_name, self.path_dic['authz_path']), '') expires = uts_now() + self.validity token = generate_random_string(self.logger, 32) authz_info_dic = {} # lookup authorization based on name try: authz = self.dbstore.authorization_lookup('name', authz_name) except BaseException as err_: self.logger.critical('acme2certifier database error in Authorization._authz_info(): {0}'.format(err_)) authz = None if authz: # update authorization with expiry date and token (just to be sure) try: self.dbstore.authorization_update({'name' : authz_name, 'token' : token, 'expires' : expires}) except BaseException as err_: self.logger.critical('acme2certifier database error in Authorization._authz_info(): {0}'.format(err_)) authz_info_dic['expires'] = uts_to_date_utc(expires) # get authorization information from db to be inserted in message tnauth = None try: auth_info = self.dbstore.authorization_lookup('name', authz_name, ['status__name', 'type', 'value']) except BaseException as err_: self.logger.critical('acme2certifier database error in Authorization._authz_info(): {0}'.format(err_)) auth_info = {} if auth_info: if 'status__name' in auth_info[0]: authz_info_dic['status'] = auth_info[0]['status__name'] else: authz_info_dic['status'] = 'pending' if 'type' in auth_info[0] and 'value' in auth_info[0]: authz_info_dic['identifier'] = {'type' : auth_info[0]['type'], 'value' : auth_info[0]['value']} if auth_info[0]['type'] == 'TNAuthList': tnauth = True with Challenge(self.debug, self.server_name, self.logger, expires) as challenge: # get challenge data (either existing or new ones) authz_info_dic['challenges'] = challenge.challengeset_get(authz_name, authz_info_dic['status'], token, tnauth) self.logger.debug('Authorization._authz_info() returns: {0}'.format(json.dumps(authz_info_dic))) return authz_info_dic
def revoke(self, cert, rev_reason='unspecified', rev_date=None): """ revoke certificate """ self.logger.debug('CAhandler.revoke()') # overwrite revocation date - we ignore what has been submitted rev_date = uts_to_date_utc(uts_now(), '%Y%m%d%H%M%SZ') rev_reason = 0 if self.xdb_file: # load ca cert and key (_ca_key, _ca_cert, ca_id) = self._ca_load() serial = cert_serial_get(self.logger, cert) if serial: serial = '{:X}'.format(serial) if ca_id and serial: # check if certificate has alreay been revoked: if not self._revocation_search('serial', serial): rev_dic = {'caID': ca_id, 'serial': serial, 'date': rev_date, 'invaldate': rev_date, 'reasonBit': rev_reason} row_id = self._revocation_insert(rev_dic) if row_id: code = 200 message = None detail = None else: code = 500 message = 'urn:ietf:params:acme:error:serverInternal' detail = 'database update failed' else: code = 400 message = 'urn:ietf:params:acme:error:alreadyRevoked' detail = 'Certificate has already been revoked' else: code = 500 message = 'urn:ietf:params:acme:error:serverInternal' detail = 'certificate lookup failed' else: code = 500 message = 'urn:ietf:params:acme:error:serverInternal' detail = 'configuration error' self.logger.debug('Certificate.revoke() ended') return(code, message, detail)
def _csr_import(self, csr, request_name): """ check existance of csr and load into db """ self.logger.debug('CAhandler._csr_insert()') csr_info = self._csr_search('request', csr) if not csr_info: # csr does not exist in db - lets import it insert_date = uts_to_date_utc(uts_now(), '%Y%m%d%H%M%SZ') item_dic = {'type': 2, 'comment': 'from acme2certifier', 'source': 2, 'date': insert_date, 'name': request_name} row_id = self._item_insert(item_dic) # insert csr csr_info = {'item': row_id, 'signed': 1, 'request': csr} self._csr_insert(csr_info) self.logger.debug('CAhandler._csr_insert()') return csr_info
def cleanup(self, timestamp=None, purge=False): """ cleanup routine to shrink table-size """ self.logger.debug('Certificate.cleanup({0},{1})'.format(timestamp, purge)) field_list = ['id', 'name', 'expire_uts', 'issue_uts', 'cert', 'cert_raw', 'csr', 'created_at', 'order__id', 'order__name'] # get expired certificates try: certificate_list = self.dbstore.certificates_search('expire_uts', timestamp, field_list, '<=') except BaseException as err_: self.logger.critical('acme2certifier database error in Certificate.cleanup() search: {0}'.format(err_)) certificate_list = [] report_list = [] for cert in certificate_list: (to_be_cleared, cert) = self._invalidation_check(cert, timestamp, purge) if to_be_cleared: report_list.append(cert) if not purge: # we are just modifiying data for cert in report_list: data_dic = { 'name': cert['name'], 'expire_uts': cert['expire_uts'], 'issue_uts': cert['issue_uts'], 'cert': 'removed by certificates.cleanup() on {0} '.format(uts_to_date_utc(timestamp)), 'cert_raw': cert['cert_raw'] } try: self.dbstore.certificate_add(data_dic) except BaseException as err_: self.logger.critical('acme2certifier database error in Certificate.cleanup() add: {0}'.format(err_)) else: # delete entries from certificates table for cert in report_list: try: self.dbstore.certificate_delete('id', cert['id']) except BaseException as err_: self.logger.critical('acme2certifier database error in Certificate.cleanup() delete: {0}'.format(err_)) self.logger.debug('Certificate.cleanup() ended with: {0} certs'.format(len(report_list))) return (field_list, report_list)
def authz_info(self, url): """ return authzs information """ self.logger.debug('Authorization.info({0})'.format(url)) authz_name = url.replace( '{0}{1}'.format(self.server_name, self.path_dic['authz_path']), '') expires = uts_now() + self.expiry token = generate_random_string(self.logger, 32) authz_info_dic = {} if self.dbstore.authorization_lookup('name', authz_name): # update authorization with expiry date and token (just to be sure) self.dbstore.authorization_update({ 'name': authz_name, 'token': token, 'expires': expires }) authz_info_dic['expires'] = uts_to_date_utc(expires) # get authorization information from db to be inserted in message tnauth = None auth_info = self.dbstore.authorization_lookup( 'name', authz_name, ['status__name', 'type', 'value']) if auth_info: authz_info_dic['status'] = auth_info[0]['status__name'] authz_info_dic['identifier'] = { 'type': auth_info[0]['type'], 'value': auth_info[0]['value'] } if auth_info[0]['type'] == 'TNAuthList': tnauth = True challenge = Challenge(self.debug, self.server_name, self.logger, expires) authz_info_dic['challenges'] = challenge.new_set( authz_name, token, tnauth) self.logger.debug('Authorization.authz_info() returns: {0}'.format( json.dumps(authz_info_dic))) return authz_info_dic
def revoke(self, cert, rev_reason='unspecified', rev_date=None): """ revoke certificate """ self.logger.debug('CAhandler.revoke({0}: {1})'.format(rev_reason, rev_date)) code = None message = None detail = None # overwrite revocation date - we ignore what has been submitted rev_date = uts_to_date_utc(uts_now(), '%y%m%d%H%M%SZ') if 'issuing_ca_crl' in self.issuer_dict and self.issuer_dict['issuing_ca_crl']: # load ca cert and key (ca_key, ca_cert) = self._ca_load() # turn of chain_check due to issues in pyopenssl (check is not working if key-usage is set) # result = self._certificate_chain_verify(cert, ca_cert) result = None # proceed if the cert and ca-cert belong together # if not result: serial = cert_serial_get(self.logger, cert) # serial = serial.replace('0x', '') if ca_key and ca_cert and serial: serial = hex(serial).replace('0x', '') if os.path.exists(self.issuer_dict['issuing_ca_crl']): # existing CRL with open(self.issuer_dict['issuing_ca_crl'], 'r') as fso: crl = crypto.load_crl(crypto.FILETYPE_PEM, fso.read()) # check CRL already contains serial sn_match = self._crl_check(crl, serial) else: # new CRL crl = crypto.CRL() sn_match = None # this is the revocation operation if not sn_match: revoked = crypto.Revoked() revoked.set_reason(convert_string_to_byte(rev_reason)) revoked.set_serial(convert_string_to_byte(serial)) revoked.set_rev_date(convert_string_to_byte(rev_date)) crl.add_revoked(revoked) # save CRL crl_text = crl.export(ca_cert, ca_key, crypto.FILETYPE_PEM, 7, convert_string_to_byte('sha256')) with open(self.issuer_dict['issuing_ca_crl'], 'wb') as fso: fso.write(crl_text) code = 200 else: code = 400 message = 'urn:ietf:params:acme:error:alreadyRevoked' detail = 'Certificate has already been revoked' else: code = 400 message = 'urn:ietf:params:acme:error:serverInternal' detail = 'configuration error' #else: # code = 400 # message = 'urn:ietf:params:acme:error:serverInternal' # detail = result else: code = 400 message = 'urn:ietf:params:acme:error:serverInternal' detail = 'Unsupported operation' self.logger.debug('CAhandler.revoke() ended') return(code, message, detail)
# pylint: disable=E0401, C0413 import sys sys.path.insert(0, '..') sys.path.insert(1, '.') import time from acme.helper import logger_setup, uts_to_date_utc from acme.housekeeping import Housekeeping if __name__ == '__main__': DEBUG = True # initialize logger LOGGER = logger_setup(DEBUG) SUFFIX = uts_to_date_utc(int(time.time()), '%Y-%m-%d-%H%M%S') # this is just for testing # from shutil import copyfile # copyfile('db.sqlite3.old', 'db.sqlite3') # copyfile('acme/acme_srv.db.old', 'acme/acme_srv.db') with Housekeeping(DEBUG, LOGGER) as housekeeping: # certificate report in json format cert_report = housekeeping.certreport_get( report_name='certificate_report_{0}'.format(SUFFIX), report_format='json') # certificate report in csv format housekeeping.certreport_get( report_name='certificate_report_{0}'.format(SUFFIX))