File fix-tls-and-x509-modules-for-older-cryptography-modu.patch of Package salt
From 7f15657c26c4e5e9fabc72f4da2d9a91353d5d3a Mon Sep 17 00:00:00 2001
From: Marek Czernek <marek.czernek@suse.com>
Date: Tue, 11 Nov 2025 08:46:20 +0100
Subject: [PATCH] Fix tls and x509 modules for older cryptography
module (#737)
---
salt/modules/tls.py | 73 +++++++++++++++++-------
salt/modules/x509.py | 128 +++++++++++++++++++++++++++++++++++--------
2 files changed, 158 insertions(+), 43 deletions(-)
diff --git a/salt/modules/tls.py b/salt/modules/tls.py
index 9d29bd1e9b..4d7db87f93 100644
--- a/salt/modules/tls.py
+++ b/salt/modules/tls.py
@@ -104,6 +104,7 @@ import logging
import math
import os
import re
+import sys
import time
from datetime import datetime
@@ -1594,6 +1595,9 @@ def create_pkcs12(ca_name, CN, passphrase="", cacert_path=None, replace=False):
salt '*' tls.create_pkcs12 test localhost
"""
+ # Necessary for OSes with older cryptography module
+ compat_mode = sys.version_info < (3,12)
+
set_ca_path(cacert_path)
p12_path = f"{cert_base_path()}/{ca_name}/certs/{CN}.p12"
ca_cert_path = f"{cert_base_path()}/{ca_name}/{ca_name}_ca_cert.crt"
@@ -1605,7 +1609,12 @@ def create_pkcs12(ca_name, CN, passphrase="", cacert_path=None, replace=False):
try:
with salt.utils.files.fopen(ca_cert_path, "rb") as fhr:
- ca_cert = cryptography.x509.load_pem_x509_certificate(fhr.read())
+ if compat_mode:
+ ca_cert = OpenSSL.crypto.load_certificate(
+ OpenSSL.crypto.FILETYPE_PEM, fhr.read()
+ )
+ else:
+ ca_cert = cryptography.x509.load_pem_x509_certificate(fhr.read())
except OSError:
return 'There is no CA named "{}"'.format(ca_name)
except ValueError as e:
@@ -1613,34 +1622,58 @@ def create_pkcs12(ca_name, CN, passphrase="", cacert_path=None, replace=False):
try:
with salt.utils.files.fopen(cert_path, "rb") as fhr:
- cert = cryptography.x509.load_pem_x509_certificate(fhr.read())
+ if compat_mode:
+ cert = OpenSSL.crypto.load_certificate(
+ OpenSSL.crypto.FILETYPE_PEM, fhr.read()
+ )
+ else:
+ cert = cryptography.x509.load_pem_x509_certificate(fhr.read())
with salt.utils.files.fopen(priv_key_path, "rb") as fhr:
- key = cryptography_serialization.load_pem_private_key(
- fhr.read(),
- password=None,
- )
+ if compat_mode:
+ key = OpenSSL.crypto.load_privatekey(
+ OpenSSL.crypto.FILETYPE_PEM, fhr.read()
+ )
+ else:
+ key = cryptography_serialization.load_pem_private_key(
+ fhr.read(),
+ password=None,
+ )
except OSError:
return 'There is no certificate that matches the CN "{}"'.format(CN)
except ValueError as e:
return f'Could not load certificate {cert_path}: {e}'
- if passphrase:
- encryption_algorithm = cryptography_serialization.BestAvailableEncryption(
- salt.utils.stringutils.to_bytes(passphrase)
- )
+ if compat_mode:
+ pkcs12 = OpenSSL.crypto.PKCS12()
+
+ pkcs12.set_certificate(cert)
+ pkcs12.set_ca_certificates([ca_cert])
+ pkcs12.set_privatekey(key)
+
+ with salt.utils.files.fopen(
+ "{}/{}/certs/{}.p12".format(cert_base_path(), ca_name, CN), "wb"
+ ) as ofile:
+ ofile.write(
+ pkcs12.export(passphrase=salt.utils.stringutils.to_bytes(passphrase))
+ )
else:
- encryption_algorithm = cryptography_serialization.NoEncryption()
+ if passphrase:
+ encryption_algorithm = cryptography_serialization.BestAvailableEncryption(
+ salt.utils.stringutils.to_bytes(passphrase)
+ )
+ else:
+ encryption_algorithm = cryptography_serialization.NoEncryption()
- pkcs12 = cryptography_pkcs12.serialize_key_and_certificates(
- name=salt.utils.stringutils.to_bytes(CN),
- key=key,
- cert=cert,
- cas=[ca_cert],
- encryption_algorithm=encryption_algorithm,
- )
+ pkcs12 = cryptography_pkcs12.serialize_key_and_certificates(
+ name=salt.utils.stringutils.to_bytes(CN),
+ key=key,
+ cert=cert,
+ cas=[ca_cert],
+ encryption_algorithm=encryption_algorithm,
+ )
- with salt.utils.files.fopen(p12_path, "wb") as ofile:
- ofile.write(pkcs12)
+ with salt.utils.files.fopen(p12_path, "wb") as ofile:
+ ofile.write(pkcs12)
return 'Created PKCS#12 Certificate for "{0}": "{1}/{2}/certs/{0}.p12"'.format(
CN,
diff --git a/salt/modules/x509.py b/salt/modules/x509.py
index 164541fc76..373e394856 100644
--- a/salt/modules/x509.py
+++ b/salt/modules/x509.py
@@ -32,16 +32,20 @@ import tempfile
import salt.exceptions
import salt.utils.data
-import salt.utils.dictupdate
import salt.utils.files
import salt.utils.path
import salt.utils.platform
import salt.utils.stringutils
import salt.utils.versions
-import salt.utils.x509 as x509util
from salt.state import STATE_INTERNAL_KEYWORDS as _STATE_INTERNAL_KEYWORDS
from salt.utils.odict import OrderedDict
+# Necessary for OSes with older cryptography module
+COMPAT_MODE = sys.version_info < (3,12)
+if not COMPAT_MODE:
+ import salt.utils.dictupdate
+ import salt.utils.x509 as x509util
+
try:
import M2Crypto
@@ -988,35 +992,113 @@ def create_crl(
if revoked is None:
revoked = []
+ if COMPAT_MODE:
+ crl = OpenSSL.crypto.CRL()
+ for rev_item in revoked:
+ if "certificate" in rev_item:
+ rev_cert = read_certificate(rev_item["certificate"])
+ rev_item["serial_number"] = rev_cert["Serial Number"]
+ rev_item["not_after"] = rev_cert["Not After"]
- for rev_item in revoked:
- if "reason" in rev_item:
- salt.utils.dictupdate.set_dict_key_value(
- rev_item, "extensions:CRLReason", rev_item["reason"]
+ serial_number = rev_item["serial_number"].replace(":", "")
+ # OpenSSL bindings requires this to be a non-unicode string
+ serial_number = salt.utils.stringutils.to_bytes(serial_number)
+
+ if "not_after" in rev_item and not include_expired:
+ not_after = datetime.datetime.strptime(
+ rev_item["not_after"], "%Y-%m-%d %H:%M:%S"
+ )
+ if datetime.datetime.now() > not_after:
+ continue
+
+ if "revocation_date" not in rev_item:
+ rev_item["revocation_date"] = datetime.datetime.now().strftime(
+ "%Y-%m-%d %H:%M:%S"
+ )
+
+ rev_date = datetime.datetime.strptime(
+ rev_item["revocation_date"], "%Y-%m-%d %H:%M:%S"
)
+ rev_date = rev_date.strftime("%Y%m%d%H%M%SZ")
+ rev_date = salt.utils.stringutils.to_bytes(rev_date)
- builder, private_key_obj = x509util.build_crl(
- signing_private_key=signing_private_key,
- signing_private_key_passphrase=signing_private_key_passphrase,
- include_expired=include_expired,
- revoked=revoked,
- signing_cert=signing_cert,
- days_valid=days_valid,
- )
+ rev = OpenSSL.crypto.Revoked()
+ rev.set_serial(salt.utils.stringutils.to_bytes(serial_number))
+ rev.set_rev_date(salt.utils.stringutils.to_bytes(rev_date))
+
+ if "reason" in rev_item:
+ # Same here for OpenSSL bindings and non-unicode strings
+ reason = salt.utils.stringutils.to_bytes(rev_item["reason"])
+ rev.set_reason(reason)
+
+ crl.add_revoked(rev)
+
+ signing_cert = _text_or_file(signing_cert)
+ cert = OpenSSL.crypto.load_certificate(
+ OpenSSL.crypto.FILETYPE_PEM, get_pem_entry(signing_cert, pem_type="CERTIFICATE")
+ )
+ signing_private_key = _get_private_key_obj(
+ signing_private_key, passphrase=signing_private_key_passphrase
+ ).as_pem(cipher=None)
+ key = OpenSSL.crypto.load_privatekey(
+ OpenSSL.crypto.FILETYPE_PEM, get_pem_entry(signing_private_key)
+ )
+
+ export_kwargs = {
+ "cert": cert,
+ "key": key,
+ "type": OpenSSL.crypto.FILETYPE_PEM,
+ "days": days_valid,
+ }
+ if digest:
+ export_kwargs["digest"] = salt.utils.stringutils.to_bytes(digest)
+ else:
+ log.warning("No digest specified. The default md5 digest will be used.")
+
+ try:
+ crltext = crl.export(**export_kwargs)
+ except (TypeError, ValueError):
+ log.warning(
+ "Error signing crl with specified digest. Are you using "
+ "pyopenssl 0.15 or newer? The default md5 digest will be used."
+ )
+ export_kwargs.pop("digest", None)
+ crltext = crl.export(**export_kwargs)
+
+ if text:
+ return crltext
+
+ return write_pem(text=crltext, path=path, pem_type="X509 CRL")
- if digest:
- hashing_algorithm = x509util.get_hashing_algorithm(digest)
else:
- log.warning("No digest specified. The default md5 digest will be used.")
- hashing_algorithm = x509util.get_hashing_algorithm("MD5")
+ for rev_item in revoked:
+ if "reason" in rev_item:
+ salt.utils.dictupdate.set_dict_key_value(
+ rev_item, "extensions:CRLReason", rev_item["reason"]
+ )
- crl = builder.sign(private_key_obj, algorithm=hashing_algorithm)
- crl_bytes = crl.public_bytes(x509util.serialization.Encoding.PEM)
+ builder, private_key_obj = x509util.build_crl(
+ signing_private_key=signing_private_key,
+ signing_private_key_passphrase=signing_private_key_passphrase,
+ include_expired=include_expired,
+ revoked=revoked,
+ signing_cert=signing_cert,
+ days_valid=days_valid,
+ )
- if text:
- return crl_bytes.decode()
+ if digest:
+ hashing_algorithm = x509util.get_hashing_algorithm(digest)
+ else:
+ log.warning("No digest specified. The default md5 digest will be used.")
+ hashing_algorithm = x509util.get_hashing_algorithm("MD5")
- return write_pem(text=crl_bytes, path=path, pem_type="X509 CRL")
+ crl = builder.sign(private_key_obj, algorithm=hashing_algorithm)
+ crl_bytes = crl.public_bytes(x509util.serialization.Encoding.PEM)
+
+ if text:
+ return crl_bytes.decode()
+
+ return write_pem(text=crl_bytes, path=path, pem_type="X509 CRL")
def sign_remote_certificate(argdic, **kwargs):
--
2.51.1