Source code for bankid.certutils

# -*- coding: utf-8 -*-
"""
:mod:`bankid.certutils` -- Certificate Utilities
================================================
.. moduleauthor:: hbldh <henrik.blidh@nedomkull.com>
"""

import os
import subprocess
from typing import Tuple, Union

import pathlib
import importlib.resources

from bankid.certs import get_test_cert_p12
from bankid.exceptions import BankIDError

_TEST_CERT_PASSWORD = "qwerty123"


def resolve_cert_path(file: str) -> pathlib.Path:
    path = importlib.resources.files("bankid.certs").joinpath(file)
    assert isinstance(path, pathlib.Path)
    return path


[docs] def create_bankid_test_server_cert_and_key(destination_path: str = ".") -> Tuple[str, str]: """Split the bundled test certificate into certificate and key parts and save them as separate files, stored in PEM format. If the environment variable TEST_CERT_FILE is set, use this file instead of fetching the P12 certificate. :param destination_path: The directory to save certificate and key files to. Default is the current directory. :type destination_path: str :returns: The path tuple ``(cert_path, key_path)``. :rtype: tuple """ if test_cert_file := os.getenv("TEST_CERT_FILE"): certificate, key = split_certificate( test_cert_file, destination_path, password=_TEST_CERT_PASSWORD ) else: # Fetch testP12 certificate path certificate, key = split_certificate(str(get_test_cert_p12()), destination_path, password=_TEST_CERT_PASSWORD) # Return path tuples. return certificate, key
[docs] def split_certificate(certificate_path: str, destination_folder: str, password: Union[str, None] = None) -> Tuple[str, str]: """Splits a PKCS12 certificate into Base64-encoded DER certificate and key. This method splits a potentially password-protected `PKCS12 <https://en.wikipedia.org/wiki/PKCS_12>`_ certificate (format ``.p12`` or ``.pfx``) into one certificate and one key part, both in `pem <https://en.wikipedia.org/wiki/X.509#Certificate_filename_extensions>`_ format. :returns: Tuple of certificate and key string data. :rtype: tuple """ try: # Attempt Linux and Darwin call first. p = subprocess.Popen(["openssl", "version"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) sout, _ = p.communicate() openssl_executable_version = sout.decode().lower() if not (openssl_executable_version.startswith("openssl") or openssl_executable_version.startswith("libressl")): raise BankIDError("OpenSSL executable could not be found. " "Splitting cannot be performed.") openssl_executable = "openssl" except Exception: # Attempt to call on standard Git for Windows path. p = subprocess.Popen( ["C:\\Program Files\\Git\\mingw64\\bin\\openssl.exe", "version"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, ) sout, _ = p.communicate() if not sout.decode().lower().startswith("openssl"): raise BankIDError("OpenSSL executable could not be found. " "Splitting cannot be performed.") openssl_executable = "C:\\Program Files\\Git\\mingw64\\bin\\openssl.exe" if not os.path.exists(os.path.abspath(os.path.expanduser(destination_folder))): os.makedirs(os.path.abspath(os.path.expanduser(destination_folder))) # Paths to output files. out_cert_path = os.path.join(os.path.abspath(os.path.expanduser(destination_folder)), "certificate.pem") out_key_path = os.path.join(os.path.abspath(os.path.expanduser(destination_folder)), "key.pem") # Use openssl for converting to pem format. pipeline_1 = [ openssl_executable, "pkcs12", "-in", "{0}".format(certificate_path), "-passin" if password is not None else "", "pass:{0}".format(password) if password is not None else "", "-out", "{0}".format(out_cert_path), "-clcerts", "-nokeys", ] p = subprocess.Popen(list(filter(None, pipeline_1)), stdout=subprocess.PIPE, stderr=subprocess.PIPE) out, err = p.communicate() if p.returncode: raise BankIDError("Error converting certificate: {0}".format(err.decode("utf-8"))) pipeline_2 = [ openssl_executable, "pkcs12", "-in", "{0}".format(certificate_path), "-passin" if password is not None else "", "pass:{0}".format(password) if password is not None else "", "-out", "{0}".format(out_key_path), "-nocerts", "-nodes", ] p = subprocess.Popen(list(filter(None, pipeline_2)), stdout=subprocess.PIPE, stderr=subprocess.PIPE) out, err = p.communicate() if p.returncode: raise BankIDError("Error converting certificate: {0}".format(err.decode("utf-8"))) # Return path tuples. return out_cert_path, out_key_path
def main(verbose: bool = True) -> Tuple[str, str]: paths = create_bankid_test_server_cert_and_key(os.path.expanduser("~")) if verbose: print("Saved certificate as {0}".format(paths[0])) print("Saved key as {0}".format(paths[1])) return paths if __name__ == "__main__": # pragma: no cover main()