def public_certificate(body): """ Determines if specified string is valid public certificate. :param body: :return: """ try: parse_certificate(body) except Exception: raise ValidationError('Public certificate presented is not valid.')
def test_convert_pkcs7_bytes_to_pem(): from lemur.common.utils import convert_pkcs7_bytes_to_pem from lemur.common.utils import parse_certificate cert_chain = convert_pkcs7_bytes_to_pem(CERT_CHAIN_PKCS7_PEM) assert (len(cert_chain) == 3) leaf = cert_chain[1] root = cert_chain[2] assert (parse_certificate("\n".join( str(root).splitlines())) == ROOTCA_CERT) assert (parse_certificate("\n".join( str(leaf).splitlines())) == INTERMEDIATE_CERT)
def upload(pending_certificate_id, **kwargs): """ Uploads a (signed) pending certificate. The allowed fields are validated by PendingCertificateUploadInputSchema. The certificate is also validated to be signed by the correct authoritity. """ pending_cert = get(pending_certificate_id) partial_cert = kwargs uploaded_chain = partial_cert['chain'] authority = authorities_service.get(pending_cert.authority.id) # Construct the chain for cert validation if uploaded_chain: chain = uploaded_chain + '\n' + authority.authority_certificate.body else: chain = authority.authority_certificate.body parsed_chain = parse_cert_chain(chain) # Check that the certificate is actually signed by the CA to avoid incorrect cert pasting validators.verify_cert_chain([parse_certificate(partial_cert['body'])] + parsed_chain) final_cert = create_certificate(pending_cert, partial_cert, pending_cert.user) update( pending_cert.id, resolved=True ) pending_cert_final_result = update( pending_cert.id, resolved_cert_id=final_cert.id ) return pending_cert_final_result
def verify(cert_path, issuer_chain_path): """ Verify a certificate using OCSP and CRL :param cert_path: :param issuer_chain_path: :return: True if valid, False otherwise """ with open(cert_path, 'rt') as c: try: cert = parse_certificate(c.read()) except ValueError as e: current_app.logger.error(e) return None # OCSP is our main source of truth, in a lot of cases CRLs # have been deprecated and are no longer updated verify_result = ocsp_verify(cert, cert_path, issuer_chain_path) if verify_result is None: verify_result = crl_verify(cert, cert_path) if verify_result is None: current_app.logger.debug("Failed to verify {}".format(cert.serial_number)) return verify_result
def verify(cert_path, issuer_chain_path): """ Verify a certificate using OCSP and CRL :param cert_path: :param issuer_chain_path: :return: True if valid, False otherwise """ with open(cert_path, 'rt') as c: try: cert = parse_certificate(c.read()) except ValueError as e: current_app.logger.error(e) return None # OCSP is our main source of truth, in a lot of cases CRLs # have been deprecated and are no longer updated verify_result = ocsp_verify(cert, cert_path, issuer_chain_path) if verify_result is None: verify_result = crl_verify(cert, cert_path) if verify_result is None: current_app.logger.debug("Failed to verify {}".format( cert.serial_number)) return verify_result
def upload(self, name, body, private_key, cert_chain, options, **kwargs): try: k8_base_uri = self.get_option('kubernetesURL', options) secret_format = self.get_option('secretFormat', options) k8s_api = K8sSession( self.k8s_bearer(options), self.k8s_cert(options) ) cn = common_name(parse_certificate(body)) secret_name_format = self.get_option('secretNameFormat', options) secret_name = secret_name_format.format(common_name=cn) secret = build_secret(secret_format, secret_name, body, private_key, cert_chain) err = ensure_resource( k8s_api, k8s_base_uri=k8_base_uri, namespace=self.k8s_namespace(options), kind="secret", name=secret_name, data=secret ) except Exception as e: current_app.logger.exception("Exception in upload: {}".format(e), exc_info=True) raise if err is not None: current_app.logger.error("Error deploying resource: %s", err) raise Exception("Error uploading secret: " + err)
def upload(self, name, body, private_key, cert_chain, options, **kwargs): try: k8_base_uri = self.get_option("kubernetesURL", options) secret_format = self.get_option("secretFormat", options) k8s_api = K8sSession(self.k8s_bearer(options), self.k8s_cert(options)) cn = common_name(parse_certificate(body)) secret_name_format = self.get_option("secretNameFormat", options) secret_name = secret_name_format.format(common_name=cn) secret = build_secret(secret_format, secret_name, body, private_key, cert_chain) err = ensure_resource( k8s_api, k8s_base_uri=k8_base_uri, namespace=self.k8s_namespace(options), kind="secret", name=secret_name, data=secret, ) except Exception as e: current_app.logger.exception("Exception in upload: {}".format(e), exc_info=True) raise if err is not None: current_app.logger.error("Error deploying resource: %s", err) raise Exception("Error uploading secret: " + err)
def crl_verify(cert_path): """ Attempts to verify a certificate using CRL. :param cert_path: :return: True if certificate is valid, False otherwise :raise Exception: If certificate does not have CRL """ with open(cert_path, 'rt') as c: cert = parse_certificate(c.read()) distribution_points = cert.extensions.get_extension_for_oid( x509.OID_CRL_DISTRIBUTION_POINTS).value for p in distribution_points: point = p.full_name[0].value try: response = requests.get(point) if response.status_code != 200: raise Exception("Unable to retrieve CRL: {0}".format(point)) except ConnectionError: raise Exception("Unable to retrieve CRL: {0}".format(point)) crl = x509.load_der_x509_crl(response.content, backend=default_backend()) for r in crl: if cert.serial == r.serial_number: return return True
def create_certificate(self, csr, issuer_options): """ Creates a CFSSL certificate. :param csr: :param issuer_options: :return: """ current_app.logger.info("Requesting a new cfssl certificate with csr: {0}".format(csr)) url = "{0}{1}".format(current_app.config.get('CFSSL_URL'), '/api/v1/cfssl/sign') data = {'certificate_request': csr} data = json.dumps(data) response = self.session.post(url, data=data.encode(encoding='utf_8', errors='strict')) if response.status_code > 399: metrics.send('cfssl_create_certificate_failure', 'counter', 1) raise Exception( "Error creating cert. Please check your CFSSL API server") response_json = json.loads(response.content.decode('utf_8')) cert = response_json['result']['certificate'] parsed_cert = parse_certificate(cert) metrics.send('cfssl_create_certificate_success', 'counter', 1) return cert, current_app.config.get('CFSSL_INTERMEDIATE'), parsed_cert.serial_number
def upload(pending_certificate_id, **kwargs): """ Uploads a (signed) pending certificate. The allowed fields are validated by PendingCertificateUploadInputSchema. The certificate is also validated to be signed by the correct authority. """ pending_cert = get(pending_certificate_id) partial_cert = kwargs uploaded_chain = partial_cert["chain"] authority = authorities_service.get(pending_cert.authority.id) # Construct the chain for cert validation if uploaded_chain: chain = uploaded_chain + "\n" + authority.authority_certificate.body else: chain = authority.authority_certificate.body parsed_chain = parse_cert_chain(chain) # Check that the certificate is actually signed by the CA to avoid incorrect cert pasting validators.verify_cert_chain([parse_certificate(partial_cert["body"])] + parsed_chain) final_cert = create_certificate(pending_cert, partial_cert, pending_cert.user) pending_cert_final_result = update(pending_cert.id, resolved_cert_id=final_cert.id) update(pending_cert.id, resolved=True) log_service.audit_log("resolve_pending_certificate", pending_cert.name, "Resolved the pending certificate") return pending_cert_final_result
def crl_verify(cert_path): """ Attempts to verify a certificate using CRL. :param cert_path: :return: True if certificate is valid, False otherwise :raise Exception: If certificate does not have CRL """ with open(cert_path, 'rt') as c: cert = parse_certificate(c.read()) distribution_points = cert.extensions.get_extension_for_oid(x509.OID_CRL_DISTRIBUTION_POINTS).value for p in distribution_points: point = p.full_name[0].value try: response = requests.get(point) if response.status_code != 200: raise Exception("Unable to retrieve CRL: {0}".format(point)) except ConnectionError: raise Exception("Unable to retrieve CRL: {0}".format(point)) crl = x509.load_der_x509_crl(response.content, backend=default_backend()) for r in crl: if cert.serial == r.serial_number: return return True
def validate_cert_private_key_chain(self, data): cert = None key = None if data.get('body'): try: cert = utils.parse_certificate(data['body']) except ValueError: raise ValidationError("Public certificate presented is not valid.", field_names=['body']) if data.get('private_key'): try: key = utils.parse_private_key(data['private_key']) except ValueError: raise ValidationError("Private key presented is not valid.", field_names=['private_key']) if cert and key: # Throws ValidationError validators.verify_private_key_match(key, cert) if data.get('chain'): try: chain = utils.parse_cert_chain(data['chain']) except ValueError: raise ValidationError("Invalid certificate in certificate chain.", field_names=['chain']) # Throws ValidationError validators.verify_cert_chain([cert] + chain)
def validate_cert_private_key_chain(self, data): cert = None key = None if data.get("body"): try: cert = utils.parse_certificate(data["body"]) except ValueError: raise ValidationError( "Public certificate presented is not valid.", field_names=["body"]) if data.get("private_key"): try: key = utils.parse_private_key(data["private_key"]) except ValueError: raise ValidationError("Private key presented is not valid.", field_names=["private_key"]) if cert and key: # Throws ValidationError validators.verify_private_key_match(key, cert) if data.get("chain"): try: chain = utils.parse_cert_chain(data["chain"]) except ValueError: raise ValidationError( "Invalid certificate in certificate chain.", field_names=["chain"]) # Throws ValidationError validators.verify_cert_chain([cert] + chain)
def cert_chain_as_der(cert, chain): """Return a certificate and its chain in a list format, as expected by pyjks.""" certs = [parse_certificate(cert)] certs.extend(parse_cert_chain(chain)) # certs (list) – A list of certificates, as byte strings. The first one should be the one belonging to the private # key, the others the chain (in correct order). return [cert.public_bytes(encoding=serialization.Encoding.DER) for cert in certs]
def cert_chain_as_der(cert, chain): """Return a certificate and its chain in a list format, as expected by pyjks.""" certs = [parse_certificate(cert)] certs.extend(parse_cert_chain(chain)) # certs (list) – A list of certificates, as byte strings. The first one should be the one belonging to the private # key, the others the chain (in correct order). return [ cert.public_bytes(encoding=serialization.Encoding.DER) for cert in certs ]
def create_certificate(self, csr, issuer_options): """ Creates a CFSSL certificate. :param csr: :param issuer_options: :return: """ current_app.logger.info( "Requesting a new cfssl certificate with csr: {0}".format(csr)) url = "{0}{1}".format(current_app.config.get("CFSSL_URL"), "/api/v1/cfssl/sign") data = {"certificate_request": csr} data = json.dumps(data) try: hex_key = current_app.config.get("CFSSL_KEY") key = bytes.fromhex(hex_key) except (ValueError, NameError, TypeError): # unable to find CFSSL_KEY in config, continue using normal sign method pass else: data = data.encode() token = base64.b64encode( hmac.new(key, data, digestmod=hashlib.sha256).digest()) data = base64.b64encode(data) data = json.dumps({ "token": token.decode("utf-8"), "request": data.decode("utf-8") }) url = "{0}{1}".format(current_app.config.get("CFSSL_URL"), "/api/v1/cfssl/authsign") response = self.session.post(url, data=data.encode(encoding="utf_8", errors="strict")) if response.status_code > 399: metrics.send("cfssl_create_certificate_failure", "counter", 1) raise Exception( "Error creating cert. Please check your CFSSL API server") response_json = json.loads(response.content.decode("utf_8")) cert = response_json["result"]["certificate"] parsed_cert = parse_certificate(cert) metrics.send("cfssl_create_certificate_success", "counter", 1) return ( cert, current_app.config.get("CFSSL_INTERMEDIATE"), parsed_cert.serial_number, )
def upload(self, name, body, private_key, cert_chain, options, **kwargs): """ Upload certificate and private key :param private_key: :param cert_chain: :return: """ # we use the common name to identify the certificate # Azure does not allow "." in the certificate name we replace them with "-" cert = parse_certificate(body) certificate_name = common_name(cert).replace(".", "-") vault_URI = self.get_option("vaultUrl", options) tenant = self.get_option("azureTenant", options) app_id = self.get_option("appID", options) password = self.get_option("azurePassword", options) access_token = get_access_token(tenant, app_id, password, self) cert_url = f"{vault_URI}/certificates/{certificate_name}/import?api-version=7.1" post_header = {"Authorization": f"Bearer {access_token}"} key_pkcs8 = parse_private_key(private_key).private_bytes( encoding=serialization.Encoding.PEM, format=serialization.PrivateFormat.PKCS8, encryption_algorithm=serialization.NoEncryption(), ) key_pkcs8 = key_pkcs8.decode("utf-8").replace('\\n', '\n') cert_package = f"{body}\n{key_pkcs8}" post_body = { "value": cert_package, "policy": { "key_props": { "exportable": True, "kty": "RSA", "key_size": bitstrength(cert), "reuse_key": True }, "secret_props": { "contentType": "application/x-pem-file" } } } try: response = self.session.post(cert_url, headers=post_header, json=post_body) except requests.exceptions.RequestException as e: current_app.logger.exception(f"AZURE: Error for POST {e}") return_value = handle_response(response)
def upload(self, name, body, private_key, cert_chain, options, **kwargs): """ Upload certificate and private key :param private_key: :param cert_chain: :return: """ cname = common_name(parse_certificate(body)) url = self.get_option('vaultUrl', options) token_file = self.get_option('vaultAuthTokenFile', options) mount = self.get_option('vaultMount', options) path = self.get_option('vaultPath', options) bundle = self.get_option('bundleChain', options) obj_name = self.get_option('objectName', options) api_version = self.get_option('vaultKvApiVersion', options) with open(token_file, 'r') as file: token = file.readline().rstrip('\n') client = hvac.Client(url=url, token=token) client.secrets.kv.default_kv_version = api_version if obj_name: path = '{0}/{1}'.format(path, obj_name) else: path = '{0}/{1}'.format(path, cname) secret = get_secret(client, mount, path) secret['data'][cname] = {} if bundle == 'Nginx' and cert_chain: secret['data'][cname]['crt'] = '{0}\n{1}'.format(body, cert_chain) elif bundle == 'Apache' and cert_chain: secret['data'][cname]['crt'] = body secret['data'][cname]['chain'] = cert_chain else: secret['data'][cname]['crt'] = body secret['data'][cname]['key'] = private_key san_list = get_san_list(body) if isinstance(san_list, list): secret['data'][cname]['san'] = san_list try: client.secrets.kv.create_or_update_secret(path=path, mount_point=mount, secret=secret['data']) except ConnectionError as err: current_app.logger.exception( "Exception uploading secret to vault: {0}".format(err), exc_info=True)
def sync_certificates(source, user): new, updated = 0, 0 current_app.logger.debug("Retrieving certificates from {0}".format( source.label)) s = plugins.get(source.plugin_name) certificates = s.get_certificates(source.options) for certificate in certificates: exists = False if certificate.get("search", None): conditions = certificate.pop("search") exists = certificate_service.get_by_attributes(conditions) if not exists and certificate.get("name"): result = certificate_service.get_by_name(certificate["name"]) if result: exists = [result] if not exists and certificate.get("serial"): exists = certificate_service.get_by_serial(certificate["serial"]) if not exists: cert = parse_certificate(certificate["body"]) matching_serials = certificate_service.get_by_serial(serial(cert)) exists = find_matching_certificates_by_hash(cert, matching_serials) if not certificate.get("owner"): certificate["owner"] = user.email certificate["creator"] = user exists = [x for x in exists if x] if not exists: certificate_create(certificate, source) new += 1 else: for e in exists: if certificate.get("external_id"): e.external_id = certificate["external_id"] if certificate.get("authority_id"): e.authority_id = certificate["authority_id"] certificate_update(e, source) updated += 1 return new, updated
def sync_certificates(source, user): new, updated = 0, 0 current_app.logger.debug("Retrieving certificates from {0}".format(source.label)) s = plugins.get(source.plugin_name) certificates = s.get_certificates(source.options) for certificate in certificates: exists = False if certificate.get('search', None): conditions = certificate.pop('search') exists = certificate_service.get_by_attributes(conditions) if not exists and certificate.get('name'): result = certificate_service.get_by_name(certificate['name']) if result: exists = [result] if not exists and certificate.get('serial'): exists = certificate_service.get_by_serial(certificate['serial']) if not exists: cert = parse_certificate(certificate['body']) matching_serials = certificate_service.get_by_serial(serial(cert)) exists = find_matching_certificates_by_hash(cert, matching_serials) if not certificate.get('owner'): certificate['owner'] = user.email certificate['creator'] = user exists = [x for x in exists if x] if not exists: certificate_create(certificate, source) new += 1 else: for e in exists: if certificate.get('external_id'): e.external_id = certificate['external_id'] if certificate.get('authority_id'): e.authority_id = certificate['authority_id'] certificate_update(e, source) updated += 1 return new, updated
def crl_verify(cert_path): """ Attempts to verify a certificate using CRL. :param cert_path: :return: True if certificate is valid, False otherwise :raise Exception: If certificate does not have CRL """ with open(cert_path, 'rt') as c: cert = parse_certificate(c.read()) distribution_points = cert.extensions.get_extension_for_oid( x509.OID_CRL_DISTRIBUTION_POINTS).value for p in distribution_points: point = p.full_name[0].value try: response = requests.get(point) if response.status_code != 200: raise Exception("Unable to retrieve CRL: {0}".format(point)) except InvalidSchema: # Unhandled URI scheme (like ldap://); skip this distribution point. continue except ConnectionError: raise Exception("Unable to retrieve CRL: {0}".format(point)) crl = x509.load_der_x509_crl(response.content, backend=default_backend()) for r in crl: if cert.serial == r.serial_number: try: reason = r.extensions.get_extension_for_class( x509.CRLReason).value # Handle "removeFromCRL" revoke reason as unrevoked; continue with the next distribution point. # Per RFC 5280 section 6.3.3 (k): https://tools.ietf.org/html/rfc5280#section-6.3.3 if reason == x509.ReasonFlags.remove_from_crl: break except x509.ExtensionNotFound: pass return return True
def upload(self, name, body, private_key, cert_chain, options, **kwargs): """ Upload certificate and private key :param private_key: :param cert_chain: :return: """ priv_id = None cert_id = None name_found = False cname = common_name(parse_certificate(body)) priv_keys = get_all_private_keys() cert_keys = get_all_certificates() log_data = { "function": inspect.currentframe().f_code.co_name } for each in priv_keys: if each['name'] == cname: name_found = True if each['sha1'] == get_public_key_sha1(private_key): priv_id = None break else: priv_id = each['id'] for cert in cert_keys: if cert['name'] == cname: cert_id = cert['id'] if priv_id or not name_found: post_private_key(private_key, name=cname) time.sleep(2) new_cert_id = post_certificate(body, cert_chain, name=cname) log_data["message"] = f"Certificate updated: {cname}" act_id = get_activation(cert_id) if len(act_id) > 0 and new_cert_id: for each in act_id: patch_activation(each, new_cert_id) log_data["message"] = f"Certificate updated: {new_cert_id} activated: {act_id}" if cert_id: delete_certificate(cert_id) if priv_id: delete_private_key(priv_id) else: log_data["message"] = f"Certificate up to data, no changes made" current_app.logger.debug(log_data)
def export(self, body, chain, key, options, **kwargs): """ Generates a Java Keystore """ if self.get_option('passphrase', options): passphrase = self.get_option('passphrase', options) else: passphrase = Fernet.generate_key().decode('utf-8') if self.get_option('alias', options): alias = self.get_option('alias', options) else: alias = common_name(parse_certificate(body)) raw = create_keystore(body, chain, key, alias, passphrase) return 'jks', passphrase, raw
def export(self, body, chain, key, options, **kwargs): """ Generates a Java Truststore """ if self.get_option("alias", options): alias = self.get_option("alias", options) else: alias = common_name(parse_certificate(body)) if self.get_option("passphrase", options): passphrase = self.get_option("passphrase", options) else: passphrase = Fernet.generate_key().decode("utf-8") raw = create_truststore(body, chain, alias, passphrase) return "jks", passphrase, raw
def export(self, body, chain, key, options, **kwargs): """ Generates a Java Truststore """ if self.get_option('alias', options): alias = self.get_option('alias', options) else: alias = common_name(parse_certificate(body)) if self.get_option('passphrase', options): passphrase = self.get_option('passphrase', options) else: passphrase = Fernet.generate_key().decode('utf-8') raw = create_truststore(body, chain, alias, passphrase) return 'jks', passphrase, raw
def find_cert(certificate): if not certificate.get("name"): # certificate must have a name return False, 0 matched_cert = certificate_service.get_by_name(certificate["name"]) if not matched_cert: # no cert with the same name found return False, 0 # check hash of matched cert cert = parse_certificate(certificate["body"]) exists = find_matching_certificates_by_hash(cert, [matched_cert]) if not exists: raise Exception( f"A certificate with the same name {certificate['name']} already exists with a different hash" ) return exists, len(exists)
def sync_certificates(source, user): new, updated = 0, 0 current_app.logger.debug("Retrieving certificates from {0}".format( source.label)) s = plugins.get(source.plugin_name) certificates = s.get_certificates(source.options) for certificate in certificates: exists = False if certificate.get('name'): result = certificate_service.get_by_name(certificate['name']) if result: exists = [result] if not exists and certificate.get('serial'): exists = certificate_service.get_by_serial(certificate['serial']) if not exists: cert = parse_certificate(certificate['body']) exists = certificate_service.get_by_serial(serial(cert)) if not certificate.get('owner'): certificate['owner'] = user.email certificate['creator'] = user exists = [x for x in exists if x] if not exists: certificate_create(certificate, source) new += 1 else: for e in exists: if certificate.get('external_id'): e.external_id = certificate['external_id'] if certificate.get('authority_id'): e.authority_id = certificate['authority_id'] certificate_update(e, source) updated += 1 return new, updated
def validate_cert_chain(self, data): cert = None if data.get('body'): try: cert = utils.parse_certificate(data['body']) except ValueError: raise ValidationError( "Public certificate presented is not valid.", field_names=['body']) if data.get('chain'): try: chain = utils.parse_cert_chain(data['chain']) except ValueError: raise ValidationError( "Invalid certificate in certificate chain.", field_names=['chain']) # Throws ValidationError validators.verify_cert_chain([cert] + chain)
def validate_cert_private_key(self, data): cert = None key = None if data.get('body'): try: cert = utils.parse_certificate(data['body']) except ValueError: raise ValidationError( "Public certificate presented is not valid.", field_names=['body']) if data.get('private_key'): try: key = utils.parse_private_key(data['private_key']) except ValueError: raise ValidationError("Private key presented is not valid.", field_names=['private_key']) if cert and key: # Throws ValidationError validators.verify_private_key_match(key, cert)
def upload(self, name, body, private_key, cert_chain, options, **kwargs): current_app.logger.debug("SFTP destination plugin is started") cn = common_name(parse_certificate(body)) dst_path = self.get_option("destinationPath", options) dst_path_cn = dst_path + "/" + cn export_format = self.get_option("exportFormat", options) # prepare files for upload files = {cn + ".key": private_key, cn + ".pem": body} if cert_chain: if export_format == "NGINX": # assemble body + chain in the single file files[cn + ".pem"] += "\n" + cert_chain elif export_format == "Apache": # store chain in the separate file files[cn + ".ca.bundle.pem"] = cert_chain self.upload_file(dst_path_cn, files, options)
def export(self, body, chain, key, options, **kwargs): """ Generates a PKCS#12 archive. :param key: :param chain: :param body: :param options: :param kwargs: """ if self.get_option('passphrase', options): passphrase = self.get_option('passphrase', options) else: passphrase = get_psuedo_random_string() if self.get_option('alias', options): alias = self.get_option('alias', options) else: alias = common_name(parse_certificate(body)) type = self.get_option('type', options) with mktemppath() as output_tmp: if type == 'PKCS12 (.p12)': if not key: raise Exception("Private Key required by {0}".format(type)) create_pkcs12(body, chain, output_tmp, key, alias, passphrase) extension = "p12" else: raise Exception( "Unable to export, unsupported type: {0}".format(type)) with open(output_tmp, 'rb') as f: raw = f.read() return extension, passphrase, raw
def find_cert(certificate): updated_by_hash = 0 exists = False if certificate.get("search", None): conditions = certificate.pop("search") exists = certificate_service.get_by_attributes(conditions) if not exists and certificate.get("name"): result = certificate_service.get_by_name(certificate["name"]) if result: exists = [result] if not exists and certificate.get("serial"): exists = certificate_service.get_by_serial(certificate["serial"]) if not exists: cert = parse_certificate(certificate["body"]) matching_serials = certificate_service.get_by_serial(serial(cert)) exists = find_matching_certificates_by_hash(cert, matching_serials) updated_by_hash += 1 exists = [x for x in exists if x] return exists, updated_by_hash
def upload(self, name, body, private_key, cert_chain, options, **kwargs): current_app.logger.debug('SFTP destination plugin is started') cn = common_name(parse_certificate(body)) host = self.get_option('host', options) port = self.get_option('port', options) user = self.get_option('user', options) password = self.get_option('password', options) ssh_priv_key = self.get_option('privateKeyPath', options) ssh_priv_key_pass = self.get_option('privateKeyPass', options) dst_path = self.get_option('destinationPath', options) export_format = self.get_option('exportFormat', options) # prepare files for upload files = {cn + '.key': private_key, cn + '.pem': body} if cert_chain: if export_format == 'NGINX': # assemble body + chain in the single file files[cn + '.pem'] += '\n' + cert_chain elif export_format == 'Apache': # store chain in the separate file files[cn + '.ca.bundle.pem'] = cert_chain # upload files try: current_app.logger.debug('Connecting to {0}@{1}:{2}'.format(user, host, port)) ssh = paramiko.SSHClient() # allow connection to the new unknown host ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) # open the ssh connection if password: current_app.logger.debug('Using password') ssh.connect(host, username=user, port=port, password=password) elif ssh_priv_key: current_app.logger.debug('Using RSA private key') pkey = paramiko.RSAKey.from_private_key_file(ssh_priv_key, ssh_priv_key_pass) ssh.connect(host, username=user, port=port, pkey=pkey) else: current_app.logger.error("No password or private key provided. Can't proceed") raise paramiko.ssh_exception.AuthenticationException # open the sftp session inside the ssh connection sftp = ssh.open_sftp() # make sure that the destination path exist try: current_app.logger.debug('Creating {0}'.format(dst_path)) sftp.mkdir(dst_path) except IOError: current_app.logger.debug('{0} already exist, resuming'.format(dst_path)) try: dst_path_cn = dst_path + '/' + cn current_app.logger.debug('Creating {0}'.format(dst_path_cn)) sftp.mkdir(dst_path_cn) except IOError: current_app.logger.debug('{0} already exist, resuming'.format(dst_path_cn)) # upload certificate files to the sftp destination for filename, data in files.items(): current_app.logger.debug('Uploading {0} to {1}'.format(filename, dst_path_cn)) with sftp.open(dst_path_cn + '/' + filename, 'w') as f: f.write(data) # read only for owner, -r-------- sftp.chmod(dst_path_cn + '/' + filename, 0o400) ssh.close() except Exception as e: current_app.logger.error('ERROR in {0}: {1}'.format(e.__class__, e)) try: ssh.close() except BaseException: pass
ggEPADCCAQoCggEBAL8laXtLXyM64t5dz2B9q+4VvOsChefBi2PlGudqxDuRN3l0 Kmcfun6x2Gng24pTlGdtmiTEWA0a2F8HRLv4YBWhuYleVeBPtf1fF1/SuYgkJOWT 7S5qk/od/tUOLHS0Y067st3FydnFQTKpAuYveEkxleFrMS8hX8cuEgbER+8ybiXK n4GsyM/om6lsTyBoaLp5yTAoQb4jAWDbiz1xcjPSkvH2lm7rLGtKoylCYwxRsMh2 nZcRr1OXVhYHXwpYHVB/jVAjy7PAWQ316hi6mpPYbBV+yfn2GUfGuytqyoXLEsrM 3iEEAkU0mJjQmYsCDM3r7ONHTM+UFEk47HCZJccCAwEAAaNCMEAwDwYDVR0TAQH/ BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFFL12SFeOTTDdGKsHKoz eByGHY6nMA0GCSqGSIb3DQEBCwUAA4IBAQAJfe0/uAHobkxth38dqrSFmTo+D5/T MlRt3hdgjlah6sD2+/DObCyut/XhQWCgTNWyRi4xTKgLh5KSoeJ9EMkADGEgDkU2 vjBg5FmGZsxg6bqjxehK+2HvASJoTH8r41xmTioav7a2i3wNhaNSntw2QRTQBQED OIzHRpPDQ2quErjA8nSifE2xmAAr3g+FuookTTJuv37s2cS59zRYsg+WC3+TtPpR ssvobJ6Xe2D4cCVjUmsqtFEztMgdqgmlcWyGdUKeXdi7CMoeTb4uO+9qRQq46wYW n7K1z+W0Kp5yhnnPAoOioAP4vjASDx3z3RnLaZvMmcO7YdCIwhE5oGV0 -----END CERTIFICATE----- """ ROOTCA_CERT = parse_certificate(ROOTCA_CERT_STR) ROOTCA_KEY = """\ -----BEGIN RSA PRIVATE KEY----- MIIEowIBAAKCAQEAvyVpe0tfIzri3l3PYH2r7hW86wKF58GLY+Ua52rEO5E3eXQq Zx+6frHYaeDbilOUZ22aJMRYDRrYXwdEu/hgFaG5iV5V4E+1/V8XX9K5iCQk5ZPt LmqT+h3+1Q4sdLRjTruy3cXJ2cVBMqkC5i94STGV4WsxLyFfxy4SBsRH7zJuJcqf gazIz+ibqWxPIGhounnJMChBviMBYNuLPXFyM9KS8faWbussa0qjKUJjDFGwyHad lxGvU5dWFgdfClgdUH+NUCPLs8BZDfXqGLqak9hsFX7J+fYZR8a7K2rKhcsSysze IQQCRTSYmNCZiwIMzevs40dMz5QUSTjscJklxwIDAQABAoIBAAyVzwMiLEpqhyNy 88N7osVTQxQKH3zp3l6eaA4SlocBgbCKeHw/t4y98uzNtEbASAYjTkHbd5ytRs/C 78Cckt75vfiQcIELXoUnLKfPfQ28q30+JyCmPcX7EZs/iqfIdL1rWFSHwEmJVkia nik/uODA1gh4gU2EGgVIQEGXzNCv2RgTgmuyY+/4LbgEnUdMF+/umDhQd+o+nnL3 Ie0eJ7mTNPq78Dw6/21OcpE3j+yBIHGf5ZOHf5Qy+kBAytR7n+wzorkVYhbTvb4s aWzHzmNBViQcum2SOaa/5I/HiG/Z4R2vnD53+bbjeLAsT/4OgUMdWziKOQbGbWwp z+j/tekCgYEA8cfyOrbvkTLQHpC/1PC0GyrPGW+/5HKv34N+lCK4U+j6q5x7T7ia kIacvW3/gPsmW0zcdty1RfgEKQXOT7sK7YN1yMwfRwHX7Ea9TW9ZPW/gnOlhwVJ0
sqb0HI10i2eRSx3pLeA7JoGdUpud7hy3bGws/1HgOSpRMin9Y65DEpVq2Ia9oir7 XOJLpSTEIulnBkgDHNOsdKVYHDR6k0gUisnIKSl2C3IgKHpCouwiOvvVPwd3PExg 17+d7KLBIu8LpG28wkXKFU8vSz5i7H4i/XCEChnKJ4oGJuGAJJM4Zn022U156pco 97aEAc9ZXR/1dm2njr4XxCXmrnKCYTElfRhLkmxtv+mCi6eV//5d12z7mY3dTBkQ EG2xpb5DQ+ITQ8BzsKcPX80rz8rTzgYFwaV3gUg38+bgka/JGJq8HgBuNnHv5CeT 1T/EoZTRYW2oPfOgQK8CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8B Af8EBAMCAQYwHQYDVR0OBBYEFIuDY73dQIhj2nnd4DG2SvseHVVaMA0GCSqGSIb3 DQEBCwUAA4IBAQBk/WwfoWYdS0M8rz5tJda/cMdYFSugUbTn6JJdmHuw6RmiKzKG 8NzfSqBR6m8MWdSTuAZ/chsUZH9YEIjS9tAH9/FfUFBrsUE7TXaUgpNBm4DBLLfl fj5xDmEyj17JPN/C36amQ9eU5BNesdCx9EkdWLyVJaM50HFRo71W0/FrpKZyKK68 XPhd1z9w/xgfCfYhe7PjEmrmNPN5Tgk5TyXW+UUhOepDctAv2DBetptcx+gHrtW+ Ygk1wptlt/tg7uUmstmXZA4vTPx83f4P3KSS3XHIYFIyGFWUDs23C20K6mmW1iXa h0S8LN4iv/+vNFPNiM1z9X/SZgfbwZXrLsSi -----END CERTIFICATE----- """ INTERNAL_VALID_LONG_CERT = parse_certificate(INTERNAL_VALID_LONG_STR) INTERNAL_INVALID_STR = """ -----BEGIN CERTIFICATE----- MIIEFTCCAv2gAwIBAgICA+gwDQYJKoZIhvcNAQELBQAwgYwxCzAJBgNVBAYTAlVT MQswCQYDVQQIDAJDQTEQMA4GA1UEBwwHQSBwbGFjZTEXMBUGA1UEAwwObG9uZy5s aXZlZC5jb20xEDAOBgNVBAoMB0V4YW1wbGUxEzARBgNVBAsMCk9wZXJhdGlvbnMx HjAcBgkqhkiG9w0BCQEWD2ppbUBleGFtcGxlLmNvbTAeFw0xNTA2MjYyMDM2NDha Fw0xNTA2MjcyMDM2NDhaMGkxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEQMA4G A1UEBxMHQSBwbGFjZTEQMA4GA1UEChMHRXhhbXBsZTETMBEGA1UECxMKT3BlcmF0 aW9uczEUMBIGA1UEAxMLZXhwaXJlZC5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IB DwAwggEKAoIBAQCcSMzRxB6+UONPqYMy1Ojw3Wi8DIpt9USnSR60I8LiEuRK2ayr 0RMjLJ6sBEgy/hISEqpLgTsciDpxwaTC/WNrkT9vaMcwfiG3V0Red8zbKHQzC+Ty cLRg9wbC3v613kaIZCQCoE7Aouru9WbVPmuRoasfztrgksWmH9infQbL4TDcmcxo qGaMn4ajQTVAD63CKnut+CULZIMBREBVlSTLiOO7qZdTrd+vjtLWvdXVPcWLSBrd
def upload(self, name, body, private_key, cert_chain, options, **kwargs): current_app.logger.debug("SFTP destination plugin is started") cn = common_name(parse_certificate(body)) host = self.get_option("host", options) port = self.get_option("port", options) user = self.get_option("user", options) password = self.get_option("password", options) ssh_priv_key = self.get_option("privateKeyPath", options) ssh_priv_key_pass = self.get_option("privateKeyPass", options) dst_path = self.get_option("destinationPath", options) export_format = self.get_option("exportFormat", options) # prepare files for upload files = {cn + ".key": private_key, cn + ".pem": body} if cert_chain: if export_format == "NGINX": # assemble body + chain in the single file files[cn + ".pem"] += "\n" + cert_chain elif export_format == "Apache": # store chain in the separate file files[cn + ".ca.bundle.pem"] = cert_chain # upload files try: current_app.logger.debug("Connecting to {0}@{1}:{2}".format( user, host, port)) ssh = paramiko.SSHClient() # allow connection to the new unknown host ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) # open the ssh connection if password: current_app.logger.debug("Using password") ssh.connect(host, username=user, port=port, password=password) elif ssh_priv_key: current_app.logger.debug("Using RSA private key") pkey = paramiko.RSAKey.from_private_key_file( ssh_priv_key, ssh_priv_key_pass) ssh.connect(host, username=user, port=port, pkey=pkey) else: current_app.logger.error( "No password or private key provided. Can't proceed") raise paramiko.ssh_exception.AuthenticationException # open the sftp session inside the ssh connection sftp = ssh.open_sftp() # make sure that the destination path exist try: current_app.logger.debug("Creating {0}".format(dst_path)) sftp.mkdir(dst_path) except IOError: current_app.logger.debug( "{0} already exist, resuming".format(dst_path)) try: dst_path_cn = dst_path + "/" + cn current_app.logger.debug("Creating {0}".format(dst_path_cn)) sftp.mkdir(dst_path_cn) except IOError: current_app.logger.debug( "{0} already exist, resuming".format(dst_path_cn)) # upload certificate files to the sftp destination for filename, data in files.items(): current_app.logger.debug("Uploading {0} to {1}".format( filename, dst_path_cn)) with sftp.open(dst_path_cn + "/" + filename, "w") as f: f.write(data) # read only for owner, -r-------- sftp.chmod(dst_path_cn + "/" + filename, 0o400) ssh.close() except Exception as e: current_app.logger.error("ERROR in {0}: {1}".format( e.__class__, e)) try: ssh.close() except BaseException: pass
def parsed_cert(self): assert self.body, "Certificate body not set" return utils.parse_certificate(self.body)
sqb0HI10i2eRSx3pLeA7JoGdUpud7hy3bGws/1HgOSpRMin9Y65DEpVq2Ia9oir7 XOJLpSTEIulnBkgDHNOsdKVYHDR6k0gUisnIKSl2C3IgKHpCouwiOvvVPwd3PExg 17+d7KLBIu8LpG28wkXKFU8vSz5i7H4i/XCEChnKJ4oGJuGAJJM4Zn022U156pco 97aEAc9ZXR/1dm2njr4XxCXmrnKCYTElfRhLkmxtv+mCi6eV//5d12z7mY3dTBkQ EG2xpb5DQ+ITQ8BzsKcPX80rz8rTzgYFwaV3gUg38+bgka/JGJq8HgBuNnHv5CeT 1T/EoZTRYW2oPfOgQK8CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8B Af8EBAMCAQYwHQYDVR0OBBYEFIuDY73dQIhj2nnd4DG2SvseHVVaMA0GCSqGSIb3 DQEBCwUAA4IBAQBk/WwfoWYdS0M8rz5tJda/cMdYFSugUbTn6JJdmHuw6RmiKzKG 8NzfSqBR6m8MWdSTuAZ/chsUZH9YEIjS9tAH9/FfUFBrsUE7TXaUgpNBm4DBLLfl fj5xDmEyj17JPN/C36amQ9eU5BNesdCx9EkdWLyVJaM50HFRo71W0/FrpKZyKK68 XPhd1z9w/xgfCfYhe7PjEmrmNPN5Tgk5TyXW+UUhOepDctAv2DBetptcx+gHrtW+ Ygk1wptlt/tg7uUmstmXZA4vTPx83f4P3KSS3XHIYFIyGFWUDs23C20K6mmW1iXa h0S8LN4iv/+vNFPNiM1z9X/SZgfbwZXrLsSi -----END CERTIFICATE----- """ INTERNAL_VALID_LONG_CERT = parse_certificate(INTERNAL_VALID_LONG_STR) INTERNAL_INVALID_STR = b""" -----BEGIN CERTIFICATE----- MIIEFTCCAv2gAwIBAgICA+gwDQYJKoZIhvcNAQELBQAwgYwxCzAJBgNVBAYTAlVT MQswCQYDVQQIDAJDQTEQMA4GA1UEBwwHQSBwbGFjZTEXMBUGA1UEAwwObG9uZy5s aXZlZC5jb20xEDAOBgNVBAoMB0V4YW1wbGUxEzARBgNVBAsMCk9wZXJhdGlvbnMx HjAcBgkqhkiG9w0BCQEWD2ppbUBleGFtcGxlLmNvbTAeFw0xNTA2MjYyMDM2NDha Fw0xNTA2MjcyMDM2NDhaMGkxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEQMA4G A1UEBxMHQSBwbGFjZTEQMA4GA1UEChMHRXhhbXBsZTETMBEGA1UECxMKT3BlcmF0 aW9uczEUMBIGA1UEAxMLZXhwaXJlZC5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IB DwAwggEKAoIBAQCcSMzRxB6+UONPqYMy1Ojw3Wi8DIpt9USnSR60I8LiEuRK2ayr 0RMjLJ6sBEgy/hISEqpLgTsciDpxwaTC/WNrkT9vaMcwfiG3V0Red8zbKHQzC+Ty cLRg9wbC3v613kaIZCQCoE7Aouru9WbVPmuRoasfztrgksWmH9infQbL4TDcmcxo qGaMn4ajQTVAD63CKnut+CULZIMBREBVlSTLiOO7qZdTrd+vjtLWvdXVPcWLSBrd
def upload(self, name, body, private_key, cert_chain, options, **kwargs): """ Upload certificate and private key :param private_key: :param cert_chain: :return: """ cname = common_name(parse_certificate(body)) url = self.get_option("vaultUrl", options) auth_method = self.get_option("authenticationMethod", options) auth_key = self.get_option("tokenFile/vaultRole", options) mount = self.get_option("vaultMount", options) path = self.get_option("vaultPath", options) bundle = self.get_option("bundleChain", options) obj_name = self.get_option("objectName", options) api_version = self.get_option("vaultKvApiVersion", options) san_filter = self.get_option("sanFilter", options) san_list = get_san_list(body) if san_filter: for san in san_list: try: if not re.match(san_filter, san, flags=re.IGNORECASE): current_app.logger.exception( "Exception uploading secret to vault: invalid SAN: {}" .format(san), exc_info=True, ) os._exit(1) except re.error: current_app.logger.exception( "Exception compiling regex filter: invalid filter", exc_info=True, ) client = hvac.Client(url=url) if auth_method == 'token': with open(auth_key, "r") as tfile: token = tfile.readline().rstrip("\n") client.token = token if auth_method == 'kubernetes': token_path = '/var/run/secrets/kubernetes.io/serviceaccount/token' with open(token_path, 'r') as f: jwt = f.read() client.auth_kubernetes(auth_key, jwt) client.secrets.kv.default_kv_version = api_version if obj_name: path = "{0}/{1}".format(path, obj_name) else: path = "{0}/{1}".format(path, cname) secret = get_secret(client, mount, path) secret["data"][cname] = {} if not cert_chain: chain = '' else: chain = cert_chain if bundle == "Nginx": secret["data"][cname]["crt"] = "{0}\n{1}".format(body, chain) secret["data"][cname]["key"] = private_key elif bundle == "Apache": secret["data"][cname]["crt"] = body secret["data"][cname]["chain"] = chain secret["data"][cname]["key"] = private_key elif bundle == "PEM": secret["data"][cname]["pem"] = "{0}\n{1}\n{2}".format( body, chain, private_key) else: secret["data"][cname]["crt"] = body secret["data"][cname]["key"] = private_key if isinstance(san_list, list): secret["data"][cname]["san"] = san_list try: client.secrets.kv.create_or_update_secret(path=path, mount_point=mount, secret=secret["data"]) except ConnectionError as err: current_app.logger.exception( "Exception uploading secret to vault: {0}".format(err), exc_info=True)
AQUAA4IBDwAwggEKAoIBAQDR+qNdfNsLhGvgw3IgCQNakL2B9dpQtkVnvAXhdRZq JETm/tHLkGvONWTXAwGdoiKv6+0j3I5InUsW+wzUPewcfj+PLNu4mFMq8jH/gPhT ElKiAztPRdm8QKchvrqiaU6uEbia8ClM6uPpIi8StxE1aJRYL03p0WeMJjJPrsl6 eSSdpR4qL69GTd1n5je9OuWAcn5utXXnt/jO4vNeFRjlGp/0n3JmTDd9w4vtAyY9 UrdGgo37eBmi6mXt5J9i//NenhaiOVU81RqxZM2Jt1kkg2WSjcqcIQfBEWp9StG4 6VmHLaL+9/v2XAV3tL1VilJGj6PoFMb4gY5MXthfGSiXAgMBAAGjQjBAMA8GA1Ud EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBQstpQr0iMBVfv0 lODIsMgT9+9oezANBgkqhkiG9w0BAQsFAAOCAQEASYQbv1Qwb5zES6Gb5LEhrAcH 81ZB2uIpKd3Ki6AS4fLJVymMGkUs0RZjt39Ep4qX1zf0hn82Yh9YwRalrkgu+tzK rp0JgegNe6+gyFRrJC0SIGA4zc3M02m/n4tdaouU2lp6jhmWruL3g25ZkgbQ8LO2 zjpSMtblR2euvR2+bI7TepklyG71qx5y6/N8x5PT+hnTlleiZeE/ji9D96MZlpWB 4kBihekWmxuptED22z/tpQtac+hPBNgt8z1uFVEYN2rKEcCE7V6Qk7icS+M4Vb7M 3D8kLyWDubs9Yy3l0EWjOXQXxEhTaKEm4gSuY/j+Y35bBVkA2Fcyuq7msiTgrw== -----END CERTIFICATE----- """ INTERMEDIATE_CERT = parse_certificate(INTERMEDIATE_CERT_STR) INTERMEDIATE_KEY = """\ -----BEGIN RSA PRIVATE KEY----- MIIEpAIBAAKCAQEA0fqjXXzbC4Rr4MNyIAkDWpC9gfXaULZFZ7wF4XUWaiRE5v7R y5BrzjVk1wMBnaIir+vtI9yOSJ1LFvsM1D3sHH4/jyzbuJhTKvIx/4D4UxJSogM7 T0XZvECnIb66omlOrhG4mvApTOrj6SIvErcRNWiUWC9N6dFnjCYyT67JenkknaUe Ki+vRk3dZ+Y3vTrlgHJ+brV157f4zuLzXhUY5Rqf9J9yZkw3fcOL7QMmPVK3RoKN +3gZoupl7eSfYv/zXp4WojlVPNUasWTNibdZJINlko3KnCEHwRFqfUrRuOlZhy2i /vf79lwFd7S9VYpSRo+j6BTG+IGOTF7YXxkolwIDAQABAoIBAQC8dTuSeLEQUTWR cVlIr043RpkPv1zF/BGm3PZaOAB6GztMJ4CcN27KkNmEsMoOdKq1QgaAnT+GpMX0 RjZpd3omyJi7JAPAVdavQNjm/RXjWRqZFlVw/LxDXbOjcc+IXQOk73rEdLBcvKT5 ZRjirzPev5IE49AF/0/0VYPqSHHEXL1HMWy5Q7KZU/C4Yp8em2l3UAh1/nLI2L+U iw6DxBhBbdsc2vD4dd4UUQmznBfvdBOiejKs9DYIDzmTrGI2lH0VUU6PVzb9LKJ3 UtlIsjOO0FdJxXhkjsDCcF4eEMhO4qOfVJDcl0YYsPLjM0t4IVfe2C0b3Hd37hhF b3ux0YoBAoGBAPWCPBepUdtiiMM21dLFPuy/d9/C3k8xTD23aO8zupwnGD1zz8OY 1GzsCtR9me0nIbHGxT+Unovuxh2JYQgrFpAFsG/izL8SpkDeT3hJDPY8PLrhGqMS