diff --git a/galaxy.yml b/galaxy.yml
index c124be9a3a9ec4616206d6cc0c7a453b629bcc31..029b94afe222be81134077b9d02bf8ac786198a1 100644
--- a/galaxy.yml
+++ b/galaxy.yml
@@ -14,6 +14,7 @@ documentation: https://kubespray.io
 license_file: LICENSE
 dependencies:
   ansible.utils: '>=2.5.0'
+  community.crypto: '>=2.22.3'
   community.general: '>=7.0.0'
   ansible.netcommon: '>=5.3.0'
   ansible.posix: '>=1.5.4'
diff --git a/requirements.txt b/requirements.txt
index 9b46368e5b4eddd9a6533e85db1a999bac4b1b3e..a0b298adf7c6dde92afa9f32db6da15bedebac5c 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,4 +1,6 @@
 ansible==9.12.0
+# Needed for community.crypto module
+cryptography==44.0.0
 # Needed for jinja2 json_query templating
 jmespath==1.0.1
 # Needed for ansible.utils.ipaddr
diff --git a/roles/kubernetes/control-plane/defaults/main/main.yml b/roles/kubernetes/control-plane/defaults/main/main.yml
index d3a28bc2154a3134ab2d5cdc5fc84f11e900d856..00da94347d75cc966348b2b1e2955097397dfa2b 100644
--- a/roles/kubernetes/control-plane/defaults/main/main.yml
+++ b/roles/kubernetes/control-plane/defaults/main/main.yml
@@ -236,3 +236,8 @@ kube_apiserver_tracing_sampling_rate_per_million: 100
 
 # Enable kubeadm file discovery if anonymous access has been removed
 kubeadm_use_file_discovery: "{{ remove_anonymous_access }}"
+
+# Supported asymmetric encryption algorithm types for the cluster's keys and certificates.
+# can be one of RSA-2048(default), RSA-3072, RSA-4096, ECDSA-P256
+# ref: https://kubernetes.io/docs/reference/config-api/kubeadm-config.v1beta4/#kubeadm-k8s-io-v1beta4-ClusterConfiguration
+kube_asymmetric_encryption_algorithm: "RSA-2048"
diff --git a/roles/kubernetes/kubeadm/tasks/main.yml b/roles/kubernetes/kubeadm/tasks/main.yml
index a65d34eea612a8951b5e0a9a56e0c1720d55051c..6e562d8ce3090e2e17f9928b1c58ea1dd107b6f7 100644
--- a/roles/kubernetes/kubeadm/tasks/main.yml
+++ b/roles/kubernetes/kubeadm/tasks/main.yml
@@ -29,20 +29,15 @@
   delegate_to: "{{ groups['kube_control_plane'][0] }}"
   run_once: true
 
-- name: Calculate kubeadm CA cert hash
-  shell: |
-    set -o pipefail && openssl x509 -pubkey -in {{ kube_cert_dir }}/ca.crt | \
-    openssl {% if 'RSA' in kube_asymmetric_encryption_algorithm %}rsa{% elif 'ECDSA' in kube_asymmetric_encryption_algorithm %}ec{% else %}rsa{% endif %} -pubin -outform der 2>/dev/null | \
-    openssl dgst -sha256 -hex | sed 's/^.* //'
-  args:
-    executable: /bin/bash
-  register: kubeadm_ca_hash
+- name: Fetch CA certificate from control plane node
+  slurp:
+    src: "{{ kube_cert_dir }}/ca.crt"
+  register: ca_cert_content
   when:
     - kubeadm_ca_stat.stat is defined
     - kubeadm_ca_stat.stat.exists
   delegate_to: "{{ groups['kube_control_plane'][0] }}"
   run_once: true
-  changed_when: false
 
 - name: Create kubeadm token for joining nodes with 24h expiration (default)
   command: "{{ bin_dir }}/kubeadm token create"
diff --git a/roles/kubernetes/kubeadm/templates/kubeadm-client.conf.j2 b/roles/kubernetes/kubeadm/templates/kubeadm-client.conf.j2
index 3735936f93d5b17447f4a36808931979156b5121..3dfe5d1fa9f767e6f71cd7c5a5db6e1f2c662bd2 100644
--- a/roles/kubernetes/kubeadm/templates/kubeadm-client.conf.j2
+++ b/roles/kubernetes/kubeadm/templates/kubeadm-client.conf.j2
@@ -13,9 +13,9 @@ discovery:
     apiServerEndpoint: {{ kubeadm_discovery_address }}
 {% endif %}
     token: {{ kubeadm_token }}
-{% if kubeadm_ca_hash.stdout is defined %}
+{% if ca_cert_content is defined %}
     caCertHashes:
-    - sha256:{{ kubeadm_ca_hash.stdout }}
+    - sha256:{{ (ca_cert_content.content | b64decode | community.crypto.x509_certificate_info).public_key_fingerprints.sha256.replace(':', '') }}
 {% else %}
     unsafeSkipCAVerification: true
 {% endif %}
diff --git a/roles/kubespray-defaults/defaults/main/main.yml b/roles/kubespray-defaults/defaults/main/main.yml
index 1210287c8017dc5a5a02b670d6692f6e59f8d523..32a78545a3da6aca3332b34fca18616bdc53c1a5 100644
--- a/roles/kubespray-defaults/defaults/main/main.yml
+++ b/roles/kubespray-defaults/defaults/main/main.yml
@@ -62,11 +62,6 @@ kubeadm_join_phases_skip: >-
 # Set to true to remove the role binding to anonymous users created by kubeadm
 remove_anonymous_access: false
 
-# Supported asymmetric encryption algorithm types for the cluster's keys and certificates.
-# can be one of RSA-2048(default), RSA-3072, RSA-4096, ECDSA-P256
-# ref: https://kubernetes.io/docs/reference/config-api/kubeadm-config.v1beta4/#kubeadm-k8s-io-v1beta4-ClusterConfiguration
-kube_asymmetric_encryption_algorithm: "RSA-2048"
-
 # A string slice of values which specify the addresses to use for NodePorts.
 # Values may be valid IP blocks (e.g. 1.2.3.0/24, 1.2.3.4/32).
 # The default empty string slice ([]) means to use all local addresses.