def enroll(self, csr): """ enroll certificate from NCLM """ self.logger.debug('CAhandler.enroll()') cert_bundle = None error = None cert_raw = None # recode csr csr = textwrap.fill(b64_url_recode(self.logger, csr), 64) + '\n' if self.est_host: (error, ca_pem) = self._cacerts_get() if not error: if ca_pem: if self.est_user or self.est_client_cert: (error, cert_raw) = self._simpleenroll(csr) else: error = 'Authentication information missing' self.logger.error('CAhandler.enroll(): {0}'.format(error)) if not error: cert_bundle = cert_raw + ca_pem cert_raw = cert_raw.replace('-----BEGIN CERTIFICATE-----\n', '') cert_raw = cert_raw.replace('-----END CERTIFICATE-----\n', '') cert_raw = cert_raw.replace('\n', '') else: self.logger.error('CAhandler.enroll(): {0}'.format(error)) else: error = 'no CA certificates found' self.logger.error('CAhandler.enroll(): {0}'.format(error)) else: self.logger.error('CAhandler.enroll(): {0}'.format(error)) self.logger.debug('Certificate.enroll() ended') return(error, cert_bundle, cert_raw, None)
def enroll(self, csr): """ enroll certificate from via MS certsrv """ self.logger.debug('CAhandler.enroll()') cert_bundle = None error = None cert_raw = None if self.openssl_bin: # prepare the CSR to be signed csr = build_pem_file(self.logger, None, b64_url_recode(self.logger, csr), None, True) # dump csr key self._file_save('{0}/csr.pem'.format(self.tmp_dir), csr) # build openssl command and run it openssl_cmd = self._opensslcmd_build() rcode = subprocess.call(openssl_cmd) if rcode: self.logger.error('CAhandler.enroll(): failed: {0}'.format(rcode)) error = 'rc from enrollment not 0' # generate certificates we need to return if os.path.isfile('{0}/cert.pem'.format(self.tmp_dir)): (cert_bundle, cert_raw) = self._certs_bundle() else: error = 'Enrollment failed' # delete temporary files self._tmp_dir_delete() else: error = 'Config incomplete' self.logger.debug('Certificate.enroll() ended with error: {0}'.format(error)) return(error, cert_bundle, cert_raw, None)
def _account_check(self, account_name, certificate): """ check account """ self.logger.debug('Certificate.issuer_check()') try: result = self.dbstore.certificate_account_check( account_name, b64_url_recode(self.logger, certificate)) except BaseException as err_: self.logger.critical( 'acme2certifier database error in Certificate._account_check(): {0}' .format(err_)) result = None return result
def enroll(self, csr): """ enroll certificate from via MS certsrv """ self.logger.debug('CAhandler.enroll({0})'.format(self.template)) cert_bundle = None error = None cert_raw = None if self.host and self.user and self.password and self.template: # setup certserv ca_server = Certsrv(self.host, self.user, self.password, self.auth_method, self.ca_bundle, proxies=self.proxy) # check connection and credentials auth_check = self._check_credentials(ca_server) if auth_check: # recode csr csr = textwrap.fill(b64_url_recode(self.logger, csr), 64) + '\n' # get ca_chain try: ca_pkcs7 = convert_byte_to_string(ca_server.get_chain(encoding='b64')) ca_pem = self._pkcs7_to_pem(ca_pkcs7) # replace crlf with lf # ca_pem = ca_pem.replace('\r\n', '\n') except BaseException as err_: ca_pem = None self.logger.error('ca_server.get_chain() failed with error: {0}'.format(err_)) try: cert_raw = convert_byte_to_string(ca_server.get_cert(csr, self.template)) # replace crlf with lf cert_raw = cert_raw.replace('\r\n', '\n') except BaseException as err_: cert_raw = None self.logger.error('ca_server.get_cert() failed with error: {0}'.format(err_)) if ca_pem and cert_raw: cert_bundle = cert_raw + ca_pem cert_raw = cert_raw.replace('-----BEGIN CERTIFICATE-----\n', '') cert_raw = cert_raw.replace('-----END CERTIFICATE-----\n', '') cert_raw = cert_raw.replace('\n', '') else: self.logger.error('cert bundling failed') error = 'cert bundling failed' else: self.logger.error('Connection or Credentialcheck failed') error = 'Connection or Credentialcheck failed.' else: self.logger.error('Config incomplete') error = 'Config incomplete' self.logger.debug('Certificate.enroll() ended') return(error, cert_bundle, cert_raw, None)
def _certificate_chain_verify(self, cert, ca_cert): """ verify certificate chain """ self.logger.debug('CAhandler._certificate_chain_verify()') error = None pem_file = build_pem_file(self.logger, None, b64_url_recode(self.logger, cert), True) try: cert = crypto.load_certificate(crypto.FILETYPE_PEM, pem_file) except Exception as err_: cert = None error = err_ if not error: # Create a certificate store and add ca cert(s) try: store = crypto.X509Store() store.add_cert(ca_cert) except Exception: error = 'issuing certificate could not be added to trust-store' if not error: # add ca chain to truststore for cert_name in self.ca_cert_chain_list: try: with open(cert_name, 'r') as fso: cain_cert = crypto.load_certificate( crypto.FILETYPE_PEM, fso.read()) store.add_cert(cain_cert) except Exception: error = 'certificate {0} could not be added to trust store'.format( cert_name) if not error: # Create a certificate context using the store and the downloaded certificate store_ctx = crypto.X509StoreContext(store, cert) # Verify the certificate, returns None if it can validate the certificate try: # pylint: disable=E1111 result = store_ctx.verify_certificate() except Exception as err_: result = str(err_) else: result = error else: result = 'certificate could not get parsed' self.logger.debug( 'CAhandler._certificate_chain_verify() ended with {0}'.format( result)) return result
def _csr_process(self, order_name, csr): """ process certificate signing request """ self.logger.debug('Order._csr_process({0})'.format(order_name)) order_dic = self._info(order_name) if order_dic: # change decoding from b64url to b64 csr = b64_url_recode(self.logger, csr) with Certificate(self.debug, self.server_name, self.logger) as certificate: # certificate = Certificate(self.debug, self.server_name, self.logger) certificate_name = certificate.store_csr(order_name, csr) if certificate_name: (error, detail) = certificate.enroll_and_store( certificate_name, csr) if not error: code = 200 message = certificate_name # detail = None else: code = 400 message = error if message == 'urn:ietf:params:acme:error:serverInternal': code = 500 else: code = 500 message = 'urn:ietf:params:acme:error:serverInternal' detail = 'CSR processing failed' else: code = 400 message = 'urn:ietf:params:acme:error:unauthorized' detail = 'order: {0} not found'.format(order_name) self.logger.debug( 'Order._csr_process() ended with order:{0} {1}:{2}:{3}'.format( order_name, code, message, detail)) return (code, message, detail)
def revoke(self, _cert, _rev_reason, _rev_date): """ revoke certificate """ self.logger.debug('CAhandler.revoke()') user_key = None code = 500 message = 'urn:ietf:params:acme:error:serverInternal' detail = None try: certpem = '-----BEGIN CERTIFICATE-----\n{0}\n-----END CERTIFICATE-----\n'.format( textwrap.fill(str(b64_url_recode(self.logger, _cert)), 64)) cert = josepy.ComparableX509( crypto.load_certificate(crypto.FILETYPE_PEM, certpem)) if os.path.exists(self.keyfile): user_key = self._user_key_load() net = client.ClientNetwork(user_key) if user_key: directory = messages.Directory.from_json( net.get('{0}{1}'.format( self.url, self.path_dic['directory_path'])).json()) acmeclient = client.ClientV2(directory, net=net) reg = messages.NewRegistration.from_data( key=user_key, email=self.email, terms_of_service_agreed=True, only_return_existing=True) if not self.account: self._account_lookup(acmeclient, reg, directory) if self.account: regr = messages.RegistrationResource( uri="{0}{1}{2}".format(self.url, self.path_dic['acct_path'], self.account), body=reg) self.logger.debug( 'CAhandler.revoke() checking remote registration status' ) regr = acmeclient.query_registration(regr) if regr.body.status == "valid": self.logger.debug( 'CAhandler.revoke() issuing revocation order') acmeclient.revoke(cert, 1) self.logger.debug('CAhandler.revoke() successfull') code = 200 message = None else: self.logger.error( 'CAhandler.enroll: Bad ACME account: {0}'.format( regr.body.error)) detail = 'Bad ACME account: {0}'.format( regr.body.error) else: self.logger.error( 'CAhandler.revoke(): could not find account key and lookup at acme-endpoint failed.' ) detail = 'account lookup failed' else: self.logger.error( 'CAhandler.revoke(): could not load user_key {0}'.format( self.keyfile)) detail = 'Internal Error' except Exception as err: self.logger.error('CAhandler.enroll: error: {0}'.format(err)) detail = str(err) finally: del user_key self.logger.debug('Certificate.revoke() ended') return (code, message, detail)
def enroll(self, csr): """ enroll certificate """ # pylint: disable=R0915 self.logger.debug('CAhandler.enroll()') csr_pem = '-----BEGIN CERTIFICATE REQUEST-----\n{0}\n-----END CERTIFICATE REQUEST-----\n'.format( textwrap.fill(str(b64_url_recode(self.logger, csr)), 64)) cert_bundle = None error = None cert_raw = None poll_indentifier = None user_key = None # check CN and SAN against black/whitlist result = self._csr_check(csr) if result: try: user_key = self._user_key_load() net = client.ClientNetwork(user_key) directory = messages.Directory.from_json( net.get('{0}{1}'.format( self.url, self.path_dic['directory_path'])).json()) acmeclient = client.ClientV2(directory, net=net) reg = messages.Registration.from_data( key=user_key, terms_of_service_agreed=True) if self.account: regr = messages.RegistrationResource( uri="{0}{1}{2}".format(self.url, self.path_dic['acct_path'], self.account), body=reg) self.logger.debug( 'CAhandler.enroll(): checking remote registration status' ) regr = acmeclient.query_registration(regr) else: # new account or existing account with missing account id regr = self._account_register(acmeclient, user_key, directory) if regr.body.status == "valid": self.logger.debug( 'CAhandler.enroll() issuing signing order') self.logger.debug('CAhandler.enroll() CSR: ' + str(csr_pem)) order = acmeclient.new_order(csr_pem) # query challenges for authzr in list(order.authorizations): (challenge_name, challenge_content, challenge) = self._http_challenge_info( authzr, user_key) if challenge_name and challenge_content: # store challenge in database to allow challenge validation self._challenge_store(challenge_name, challenge_content) _auth_response = acmeclient.answer_challenge( challenge, challenge.chall.response(user_key) ) # lgtm [py/unused-local-variable] self.logger.debug( 'CAhandler.enroll() polling for certificate') order = acmeclient.poll_and_finalize(order) if order.fullchain_pem: self.logger.debug('CAhandler.enroll() successful') cert_bundle = str(order.fullchain_pem) cert_raw = str( base64.b64encode( crypto.dump_certificate( crypto.FILETYPE_ASN1, crypto.load_certificate( crypto.FILETYPE_PEM, cert_bundle))), 'utf-8') else: # raise Exception("Error getting certificate: " + str(order.error)) self.logger.error( 'CAhandler.enroll: Error getting certificate: {0}'. format(order.error)) error = 'Error getting certificate: {0}'.format( order.error) else: self.logger.error( 'CAhandler.enroll: Bad ACME account: {0}'.format( regr.body.error)) error = 'Bad ACME account: {0}'.format(regr.body.error) # raise Exception("Bad ACME account: " + str(regr.body.error)) except Exception as err: self.logger.error('CAhandler.enroll: error: {0}'.format(err)) error = str(err) finally: del user_key else: error = 'CSR rejected. Either CN or SANs are not allowed by policy' self.logger.error( 'CAhandler.enroll: CSR rejected. Either CN or SANs are not allowed by policy.' ) self.logger.debug('Certificate.enroll() ended') return (error, cert_bundle, cert_raw, poll_indentifier)
def enroll(self, csr): """ enroll certificate """ self.logger.debug('CAhandler.enroll()') cert_bundle = None cert_raw = None error = self._config_check() if not error: try: # check CN and SAN against black/whitlist (result, enforce_cn) = self._csr_check(csr) if result: # prepare the CSR csr = build_pem_file(self.logger, None, b64_url_recode(self.logger, csr), None, True) # load ca cert and key (ca_key, ca_cert) = self._ca_load() # load certificate_profile (if applicable) if self.openssl_conf: cert_extension_dic = self._certificate_extensions_load() else: cert_extension_dic = [] # creating a rest form CSR req = crypto.load_certificate_request(crypto.FILETYPE_PEM, csr) subject = req.get_subject() if self.cn_enforce and enforce_cn: self.logger.info('CAhandler.enroll(): overwrite CN with {0}'.format(enforce_cn)) setattr(subject, 'CN', enforce_cn) # sign csr cert = crypto.X509() cert.gmtime_adj_notBefore(0) cert.gmtime_adj_notAfter(self.cert_validity_days * 86400) cert.set_issuer(ca_cert.get_subject()) cert.set_subject(subject) cert.set_pubkey(req.get_pubkey()) cert.set_serial_number(uuid.uuid4().int) cert.set_version(2) cert.add_extensions(req.get_extensions()) default_extension_list = [ crypto.X509Extension(convert_string_to_byte('subjectKeyIdentifier'), False, convert_string_to_byte('hash'), subject=cert), crypto.X509Extension(convert_string_to_byte('authorityKeyIdentifier'), False, convert_string_to_byte('keyid:always'), issuer=ca_cert), crypto.X509Extension(convert_string_to_byte('basicConstraints'), True, convert_string_to_byte('CA:FALSE')), crypto.X509Extension(convert_string_to_byte('extendedKeyUsage'), False, convert_string_to_byte('clientAuth,serverAuth')), ] if cert_extension_dic: try: cert.add_extensions(self._certificate_extensions_add(cert_extension_dic, cert, ca_cert)) except BaseException as err_: self.logger.error('CAhandler.enroll() error while loading extensions form file. Use default set.\nerror: {0}'.format(err_)) cert.add_extensions(default_extension_list) else: # add keyUsage if it does not exist in CSR ku_is_in = False for ext in req.get_extensions(): if convert_byte_to_string(ext.get_short_name()) == 'keyUsage': ku_is_in = True if not ku_is_in: default_extension_list.append(crypto.X509Extension(convert_string_to_byte('keyUsage'), True, convert_string_to_byte('digitalSignature,keyEncipherment'))) # add default extensions cert.add_extensions(default_extension_list) cert.sign(ca_key, 'sha256') # store certifiate self._certificate_store(cert) # create bundle and raw cert cert_bundle = self._pemcertchain_generate(convert_byte_to_string(crypto.dump_certificate(crypto.FILETYPE_PEM, cert)), open(self.issuer_dict['issuing_ca_cert']).read()) cert_raw = convert_byte_to_string(base64.b64encode(crypto.dump_certificate(crypto.FILETYPE_ASN1, cert))) else: error = 'urn:ietf:params:acme:badCSR' except BaseException as err: self.logger.error('CAhandler.enroll() error: {0}'.format(err)) error = 'Unknown exception' self.logger.debug('CAhandler.enroll() ended') return(error, cert_bundle, cert_raw, None)
def enroll(self, csr): """ enroll certificate from NCLM """ self.logger.debug('CAhandler.enroll()') cert_bundle = None error = None cert_raw = None # recode csr csr = b64_url_recode(self.logger, csr) if not self.error: if self.tsg_info_dic['id']: ca_id = self._ca_id_lookup() if ca_id and self.template_info_dic[ 'name'] and not self.template_info_dic['id']: self._template_id_lookup() # get common name of CSR csr_cn = csr_cn_get(self.logger, csr) csr_san_list = csr_san_get(self.logger, csr) # import csr to NCLM self._request_import(csr) # lookup csr id csr_id = self._csr_id_lookup(csr_cn, csr_san_list) if ca_id and csr_id and self.tsg_info_dic['id']: data_dic = { "targetSystemGroupID": self.tsg_info_dic['id'], "caID": ca_id, "requestID": csr_id } # add template if correctly configured if 'id' in self.template_info_dic and self.template_info_dic[ 'id']: data_dic['templateID'] = self.template_info_dic['id'] self._api_post( self.api_host + '/targetsystemgroups/' + str(self.tsg_info_dic['id']) + '/enroll/ca/' + str(ca_id), data_dic) # wait for certificate enrollment to get finished time.sleep(self.wait_interval) cert_id = self._cert_id_lookup(csr_cn, csr_san_list) if cert_id: (error, cert_bundle, cert_raw) = self._cert_bundle_build(cert_id) else: error = 'certifcate id lookup failed for: {0}, {1}'.format( csr_cn, csr_san_list) self.logger.error( 'CAhandler.eroll(): certifcate id lookup failed for: {0}, {1}' .format(csr_cn, csr_san_list)) else: error = 'enrollment aborted. ca_id: {0}, csr_id: {1}, tsg_id: {2}'.format( ca_id, csr_id, self.tsg_info_dic['id']) self.logger.error( 'CAhandler.eroll(): enrollment aborted. ca_id: {0}, csr_id: {1}, tsg_id: {2}' .format(ca_id, csr_id, self.tsg_info_dic['id'])) else: error = 'CAhandler.eroll(): ID lookup for targetSystemGroup "{0}" failed.'.format( self.tsg_info_dic['name']) else: self.logger.error(self.error) self.logger.debug('CAhandler.enroll() ended') return (error, cert_bundle, cert_raw, None)
def enroll(self, csr): """ enroll certificate """ # pylint: disable=R0914 self.logger.debug('CAhandler.enroll()') cert_bundle = None cert_raw = None error = self._config_check() if not error: request_name = self._requestname_get(csr) if request_name: # import CSR to database _csr_info = self._csr_import(csr, request_name) # prepare the CSR to be signed csr = build_pem_file(self.logger, None, b64_url_recode(self.logger, csr), None, True) # load ca cert and key (ca_key, ca_cert, ca_id) = self._ca_load() if ca_key and ca_cert and ca_id: # load request req = crypto.load_certificate_request(crypto.FILETYPE_PEM, csr) # copy cn of request subject = req.get_subject() # rewrite CN if required if not subject.CN: self.logger.info('rewrite CN to {0}'.format(request_name)) subject.CN = request_name # create certificate object cert = crypto.X509() cert.set_pubkey(req.get_pubkey()) cert.set_version(2) cert.set_serial_number(uuid.uuid4().int & (1<<63)-1) cert.set_issuer(ca_cert.get_subject()) # load template if configured if self.template_name: (dn_dic, template_dic) = self._template_load() else: dn_dic = {} template_dic = {} # set cert_validity if 'validity' in template_dic: self.logger.info('take validity from template: {0}'.format(template_dic['validity'])) # take validity from template cert_validity = template_dic['validity'] else: cert_validity = self.cert_validity_days cert.gmtime_adj_notBefore(0) cert.gmtime_adj_notAfter(cert_validity * 86400) # get extension list from CSR csr_extensions_list = req.get_extensions() extension_list = self._extension_list_generate(template_dic, cert, ca_cert, csr_extensions_list) # add extensions (copy from CSR and take the ones we constructed) # cert.add_extensions(csr_extensions_list) cert.add_extensions(extension_list) if dn_dic: self.logger.info('modify subject with template data') subject = self._subject_modify(subject, dn_dic) cert.set_subject(subject) # sign csr cert.sign(ca_key, 'sha256') serial = cert.get_serial_number() # get hsshes issuer_hash = ca_cert.subject_name_hash() & 0x7fffffff name_hash = cert.subject_name_hash() & 0x7fffffff # store certificate self._store_cert(ca_id, request_name, '{:X}'.format(serial), convert_byte_to_string(b64_encode(self.logger, crypto.dump_certificate(crypto.FILETYPE_ASN1, cert))), name_hash, issuer_hash) cert_bundle = self._pemcertchain_generate(convert_byte_to_string(crypto.dump_certificate(crypto.FILETYPE_PEM, cert)), convert_byte_to_string(crypto.dump_certificate(crypto.FILETYPE_PEM, ca_cert))) cert_raw = convert_byte_to_string(b64_encode(self.logger, crypto.dump_certificate(crypto.FILETYPE_ASN1, cert))) else: error = 'ca lookup failed' else: error = 'request_name lookup failed' self.logger.debug('Certificate.enroll() ended') return(error, cert_bundle, cert_raw, None)
def enroll(self, csr): """ enroll certificate """ self.logger.debug('CAhandler.enroll()') csr_pem = '-----BEGIN CERTIFICATE REQUEST-----\n{0}\n-----END CERTIFICATE REQUEST-----\n'.format(textwrap.fill(str(b64_url_recode(self.logger, csr)), 64)) cert_bundle = None error = None cert_raw = None poll_indentifier = None key = None try: self.logger.debug('CAhandler.enroll() opening key') with open(self.keyfile, "r") as keyf: key = josepy.JWKRSA.json_loads(keyf.read()) net = client.ClientNetwork(key) directory = messages.Directory.from_json(net.get(self.url).json()) acmeclient = client.ClientV2(directory, net=net) reg = messages.Registration.from_data(key=key, terms_of_service_agreed=True) regr = messages.RegistrationResource(uri="{}/account/{}".format(self.url, self.account), body=reg) self.logger.debug('CAhandler.enroll() checking remote registration status') regr = acmeclient.query_registration(regr) if regr.body.status != "valid": raise Exception("Bad ACME account: " + str(regr.body.error)) self.logger.debug('CAhandler.enroll() issuing signing order') self.logger.debug('CAhandler.enroll() CSR: ' + str(csr_pem)) order = acmeclient.new_order(csr_pem) self.logger.debug('CAhandler.enroll() polling for certificate') order = acmeclient.poll_and_finalize(order) if not order.fullchain_pem: raise Exception("Error getting certificate: " + str(order.error)) self.logger.debug('CAhandler.enroll() successful') cert_bundle = str(order.fullchain_pem) cert_raw = str(base64.b64encode(crypto.dump_certificate(crypto.FILETYPE_ASN1, crypto.load_certificate(crypto.FILETYPE_PEM, cert_bundle))), 'utf-8') except Exception as e: self.logger.error(str(e)) error = str(e) finally: del key self.logger.debug('Certificate.enroll() ended') return(error, cert_bundle, cert_raw, poll_indentifier)
def revoke(self, _cert, _rev_reason, _rev_date): """ revoke certificate """ self.logger.debug('CAhandler.revoke()') certpem = '-----BEGIN CERTIFICATE-----\n{0}\n-----END CERTIFICATE-----\n'.format(textwrap.fill(str(b64_url_recode(self.logger, _cert)), 64)) cert = josepy.ComparableX509(crypto.load_certificate(crypto.FILETYPE_PEM, certpem)) code = 200 message = None detail = None try: self.logger.debug('CAhandler.revoke() opening key') with open(self.keyfile, "r") as keyf: key = josepy.JWKRSA.json_loads(keyf.read()) net = client.ClientNetwork(key) directory = messages.Directory.from_json(net.get(self.url).json()) acmeclient = client.ClientV2(directory, net=net) reg = messages.Registration.from_data(key=key, terms_of_service_agreed=True) regr = messages.RegistrationResource(uri="{}/account/{}".format(self.url, self.account), body=reg) self.logger.debug('CAhandler.revoke() checking remote registration status') regr = acmeclient.query_registration(regr) if regr.body.status != "valid": raise Exception("Bad ACME account: " + str(regr.body.error)) self.logger.debug('CAhandler.revoke() issuing revocation order') acmeclient.revoke(cert, 1) self.logger.debug('CAhandler.revoke() successfull') except Exception as e: self.logger.error(str(e)) code = 500 message = 'urn:ietf:params:acme:error:serverInternal' detail = str(e) finally: del key self.logger.debug('Certificate.revoke() ended') return(code, message, detail)