commit 6b38521bea5c85ed01e448ea867ff88a2d3ce3c0 Author: Fenris Wolf Date: Fri Apr 3 01:05:31 2026 +0200 [ini] diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..2da331d --- /dev/null +++ b/readme.md @@ -0,0 +1,8 @@ +# tls-utils + +## Running + +### Requirements + +- Python3 + diff --git a/source/helpers/certinfo.py b/source/helpers/certinfo.py new file mode 100644 index 0000000..b987bd0 --- /dev/null +++ b/source/helpers/certinfo.py @@ -0,0 +1,78 @@ +import subprocess as _subprocess + + +def get_certificate_info_raw(source): + p1 = _subprocess.Popen( + ["openssl", "x509", "-outform", "pem"], + stdin = _subprocess.PIPE, + stdout = _subprocess.PIPE + ) + (p1_stdout, p1_stderr, ) = p1.communicate(input = bytes(source, "utf-8")) + p2 = _subprocess.Popen( + ["openssl", "x509", "-noout", "-text"], + stdin = _subprocess.PIPE, + stdout = _subprocess.PIPE + ) + (p2_stdout, p2_stderr, ) = p2.communicate(input = p1_stdout) + return p2_stdout.decode("utf-8") + + +def get_certificate_info_from_file(path): + return get_certificate_info_raw( + _subprocess.check_output( + ["cat", path], + text = True + ) + ) + + +def get_certificate_info_from_internet(domain): + return get_certificate_info_raw( + _subprocess.check_output( + ["openssl", "s_client", "-connect", ("%s:443" % domain), "-showcerts"], + stdin = _subprocess.DEVNULL, + stderr = _subprocess.DEVNULL, + text = True + ) + ) + + +def extract_fingerprint(certificate_info): + state = { + "situation": 0, + "result": "", + } + for line in certificate_info.split("\n"): + if (state["situation"] == 0): + if (line.strip() == "Subject Public Key Info:"): + state = { + "situation": 1, + "result": state["result"], + } + else: + pass + elif (state["situation"] == 1): + if (line.strip() == "pub:"): + state = { + "situation": 2, + "result": state["result"], + } + else: + pass + elif (state["situation"] == 2): + if (line.startswith(" ")): + state = { + "situation": 2, + "result": (state["result"] + line.strip().replace(":", "")), + } + else: + state = { + "situation": 3, + "result": state["result"] + } + break + else: + pass + return state["result"] + + diff --git a/source/helpers/string.py b/source/helpers/string.py new file mode 100644 index 0000000..ab623c2 --- /dev/null +++ b/source/helpers/string.py @@ -0,0 +1,7 @@ +def coin(template, arguments): + result = template + for (key, value, ) in arguments.items(): + result = result.replace("{{%s}}" % key, value) + return result + + diff --git a/source/verify.py b/source/verify.py new file mode 100644 index 0000000..e0f5d7b --- /dev/null +++ b/source/verify.py @@ -0,0 +1,62 @@ +import sys as _sys +import argparse as _argparse + +import helpers.string as _string +import helpers.certinfo as _certinfo + + +def main(): + ## args + argument_parser = _argparse.ArgumentParser( + prog = "tls-verify", + description = "compares the fingerprints of a TLS certificate on the machine with the one delivered through the internet for a given domain", + ) + argument_parser.add_argument( + "domain", + type = str, + metavar = "", + ) + argument_parser.add_argument( + "-d", + "--cert-directory", + type = str, + default = "/etc/ssl/fullchains", + metavar = "", + ) + argument_parser.add_argument( + "-e", + "--file-extension", + type = str, + default = "pem", + metavar = "", + ) + args = argument_parser.parse_args() + + ## exec + fingerprint_shall = _certinfo.extract_fingerprint( + _certinfo.get_certificate_info_from_file( + _string.coin( + "{{directory}}/{{domain}}.{{extension}}", + { + "directory": args.cert_directory, + "domain": args.domain, + "extension": args.file_extension + } + ) + ) + ) + fingerprint_is = _certinfo.extract_fingerprint( + _certinfo.get_certificate_info_from_internet( + args.domain + ) + ) + if (fingerprint_shall == fingerprint_is): + _sys.stdout.write("ok\n") + _sys.exit(0) + else: + _sys.stderr.write("!MISMATCH!\n") + _sys.stderr.write("[shall] %s\n" % fingerprint_shall) + _sys.stderr.write("[is ] %s\n" % fingerprint_is) + _sys.exit(1) + + diff --git a/tools/build b/tools/build new file mode 100755 index 0000000..bea6dca --- /dev/null +++ b/tools/build @@ -0,0 +1,36 @@ +#!/usr/bin/env sh + +## consts + +dir_source="source" +dir_temp="/tmp/tls-utils-temp" +dir_build="/tmp/tls-utils" + + +## vars + +path_verify=${dir_build}/tls-verify + + +## exec + +### exec:verify + +rm ${dir_temp} --force --recursive +mkdir ${dir_temp} --parents +cp ${dir_source}/. ${dir_temp}/ --recursive --update --verbose +for dir in $(find ${dir_temp} -mindepth 1 -type d) ; do touch ${dir}/__init__.py ; done +echo '' > ${dir_temp}/__main__.py +echo 'from verify import *' >> ${dir_temp}/__main__.py +echo 'if __name__ == "__main__": main()' >> ${dir_temp}/__main__.py + +mkdir ${dir_build} --parents +# rm ${path_verify}.zip --force +cd ${dir_temp} +python3 -m zipfile -c ${path_verify}.zip . +cd - +echo '#!/usr/bin/env python3' > ${path_verify} +cat ${path_verify}.zip >> ${path_verify} +rm ${path_verify}.zip +chmod +x ${path_verify} +