Repository: ansible/ansible
Pull request: openssl_certificate/csr(_info): add support for SubjectKeyIdentifier and AuthorityKeyIdentifier
##### SUMMARY
Adds support for the `SubjectKeyIdentifier` and `AuthorityKeyIdentifier` extensions to the `openssl_certificate*` and `openssl_csr*` modules.

NOTE: since pyOpenSSL doesn't directly support the extensions, I decided to **only** add support for this for the cryptography backend. Adding support for the pyOpenSSL backend is possible, but requires to re-implement extension parsing / creating ourselves. I'm not really willing to do that.

Fixes #50782.

##### ISSUE TYPE
- New Module Pull Request

##### COMPONENT NAME
openssl_certificate
openssl_certificate_info
openssl_csr
openssl_csr_info


Problem statement:
In openssl csr & cert modules add support for v3 key identifier extensions
<!--- Verify first that your feature was not already discussed on GitHub -->
<!--- Complete *all* sections as described, this form is processed automatically -->

##### SUMMARY
As a user, I'd like to supply the subjectKeyIdentifier and authorityKeyIdentifier openssl v3 extensions when generating a csr and cert.  

##### ISSUE TYPE
- Feature Idea

##### COMPONENT NAME
openssl_certificate
openssl_csr

##### ADDITIONAL INFORMATION
Some webservers aggressively validate SSL certificate data and for a version 3 certificate these extensions are effectively a standard for commercial CAs.  The lack of these headers can lead to pedantic validation failing.  Presently there is no way using the applicable ansible modules to add this information.

Hints from issue discussion:
Files identified in the description:
* [lib/ansible/modules/crypto/openssl_certificate.py](https://github.com/ansible/ansible/blob/devel/lib/ansible/modules/crypto/openssl_certificate.py)
* [lib/ansible/modules/crypto/openssl_csr.py](https://github.com/ansible/ansible/blob/devel/lib/ansible/modules/crypto/openssl_csr.py)

If these files are inaccurate, please update the `component name` section of the description or use the `!component` bot command.

[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: components_banner --->
cc @MarkusTeufelberger @Spredzy @felixfontein
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->
cc @resmo @spredzy
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->
cc @Shaps @Xyon @puiterwijk
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->
resolved_by_pr #60741

Commit message: Apparently the function call expected different args for cryptography < 2.7.

Patch to review:
diff --git a/changelogs/fragments/60741-openssl-cert-csr-key-identifier.yml b/changelogs/fragments/60741-openssl-cert-csr-key-identifier.yml
new file mode 100644
index 00000000000000..92ed112fad658f
--- /dev/null
+++ b/changelogs/fragments/60741-openssl-cert-csr-key-identifier.yml
@@ -0,0 +1,6 @@
+minor_changes:
+- "openssl_certificate - add support for subject key identifier and authority key identifier extensions."
+- "openssl_certificate - the ``ownca`` provider creates authority key identifiers if not explicitly disabled with ``ownca_create_authority_key_identifier: no``."
+- "openssl_certificate_info - add support for subject key identifier and authority key identifier extensions."
+- "openssl_csr - add support for subject key identifier and authority key identifier extensions."
+- "openssl_csr_info - add support for subject key identifier and authority key identifier extensions."
diff --git a/docs/docsite/rst/porting_guides/porting_guide_2.9.rst b/docs/docsite/rst/porting_guides/porting_guide_2.9.rst
index a7a599f0575f33..3188969f9f318d 100644
--- a/docs/docsite/rst/porting_guides/porting_guide_2.9.rst
+++ b/docs/docsite/rst/porting_guides/porting_guide_2.9.rst
@@ -265,6 +265,7 @@ Noteworthy module changes
 * `mysql_db <mysql_db_module>` returns new `db_list` parameter in addition to `db` parameter. This `db_list` parameter refers to list of database names. `db` parameter will be deprecated in version `2.13`.
 * `snow_record <snow_record_module>` and `snow_record_find <snow_record_find_module>` now takes environment variables for `instance`, `username` and `password` parameters. This change marks these parameters as optional.
 * The deprecated ``force`` option in ``win_firewall_rule`` has been removed.
+* :ref:`openssl_certificate <openssl_certificate_module>`'s ``ownca`` provider creates authority key identifiers if not explicitly disabled with ``ownca_create_authority_key_identifier: no``. This is only the case for the ``cryptography`` backend, which is selected by default if the ``cryptography`` library is available.
 
 
 Plugins
diff --git a/lib/ansible/modules/crypto/openssl_certificate.py b/lib/ansible/modules/crypto/openssl_certificate.py
index b2499aadd7e814..846eae96228a78 100644
--- a/lib/ansible/modules/crypto/openssl_certificate.py
+++ b/lib/ansible/modules/crypto/openssl_certificate.py
@@ -138,6 +138,16 @@
         default: +3650d
         aliases: [ selfsigned_notAfter ]
 
+    selfsigned_create_subject_key_identifier:
+        description:
+            - Create the Subject Key Identifier from the public key. If the CSR provided a
+              subject key identifier, it is ignored.
+            - This is only used by the C(selfsigned) provider.
+            - Note that this is only supported if the C(cryptography) backend is used!
+        type: bool
+        default: no
+        version_added: "2.9"
+
     ownca_path:
         description:
             - Remote absolute path of the CA (Certificate Authority) certificate.
@@ -204,6 +214,28 @@
         default: +3650d
         version_added: "2.7"
 
+    ownca_create_subject_key_identifier:
+        description:
+            - Create the Subject Key Identifier from the public key. If the CSR provided a
+              subject key identifier, it is ignored.
+            - This is only used by the C(ownca) provider.
+            - Note that this is only supported if the C(cryptography) backend is used!
+        type: bool
+        default: no
+        version_added: "2.9"
+
+    ownca_create_authority_key_identifier:
+        description:
+            - Create a Authority Key Identifier from the CA's certificate. If the CSR provided
+              a authority key identifier, it is ignored.
+            - The Authority Key Identifier is generated from the CA certificate's Subject Key Identifier,
+              if available. If it is not available, the CA certificate's public key will be used.
+            - This is only used by the C(ownca) provider.
+            - Note that this is only supported if the C(cryptography) backend is used!
+        type: bool
+        default: yes
+        version_added: "2.9"
+
     acme_accountkey_path:
         description:
             - The path to the accountkey for the C(acme) provider.
@@ -833,6 +865,8 @@ def __init__(self, module, backend):
             module.check_mode
         )
 
+        self.create_subject_key_identifier = False
+        self.create_authority_key_identifier = False
         self.provider = module.params['provider']
         self.privatekey_path = module.params['privatekey_path']
         self.privatekey_passphrase = module.params['privatekey_passphrase']
@@ -918,13 +952,21 @@ def _validate_csr(self):
             if self.csr.subject != self.cert.subject:
                 return False
             # Check extensions
-            cert_exts = self.cert.extensions
-            csr_exts = self.csr.extensions
+            cert_exts = list(self.cert.extensions)
+            csr_exts = list(self.csr.extensions)
+            if self.create_subject_key_identifier:
+                # Filter out SubjectKeyIdentifier extension before comparison
+                cert_exts = list(filter(lambda x: not isinstance(x.value, x509.SubjectKeyIdentifier), cert_exts))
+                csr_exts = list(filter(lambda x: not isinstance(x.value, x509.SubjectKeyIdentifier), csr_exts))
+            if self.create_authority_key_identifier:
+                # Filter out AuthorityKeyIdentifier extension before comparison
+                cert_exts = list(filter(lambda x: not isinstance(x.value, x509.AuthorityKeyIdentifier), cert_exts))
+                csr_exts = list(filter(lambda x: not isinstance(x.value, x509.AuthorityKeyIdentifier), csr_exts))
             if len(cert_exts) != len(csr_exts):
                 return False
             for cert_ext in cert_exts:
                 try:
-                    csr_ext = csr_exts.get_extension_for_oid(cert_ext.oid)
+                    csr_ext = self.csr.extensions.get_extension_for_oid(cert_ext.oid)
                     if cert_ext != csr_ext:
                         return False
                 except cryptography.x509.ExtensionNotFound as dummy:
@@ -966,6 +1008,16 @@ def check(self, module, perms_required=True):
             if not self._validate_csr():
                 return False
 
+        # Check SubjectKeyIdentifier
+        if self.create_subject_key_identifier:
+            if self.backend == 'cryptography':
+                try:
+                    ext = self.cert.extensions.get_extension_for_class(x509.SubjectKeyIdentifier)
+                    if ext.value.digest != x509.SubjectKeyIdentifier.from_public_key(self.cert.public_key()).digest:
+                        return False
+                except cryptography.x509.ExtensionNotFound as dummy:
+                    return False
+
         return True
 
 
@@ -995,6 +1047,7 @@ class SelfSignedCertificateCryptography(Certificate):
     """Generate the self-signed certificate, using the cryptography backend"""
     def __init__(self, module):
         super(SelfSignedCertificateCryptography, self).__init__(module, 'cryptography')
+        self.create_subject_key_identifier = module.params['selfsigned_create_subject_key_identifier']
         self.notBefore = self.get_relative_time_option(module.params['selfsigned_not_before'], 'selfsigned_not_before')
         self.notAfter = self.get_relative_time_option(module.params['selfsigned_not_after'], 'selfsigned_not_after')
         self.digest = crypto_utils.select_message_digest(module.params['selfsigned_digest'])
@@ -1044,7 +1097,16 @@ def generate(self, module):
                 cert_builder = cert_builder.not_valid_after(self.notAfter)
                 cert_builder = cert_builder.public_key(self.privatekey.public_key())
                 for extension in self.csr.extensions:
+                    if self.create_subject_key_identifier and isinstance(extension.value, x509.SubjectKeyIdentifier):
+                        continue
+                    if self.create_authority_key_identifier and isinstance(extension.value, x509.AuthorityKeyIdentifier):
+                        continue
                     cert_builder = cert_builder.add_extension(extension.value, critical=extension.critical)
+                if self.create_subject_key_identifier:
+                    cert_builder = cert_builder.add_extension(
+                        x509.SubjectKeyIdentifier.from_public_key(self.privatekey.public_key()),
+                        critical=False
+                    )
             except ValueError as e:
                 raise CertificateError(str(e))
 
@@ -1098,6 +1160,8 @@ class SelfSignedCertificate(Certificate):
 
     def __init__(self, module):
         super(SelfSignedCertificate, self).__init__(module, 'pyopenssl')
+        if module.params['selfsigned_create_subject_key_identifier']:
+            module.fail_json(msg='selfsigned_create_subject_key_identifier cannot be used with the pyOpenSSL backend!')
         self.notBefore = self.get_relative_time_option(module.params['selfsigned_not_before'], 'selfsigned_not_before')
         self.notAfter = self.get_relative_time_option(module.params['selfsigned_not_after'], 'selfsigned_not_after')
         self.digest = module.params['selfsigned_digest']
@@ -1187,6 +1251,8 @@ class OwnCACertificateCryptography(Certificate):
     """Generate the own CA certificate. Using the cryptography backend"""
     def __init__(self, module):
         super(OwnCACertificateCryptography, self).__init__(module, 'cryptography')
+        self.create_subject_key_identifier = module.params['ownca_create_subject_key_identifier']
+        self.create_authority_key_identifier = module.params['ownca_create_authority_key_identifier']
         self.notBefore = self.get_relative_time_option(module.params['ownca_not_before'], 'ownca_not_before')
         self.notAfter = self.get_relative_time_option(module.params['ownca_not_after'], 'ownca_not_after')
         self.digest = crypto_utils.select_message_digest(module.params['ownca_digest'])
@@ -1244,7 +1310,28 @@ def generate(self, module):
             cert_builder = cert_builder.not_valid_after(self.notAfter)
             cert_builder = cert_builder.public_key(self.csr.public_key())
             for extension in self.csr.extensions:
+                if self.create_subject_key_identifier and isinstance(extension.value, x509.SubjectKeyIdentifier):
+                    continue
                 cert_builder = cert_builder.add_extension(extension.value, critical=extension.critical)
+            if self.create_subject_key_identifier:
+                cert_builder = cert_builder.add_extension(
+                    x509.SubjectKeyIdentifier.from_public_key(self.csr.public_key()),
+                    critical=False
+                )
+            if self.create_authority_key_identifier:
+                try:
+                    ext = self.ca_cert.extensions.get_extension_for_class(x509.SubjectKeyIdentifier)
+                    cert_builder = cert_builder.add_extension(
+                        x509.AuthorityKeyIdentifier.from_issuer_subject_key_identifier(ext.value)
+                        if CRYPTOGRAPHY_VERSION >= LooseVersion('2.7') else
+                        x509.AuthorityKeyIdentifier.from_issuer_subject_key_identifier(ext),
+                        critical=False
+                    )
+                except cryptography.x509.ExtensionNotFound:
+                    cert_builder = cert_builder.add_extension(
+                        x509.AuthorityKeyIdentifier.from_issuer_public_key(self.ca_cert.public_key()),
+                        critical=False
+                    )
 
             certificate = cert_builder.sign(
                 private_key=self.ca_private_key, algorithm=self.digest,
@@ -1264,6 +1351,32 @@ def generate(self, module):
         if module.set_fs_attributes_if_different(file_args, False):
             self.changed = True
 
+    def check(self, module, perms_required=True):
+        """Ensure the resource is in its desired state."""
+
+        if not super(OwnCACertificateCryptography, self).check(module, perms_required):
+            return False
+
+        # Check AuthorityKeyIdentifier
+        if self.create_authority_key_identifier:
+            try:
+                ext = self.ca_cert.extensions.get_extension_for_class(x509.SubjectKeyIdentifier)
+                expected_ext = (
+                    x509.AuthorityKeyIdentifier.from_issuer_subject_key_identifier(ext.value)
+                    if CRYPTOGRAPHY_VERSION >= LooseVersion('2.7') else
+                    x509.AuthorityKeyIdentifier.from_issuer_subject_key_identifier(ext)
+                )
+            except cryptography.x509.ExtensionNotFound:
+                expected_ext = x509.AuthorityKeyIdentifier.from_issuer_public_key(self.ca_cert.public_key())
+            try:
+                ext = self.cert.extensions.get_extension_for_class(x509.AuthorityKeyIdentifier)
+                if ext.value != expected_ext:
+                    return False
+            except cryptography.x509.ExtensionNotFound as dummy:
+                return False
+
+        return True
+
     def dump(self, check_mode=False):
 
         result = {
@@ -1303,6 +1416,10 @@ def __init__(self, module):
         self.digest = module.params['ownca_digest']
         self.version = module.params['ownca_version']
         self.serial_number = randint(1000, 99999)
+        if module.params['ownca_create_subject_key_identifier']:
+            module.fail_json(msg='ownca_create_subject_key_identifier cannot be used with the pyOpenSSL backend!')
+        if module.params['ownca_create_authority_key_identifier']:
+            module.warn('ownca_create_authority_key_identifier is ignored by the pyOpenSSL backend!')
         self.ca_cert_path = module.params['ownca_path']
         self.ca_privatekey_path = module.params['ownca_privatekey_path']
         self.ca_privatekey_passphrase = module.params['ownca_privatekey_passphrase']
@@ -2268,6 +2385,7 @@ def main():
             selfsigned_digest=dict(type='str', default='sha256'),
             selfsigned_not_before=dict(type='str', default='+0s', aliases=['selfsigned_notBefore']),
             selfsigned_not_after=dict(type='str', default='+3650d', aliases=['selfsigned_notAfter']),
+            selfsigned_create_subject_key_identifier=dict(type='bool', default=False),
 
             # provider: ownca
             ownca_path=dict(type='path'),
@@ -2277,6 +2395,8 @@ def main():
             ownca_version=dict(type='int', default=3),
             ownca_not_before=dict(type='str', default='+0s'),
             ownca_not_after=dict(type='str', default='+3650d'),
+            ownca_create_subject_key_identifier=dict(type='bool', default=False),
+            ownca_create_authority_key_identifier=dict(type='bool', default=True),
 
             # provider: acme
             acme_accountkey_path=dict(type='path'),
diff --git a/lib/ansible/modules/crypto/openssl_certificate_info.py b/lib/ansible/modules/crypto/openssl_certificate_info.py
index 9a1a77e6d1792c..eb47a56a05fe05 100644
--- a/lib/ansible/modules/crypto/openssl_certificate_info.py
+++ b/lib/ansible/modules/crypto/openssl_certificate_info.py
@@ -238,10 +238,45 @@
                  or not.
     returned: success
     type: dict
+subject_key_identifier:
+    description:
+        - The CSR's subject key identifier.
+        - The identifier is returned in hexadecial, with C(:) used to separate bytes.
+        - Is C(none) if the C(SubjectKeyIdentifier) extension is not present.
+    returned: success and if the pyOpenSSL backend is I(not) used
+    type: str
+    sample: '00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff:00:11:22:33'
+    version_added: "2.9"
+authority_key_identifier:
+    description:
+        - The CSR's authority key identifier.
+        - The identifier is returned in hexadecial, with C(:) used to separate bytes.
+        - Is C(none) if the C(AuthorityKeyIdentifier) extension is not present.
+    returned: success and if the pyOpenSSL backend is I(not) used
+    type: str
+    sample: '00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff:00:11:22:33'
+    version_added: "2.9"
+authority_cert_issuer:
+    description:
+        - The CSR's authority cert issuer as a list of strings.
+        - Is C(none) if the C(AuthorityKeyIdentifier) extension is not present.
+    returned: success and if the pyOpenSSL backend is I(not) used
+    type: list
+    sample: "[DNS:www.ansible.com, IP:1.2.3.4]"
+    version_added: "2.9"
+authority_cert_serial_number:
+    description:
+        - The CSR's authority cert serial number.
+        - Is C(none) if the C(AuthorityKeyIdentifier) extension is not present.
+    returned: success and if the pyOpenSSL backend is I(not) used
+    type: int
+    sample: '12345'
+    version_added: "2.9"
 '''
 
 
 import abc
+import binascii
 import datetime
 import os
 import traceback
@@ -392,6 +427,14 @@ def _get_not_after(self):
     def _get_public_key(self, binary):
         pass
 
+    @abc.abstractmethod
+    def _get_subject_key_identifier(self):
+        pass
+
+    @abc.abstractmethod
+    def _get_authority_key_identifier(self):
+        pass
+
     @abc.abstractmethod
     def _get_serial_number(self):
         pass
@@ -437,6 +480,21 @@ def get_info(self):
         pk = self._get_public_key(binary=True)
         result['public_key_fingerprints'] = crypto_utils.get_fingerprint_of_bytes(pk) if pk is not None else dict()
 
+        if self.backend != 'pyopenssl':
+            ski = self._get_subject_key_identifier()
+            if ski is not None:
+                ski = to_native(binascii.hexlify(ski))
+                ski = ':'.join([ski[i:i + 2] for i in range(0, len(ski), 2)])
+            result['subject_key_identifier'] = ski
+
+            aki, aci, acsn = self._get_authority_key_identifier()
+            if aki is not None:
+                aki = to_native(binascii.hexlify(aki))
+                aki = ':'.join([aki[i:i + 2] for i in range(0, len(aki), 2)])
+            result['authority_key_identifier'] = aki
+            result['authority_cert_issuer'] = aci
+            result['authority_cert_serial_number'] = acsn
+
         result['serial_number'] = self._get_serial_number()
         result['extensions_by_oid'] = self._get_all_extensions()
 
@@ -563,6 +621,23 @@ def _get_public_key(self, binary):
             serialization.PublicFormat.SubjectPublicKeyInfo
         )
 
+    def _get_subject_key_identifier(self):
+        try:
+            ext = self.cert.extensions.get_extension_for_class(x509.SubjectKeyIdentifier)
+            return ext.value.digest
+        except cryptography.x509.ExtensionNotFound:
+            return None
+
+    def _get_authority_key_identifier(self):
+        try:
+            ext = self.cert.extensions.get_extension_for_class(x509.AuthorityKeyIdentifier)
+            issuer = None
+            if ext.value.authority_cert_issuer is not None:
+                issuer = [crypto_utils.cryptography_decode_name(san) for san in ext.value.authority_cert_issuer]
+            return ext.value.key_identifier, issuer, ext.value.authority_cert_serial_number
+        except cryptography.x509.ExtensionNotFound:
+            return None, None, None
+
     def _get_serial_number(self):
         return self.cert.serial_number
 
@@ -675,6 +750,14 @@ def _get_public_key(self, binary):
                 self.module.warn('Your pyOpenSSL version does not support dumping public keys. '
                                  'Please upgrade to version 16.0 or newer, or use the cryptography backend.')
 
+    def _get_subject_key_identifier(self):
+        # Won't be implemented
+        return None
+
+    def _get_authority_key_identifier(self):
+        # Won't be implemented
+        return None, None, None
+
     def _get_serial_number(self):
         return self.cert.get_serial_number()
 
diff --git a/lib/ansible/modules/crypto/openssl_csr.py b/lib/ansible/modules/crypto/openssl_csr.py
index dc2fa9ca64a1bf..d849806d108964 100644
--- a/lib/ansible/modules/crypto/openssl_csr.py
+++ b/lib/ansible/modules/crypto/openssl_csr.py
@@ -200,6 +200,51 @@
         type: bool
         default: no
         version_added: "2.8"
+    create_subject_key_identifier:
+        description:
+            - Create the Subject Key Identifier from the public key.
+            - Note that this is only supported if the C(cryptography) backend is used!
+        type: bool
+        default: no
+        version_added: "2.9"
+    subject_key_identifier:
+        description:
+            - The subject key identifier as a hex string, where two bytes are separated by colons.
+            - "Example: C(00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff:00:11:22:33)"
+            - Note that this option can only be used if I(create_subject_key_identifier) is C(no).
+            - Note that this is only supported if the C(cryptography) backend is used!
+        type: str
+        version_added: "2.9"
+    authority_key_identifier:
+        description:
+            - The authority key identifier as a hex string, where two bytes are separated by colons.
+            - "Example: C(00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff:00:11:22:33)"
+            - If specified, I(authority_cert_issuer) must also be specified.
+            - Note that this is only supported if the C(cryptography) backend is used!
+            - The C(AuthorityKeyIdentifier) will only be added if at least one of I(authority_key_identifier),
+              I(authority_cert_issuer) and I(authority_cert_serial_number) is specified.
+        type: str
+        version_added: "2.9"
+    authority_cert_issuer:
+        description:
+            - Names that will be present in the authority cert issuer field of the certificate signing request.
+            - Values must be prefixed by their options. (i.e., C(email), C(URI), C(DNS), C(RID), C(IP), C(dirName),
+              C(otherName) and the ones specific to your CA)
+            - "Example: C(DNS:ca.example.org)"
+            - If specified, I(authority_key_identifier) must also be specified.
+            - Note that this is only supported if the C(cryptography) backend is used!
+            - The C(AuthorityKeyIdentifier) will only be added if at least one of I(authority_key_identifier),
+              I(authority_cert_issuer) and I(authority_cert_serial_number) is specified.
+        type: list
+        version_added: "2.9"
+    authority_cert_serial_number:
+        description:
+            - The authority cert serial number.
+            - Note that this is only supported if the C(cryptography) backend is used!
+            - The C(AuthorityKeyIdentifier) will only be added if at least one of I(authority_key_identifier),
+              I(authority_cert_issuer) and I(authority_cert_serial_number) is specified.
+        type: int
+        version_added: "2.9"
 extends_documentation_fragment:
 - files
 notes:
@@ -329,6 +374,7 @@
 '''
 
 import abc
+import binascii
 import os
 import traceback
 from distutils.version import LooseVersion
@@ -406,9 +452,17 @@ def __init__(self, module):
         self.basicConstraints_critical = module.params['basic_constraints_critical']
         self.ocspMustStaple = module.params['ocsp_must_staple']
         self.ocspMustStaple_critical = module.params['ocsp_must_staple_critical']
+        self.create_subject_key_identifier = module.params['create_subject_key_identifier']
+        self.subject_key_identifier = module.params['subject_key_identifier']
+        self.authority_key_identifier = module.params['authority_key_identifier']
+        self.authority_cert_issuer = module.params['authority_cert_issuer']
+        self.authority_cert_serial_number = module.params['authority_cert_serial_number']
         self.request = None
         self.privatekey = None
 
+        if self.create_subject_key_identifier and self.subject_key_identifier is not None:
+            module.fail_json(msg='subject_key_identifier cannot be specified if create_subject_key_identifier is true')
+
         self.backup = module.params['backup']
         self.backup_file = None
 
@@ -432,6 +486,18 @@ def __init__(self, module):
                     self.subjectAltName = ['DNS:%s' % sub[1]]
                     break
 
+        if self.subject_key_identifier is not None:
+            try:
+                self.subject_key_identifier = binascii.unhexlify(self.subject_key_identifier.replace(':', ''))
+            except Exception as e:
+                raise CertificateSigningRequestError('Cannot parse subject_key_identifier: {0}'.format(e))
+
+        if self.authority_key_identifier is not None:
+            try:
+                self.authority_key_identifier = binascii.unhexlify(self.authority_key_identifier.replace(':', ''))
+            except Exception as e:
+                raise CertificateSigningRequestError('Cannot parse authority_key_identifier: {0}'.format(e))
+
     @abc.abstractmethod
     def _generate_csr(self):
         pass
@@ -496,6 +562,11 @@ def dump(self):
 class CertificateSigningRequestPyOpenSSL(CertificateSigningRequestBase):
 
     def __init__(self, module):
+        if module.params['create_subject_key_identifier']:
+            module.fail_json(msg='You cannot use create_subject_key_identifier with the pyOpenSSL backend!')
+        for o in ('subject_key_identifier', 'authority_key_identifier', 'authority_cert_issuer', 'authority_cert_serial_number'):
+            if module.params[o] is not None:
+                module.fail_json(msg='You cannot use {0} with the pyOpenSSL backend!'.format(o))
         super(CertificateSigningRequestPyOpenSSL, self).__init__(module)
 
     def _generate_csr(self):
@@ -691,6 +762,23 @@ def _generate_csr(self):
                     critical=self.ocspMustStaple_critical
                 )
 
+        if self.create_subject_key_identifier:
+            csr = csr.add_extension(
+                cryptography.x509.SubjectKeyIdentifier.from_public_key(self.privatekey.public_key()),
+                critical=False
+            )
+        elif self.subject_key_identifier is not None:
+            csr = csr.add_extension(cryptography.x509.SubjectKeyIdentifier(self.subject_key_identifier), critical=False)
+
+        if self.authority_key_identifier is not None or self.authority_cert_issuer is not None or self.authority_cert_serial_number is not None:
+            issuers = None
+            if self.authority_cert_issuer is not None:
+                issuers = [crypto_utils.cryptography_get_name(n) for n in self.authority_cert_issuer]
+            csr = csr.add_extension(
+                cryptography.x509.AuthorityKeyIdentifier(self.authority_key_identifier, issuers, self.authority_cert_serial_number),
+                critical=False
+            )
+
         digest = None
         if self.digest == 'sha256':
             digest = cryptography.hazmat.primitives.hashes.SHA256()
@@ -806,11 +894,42 @@ def _check_ocspMustStaple(extensions):
             else:
                 return tlsfeature_ext is None
 
+        def _check_subject_key_identifier(extensions):
+            ext = _find_extension(extensions, cryptography.x509.SubjectKeyIdentifier)
+            if self.create_subject_key_identifier or self.subject_key_identifier is not None:
+                if not ext or ext.critical:
+                    return False
+                if self.create_subject_key_identifier:
+                    digest = cryptography.x509.SubjectKeyIdentifier.from_public_key(self.privatekey.public_key()).digest
+                    return ext.value.digest == digest
+                else:
+                    return ext.value.digest == self.subject_key_identifier
+            else:
+                return ext is None
+
+        def _check_authority_key_identifier(extensions):
+            ext = _find_extension(extensions, cryptography.x509.AuthorityKeyIdentifier)
+            if self.authority_key_identifier is not None or self.authority_cert_issuer is not None or self.authority_cert_serial_number is not None:
+                if not ext or ext.critical:
+                    return False
+                aci = None
+                csr_aci = None
+                if self.authority_cert_issuer is not None:
+                    aci = [str(crypto_utils.cryptography_get_name(n)) for n in self.authority_cert_issuer]
+                if ext.value.authority_cert_issuer is not None:
+                    csr_aci = [str(n) for n in ext.value.authority_cert_issuer]
+                return (ext.value.key_identifier == self.authority_key_identifier
+                        and csr_aci == aci
+                        and ext.value.authority_cert_serial_number == self.authority_cert_serial_number)
+            else:
+                return ext is None
+
         def _check_extensions(csr):
             extensions = csr.extensions
             return (_check_subjectAltName(extensions) and _check_keyUsage(extensions) and
                     _check_extenededKeyUsage(extensions) and _check_basicConstraints(extensions) and
-                    _check_ocspMustStaple(extensions))
+                    _check_ocspMustStaple(extensions) and _check_subject_key_identifier(extensions) and
+                    _check_authority_key_identifier(extensions))
 
         def _check_signature(csr):
             if not csr.is_signature_valid:
@@ -865,8 +984,14 @@ def main():
             ocsp_must_staple=dict(type='bool', default=False, aliases=['ocspMustStaple']),
             ocsp_must_staple_critical=dict(type='bool', default=False, aliases=['ocspMustStaple_critical']),
             backup=dict(type='bool', default=False),
+            create_subject_key_identifier=dict(type='bool', default=False),
+            subject_key_identifier=dict(type='str'),
+            authority_key_identifier=dict(type='str'),
+            authority_cert_issuer=dict(type='list', elements='str'),
+            authority_cert_serial_number=dict(type='int'),
             select_crypto_backend=dict(type='str', default='auto', choices=['auto', 'cryptography', 'pyopenssl']),
         ),
+        required_together=[('authority_cert_issuer', 'authority_cert_serial_number')],
         add_file_common_args=True,
         supports_check_mode=True,
     )
diff --git a/lib/ansible/modules/crypto/openssl_csr_info.py b/lib/ansible/modules/crypto/openssl_csr_info.py
index 47e44601a966b2..77635283da2dc2 100644
--- a/lib/ansible/modules/crypto/openssl_csr_info.py
+++ b/lib/ansible/modules/crypto/openssl_csr_info.py
@@ -160,10 +160,45 @@
     type: dict
     sample: "{'sha256': 'd4:b3:aa:6d:c8:04:ce:4e:ba:f6:29:4d:92:a3:94:b0:c2:ff:bd:bf:33:63:11:43:34:0f:51:b0:95:09:2f:63',
               'sha512': 'f7:07:4a:f0:b0:f0:e6:8b:95:5f:f9:e6:61:0a:32:68:f1..."
+subject_key_identifier:
+    description:
+        - The CSR's subject key identifier.
+        - The identifier is returned in hexadecial, with C(:) used to separate bytes.
+        - Is C(none) if the C(SubjectKeyIdentifier) extension is not present.
+    returned: success and if the pyOpenSSL backend is I(not) used
+    type: str
+    sample: '00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff:00:11:22:33'
+    version_added: "2.9"
+authority_key_identifier:
+    description:
+        - The CSR's authority key identifier.
+        - The identifier is returned in hexadecial, with C(:) used to separate bytes.
+        - Is C(none) if the C(AuthorityKeyIdentifier) extension is not present.
+    returned: success and if the pyOpenSSL backend is I(not) used
+    type: str
+    sample: '00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff:00:11:22:33'
+    version_added: "2.9"
+authority_cert_issuer:
+    description:
+        - The CSR's authority cert issuer as a list of strings.
+        - Is C(none) if the C(AuthorityKeyIdentifier) extension is not present.
+    returned: success and if the pyOpenSSL backend is I(not) used
+    type: list
+    sample: "[DNS:www.ansible.com, IP:1.2.3.4]"
+    version_added: "2.9"
+authority_cert_serial_number:
+    description:
+        - The CSR's authority cert serial number.
+        - Is C(none) if the C(AuthorityKeyIdentifier) extension is not present.
+    returned: success and if the pyOpenSSL backend is I(not) used
+    type: int
+    sample: '12345'
+    version_added: "2.9"
 '''
 
 
 import abc
+import binascii
 import os
 import traceback
 from distutils.version import LooseVersion
@@ -258,6 +293,14 @@ def _get_subject_alt_name(self):
     def _get_public_key(self, binary):
         pass
 
+    @abc.abstractmethod
+    def _get_subject_key_identifier(self):
+        pass
+
+    @abc.abstractmethod
+    def _get_authority_key_identifier(self):
+        pass
+
     @abc.abstractmethod
     def _get_all_extensions(self):
         pass
@@ -285,6 +328,21 @@ def get_info(self):
         pk = self._get_public_key(binary=True)
         result['public_key_fingerprints'] = crypto_utils.get_fingerprint_of_bytes(pk) if pk is not None else dict()
 
+        if self.backend != 'pyopenssl':
+            ski = self._get_subject_key_identifier()
+            if ski is not None:
+                ski = to_native(binascii.hexlify(ski))
+                ski = ':'.join([ski[i:i + 2] for i in range(0, len(ski), 2)])
+            result['subject_key_identifier'] = ski
+
+            aki, aci, acsn = self._get_authority_key_identifier()
+            if aki is not None:
+                aki = to_native(binascii.hexlify(aki))
+                aki = ':'.join([aki[i:i + 2] for i in range(0, len(aki), 2)])
+            result['authority_key_identifier'] = aki
+            result['authority_cert_issuer'] = aci
+            result['authority_cert_serial_number'] = acsn
+
         result['extensions_by_oid'] = self._get_all_extensions()
 
         result['signature_valid'] = self._is_signature_valid()
@@ -394,6 +452,23 @@ def _get_public_key(self, binary):
             serialization.PublicFormat.SubjectPublicKeyInfo
         )
 
+    def _get_subject_key_identifier(self):
+        try:
+            ext = self.csr.extensions.get_extension_for_class(x509.SubjectKeyIdentifier)
+            return ext.value.digest
+        except cryptography.x509.ExtensionNotFound:
+            return None
+
+    def _get_authority_key_identifier(self):
+        try:
+            ext = self.csr.extensions.get_extension_for_class(x509.AuthorityKeyIdentifier)
+            issuer = None
+            if ext.value.authority_cert_issuer is not None:
+                issuer = [crypto_utils.cryptography_decode_name(san) for san in ext.value.authority_cert_issuer]
+            return ext.value.key_identifier, issuer, ext.value.authority_cert_serial_number
+        except cryptography.x509.ExtensionNotFound:
+            return None, None, None
+
     def _get_all_extensions(self):
         return crypto_utils.cryptography_get_extensions_from_csr(self.csr)
 
@@ -486,6 +561,14 @@ def _get_public_key(self, binary):
                 self.module.warn('Your pyOpenSSL version does not support dumping public keys. '
                                  'Please upgrade to version 16.0 or newer, or use the cryptography backend.')
 
+    def _get_subject_key_identifier(self):
+        # Won't be implemented
+        return None
+
+    def _get_authority_key_identifier(self):
+        # Won't be implemented
+        return None, None, None
+
     def _get_all_extensions(self):
         return crypto_utils.pyopenssl_get_extensions_from_csr(self.csr)
 
diff --git a/test/integration/targets/openssl_certificate/tasks/ownca.yml b/test/integration/targets/openssl_certificate/tasks/ownca.yml
index 2a5569a022a1c5..4cd09b097e9a4d 100644
--- a/test/integration/targets/openssl_certificate/tasks/ownca.yml
+++ b/test/integration/targets/openssl_certificate/tasks/ownca.yml
@@ -40,6 +40,7 @@
     privatekey_path: '{{ output_dir }}/ca_privatekey.pem'
     provider: selfsigned
     selfsigned_digest: sha256
+    selfsigned_create_subject_key_identifier: '{{ select_crypto_backend != "pyopenssl" }}'
     select_crypto_backend: '{{ select_crypto_backend }}'
 
 - name: (OwnCA, {{select_crypto_backend}}) Generate selfsigned CA certificate (privatekey passphrase)
@@ -50,6 +51,7 @@
     privatekey_passphrase: hunter2
     provider: selfsigned
     selfsigned_digest: sha256
+    selfsigned_create_subject_key_identifier: '{{ select_crypto_backend != "pyopenssl" }}'
     select_crypto_backend: '{{ select_crypto_backend }}'
 
 - name: (OwnCA, {{select_crypto_backend}}) Generate ownca certificate
@@ -308,4 +310,134 @@
     select_crypto_backend: '{{ select_crypto_backend }}'
   register: ownca_backup_5
 
+- name: (OwnCA, {{select_crypto_backend}}) Create subject key identifier
+  openssl_certificate:
+    path: '{{ output_dir }}/ownca_cert_ski.pem'
+    csr_path: '{{ output_dir }}/csr_ecc.csr'
+    ownca_path: '{{ output_dir }}/ca_cert.pem'
+    ownca_privatekey_path: '{{ output_dir }}/privatekey.pem'
+    provider: ownca
+    ownca_digest: sha256
+    ownca_create_subject_key_identifier: yes
+    select_crypto_backend: '{{ select_crypto_backend }}'
+  when: select_crypto_backend != 'pyopenssl'
+  register: ownca_subject_key_identifier_1
+
+- name: (OwnCA, {{select_crypto_backend}}) Create subject key identifier (idempotency)
+  openssl_certificate:
+    path: '{{ output_dir }}/ownca_cert_ski.pem'
+    csr_path: '{{ output_dir }}/csr_ecc.csr'
+    ownca_path: '{{ output_dir }}/ca_cert.pem'
+    ownca_privatekey_path: '{{ output_dir }}/privatekey.pem'
+    provider: ownca
+    ownca_digest: sha256
+    ownca_create_subject_key_identifier: yes
+    select_crypto_backend: '{{ select_crypto_backend }}'
+  when: select_crypto_backend != 'pyopenssl'
+  register: ownca_subject_key_identifier_2
+
+- name: (OwnCA, {{select_crypto_backend}}) Create subject key identifier (remove)
+  openssl_certificate:
+    path: '{{ output_dir }}/ownca_cert_ski.pem'
+    csr_path: '{{ output_dir }}/csr_ecc.csr'
+    ownca_path: '{{ output_dir }}/ca_cert.pem'
+    ownca_privatekey_path: '{{ output_dir }}/privatekey.pem'
+    provider: ownca
+    ownca_digest: sha256
+    ownca_create_subject_key_identifier: no
+    select_crypto_backend: '{{ select_crypto_backend }}'
+  when: select_crypto_backend != 'pyopenssl'
+  register: ownca_subject_key_identifier_3
+
+- name: (OwnCA, {{select_crypto_backend}}) Create subject key identifier (remove idempotency)
+  openssl_certificate:
+    path: '{{ output_dir }}/ownca_cert_ski.pem'
+    csr_path: '{{ output_dir }}/csr_ecc.csr'
+    ownca_path: '{{ output_dir }}/ca_cert.pem'
+    ownca_privatekey_path: '{{ output_dir }}/privatekey.pem'
+    provider: ownca
+    ownca_digest: sha256
+    ownca_create_subject_key_identifier: no
+    select_crypto_backend: '{{ select_crypto_backend }}'
+  when: select_crypto_backend != 'pyopenssl'
+  register: ownca_subject_key_identifier_4
+
+- name: (OwnCA, {{select_crypto_backend}}) Create subject key identifier (re-enable)
+  openssl_certificate:
+    path: '{{ output_dir }}/ownca_cert_ski.pem'
+    csr_path: '{{ output_dir }}/csr_ecc.csr'
+    ownca_path: '{{ output_dir }}/ca_cert.pem'
+    ownca_privatekey_path: '{{ output_dir }}/privatekey.pem'
+    provider: ownca
+    ownca_digest: sha256
+    ownca_create_subject_key_identifier: yes
+    select_crypto_backend: '{{ select_crypto_backend }}'
+  when: select_crypto_backend != 'pyopenssl'
+  register: ownca_subject_key_identifier_5
+
+- name: (OwnCA, {{select_crypto_backend}}) Create authority key identifier
+  openssl_certificate:
+    path: '{{ output_dir }}/ownca_cert_aki.pem'
+    csr_path: '{{ output_dir }}/csr_ecc.csr'
+    ownca_path: '{{ output_dir }}/ca_cert.pem'
+    ownca_privatekey_path: '{{ output_dir }}/privatekey.pem'
+    provider: ownca
+    ownca_digest: sha256
+    ownca_create_authority_key_identifier: yes
+    select_crypto_backend: '{{ select_crypto_backend }}'
+  when: select_crypto_backend != 'pyopenssl'
+  register: ownca_authority_key_identifier_1
+
+- name: (OwnCA, {{select_crypto_backend}}) Create authority key identifier (idempotency)
+  openssl_certificate:
+    path: '{{ output_dir }}/ownca_cert_aki.pem'
+    csr_path: '{{ output_dir }}/csr_ecc.csr'
+    ownca_path: '{{ output_dir }}/ca_cert.pem'
+    ownca_privatekey_path: '{{ output_dir }}/privatekey.pem'
+    provider: ownca
+    ownca_digest: sha256
+    ownca_create_authority_key_identifier: yes
+    select_crypto_backend: '{{ select_crypto_backend }}'
+  when: select_crypto_backend != 'pyopenssl'
+  register: ownca_authority_key_identifier_2
+
+- name: (OwnCA, {{select_crypto_backend}}) Create authority key identifier (remove)
+  openssl_certificate:
+    path: '{{ output_dir }}/ownca_cert_aki.pem'
+    csr_path: '{{ output_dir }}/csr_ecc.csr'
+    ownca_path: '{{ output_dir }}/ca_cert.pem'
+    ownca_privatekey_path: '{{ output_dir }}/privatekey.pem'
+    provider: ownca
+    ownca_digest: sha256
+    ownca_create_authority_key_identifier: no
+    select_crypto_backend: '{{ select_crypto_backend }}'
+  when: select_crypto_backend != 'pyopenssl'
+  register: ownca_authority_key_identifier_3
+
+- name: (OwnCA, {{select_crypto_backend}}) Create authority key identifier (remove idempotency)
+  openssl_certificate:
+    path: '{{ output_dir }}/ownca_cert_aki.pem'
+    csr_path: '{{ output_dir }}/csr_ecc.csr'
+    ownca_path: '{{ output_dir }}/ca_cert.pem'
+    ownca_privatekey_path: '{{ output_dir }}/privatekey.pem'
+    provider: ownca
+    ownca_digest: sha256
+    ownca_create_authority_key_identifier: no
+    select_crypto_backend: '{{ select_crypto_backend }}'
+  when: select_crypto_backend != 'pyopenssl'
+  register: ownca_authority_key_identifier_4
+
+- name: (OwnCA, {{select_crypto_backend}}) Create authority key identifier (re-add)
+  openssl_certificate:
+    path: '{{ output_dir }}/ownca_cert_aki.pem'
+    csr_path: '{{ output_dir }}/csr_ecc.csr'
+    ownca_path: '{{ output_dir }}/ca_cert.pem'
+    ownca_privatekey_path: '{{ output_dir }}/privatekey.pem'
+    provider: ownca
+    ownca_digest: sha256
+    ownca_create_authority_key_identifier: yes
+    select_crypto_backend: '{{ select_crypto_backend }}'
+  when: select_crypto_backend != 'pyopenssl'
+  register: ownca_authority_key_identifier_5
+
 - import_tasks: ../tests/validate_ownca.yml
diff --git a/test/integration/targets/openssl_certificate/tasks/selfsigned.yml b/test/integration/targets/openssl_certificate/tasks/selfsigned.yml
index 8e145197c7fd3f..7b66d097d523da 100644
--- a/test/integration/targets/openssl_certificate/tasks/selfsigned.yml
+++ b/test/integration/targets/openssl_certificate/tasks/selfsigned.yml
@@ -308,4 +308,64 @@
     select_crypto_backend: '{{ select_crypto_backend }}'
   register: selfsigned_backup_5
 
+- name: (Selfsigned, {{select_crypto_backend}}) Create subject key identifier test
+  openssl_certificate:
+    path: '{{ output_dir }}/selfsigned_cert_ski.pem'
+    csr_path: '{{ output_dir }}/csr_ecc.csr'
+    privatekey_path: '{{ output_dir }}/privatekey_ecc.pem'
+    provider: selfsigned
+    selfsigned_digest: sha256
+    selfsigned_create_subject_key_identifier: yes
+    select_crypto_backend: '{{ select_crypto_backend }}'
+  when: select_crypto_backend != 'pyopenssl'
+  register: selfsigned_subject_key_identifier_1
+
+- name: (Selfsigned, {{select_crypto_backend}}) Create subject key identifier test (idempotency)
+  openssl_certificate:
+    path: '{{ output_dir }}/selfsigned_cert_ski.pem'
+    csr_path: '{{ output_dir }}/csr_ecc.csr'
+    privatekey_path: '{{ output_dir }}/privatekey_ecc.pem'
+    provider: selfsigned
+    selfsigned_digest: sha256
+    selfsigned_create_subject_key_identifier: yes
+    select_crypto_backend: '{{ select_crypto_backend }}'
+  when: select_crypto_backend != 'pyopenssl'
+  register: selfsigned_subject_key_identifier_2
+
+- name: (Selfsigned, {{select_crypto_backend}}) Create subject key identifier test (remove)
+  openssl_certificate:
+    path: '{{ output_dir }}/selfsigned_cert_ski.pem'
+    csr_path: '{{ output_dir }}/csr_ecc.csr'
+    privatekey_path: '{{ output_dir }}/privatekey_ecc.pem'
+    provider: selfsigned
+    selfsigned_digest: sha256
+    selfsigned_create_subject_key_identifier: no
+    select_crypto_backend: '{{ select_crypto_backend }}'
+  when: select_crypto_backend != 'pyopenssl'
+  register: selfsigned_subject_key_identifier_3
+
+- name: (Selfsigned, {{select_crypto_backend}}) Create subject key identifier test (remove idempotency)
+  openssl_certificate:
+    path: '{{ output_dir }}/selfsigned_cert_ski.pem'
+    csr_path: '{{ output_dir }}/csr_ecc.csr'
+    privatekey_path: '{{ output_dir }}/privatekey_ecc.pem'
+    provider: selfsigned
+    selfsigned_digest: sha256
+    selfsigned_create_subject_key_identifier: no
+    select_crypto_backend: '{{ select_crypto_backend }}'
+  when: select_crypto_backend != 'pyopenssl'
+  register: selfsigned_subject_key_identifier_4
+
+- name: (Selfsigned, {{select_crypto_backend}}) Create subject key identifier test (re-enable)
+  openssl_certificate:
+    path: '{{ output_dir }}/selfsigned_cert_ski.pem'
+    csr_path: '{{ output_dir }}/csr_ecc.csr'
+    privatekey_path: '{{ output_dir }}/privatekey_ecc.pem'
+    provider: selfsigned
+    selfsigned_digest: sha256
+    selfsigned_create_subject_key_identifier: yes
+    select_crypto_backend: '{{ select_crypto_backend }}'
+  when: select_crypto_backend != 'pyopenssl'
+  register: selfsigned_subject_key_identifier_5
+
 - import_tasks: ../tests/validate_selfsigned.yml
diff --git a/test/integration/targets/openssl_certificate/tests/validate_ownca.yml b/test/integration/targets/openssl_certificate/tests/validate_ownca.yml
index 3d90d75fcca535..a8c6114034b44c 100644
--- a/test/integration/targets/openssl_certificate/tests/validate_ownca.yml
+++ b/test/integration/targets/openssl_certificate/tests/validate_ownca.yml
@@ -120,3 +120,23 @@
       - ownca_backup_4.backup_file is string
       - ownca_backup_5 is not changed
       - ownca_backup_5.backup_file is undefined
+
+- name: Check create subject key identifier
+  assert:
+    that:
+      - ownca_subject_key_identifier_1 is changed
+      - ownca_subject_key_identifier_2 is not changed
+      - ownca_subject_key_identifier_3 is changed
+      - ownca_subject_key_identifier_4 is not changed
+      - ownca_subject_key_identifier_5 is changed
+  when: select_crypto_backend != 'pyopenssl'
+
+- name: Check create authority key identifier
+  assert:
+    that:
+      - ownca_authority_key_identifier_1 is changed
+      - ownca_authority_key_identifier_2 is not changed
+      - ownca_authority_key_identifier_3 is changed
+      - ownca_authority_key_identifier_4 is not changed
+      - ownca_authority_key_identifier_5 is changed
+  when: select_crypto_backend != 'pyopenssl'
diff --git a/test/integration/targets/openssl_certificate/tests/validate_selfsigned.yml b/test/integration/targets/openssl_certificate/tests/validate_selfsigned.yml
index 1c24effa11d33d..432a2d8d0899bb 100644
--- a/test/integration/targets/openssl_certificate/tests/validate_selfsigned.yml
+++ b/test/integration/targets/openssl_certificate/tests/validate_selfsigned.yml
@@ -126,3 +126,13 @@
       - selfsigned_backup_4.backup_file is string
       - selfsigned_backup_5 is not changed
       - selfsigned_backup_5.backup_file is undefined
+
+- name: Check create subject key identifier
+  assert:
+    that:
+      - selfsigned_subject_key_identifier_1 is changed
+      - selfsigned_subject_key_identifier_2 is not changed
+      - selfsigned_subject_key_identifier_3 is changed
+      - selfsigned_subject_key_identifier_4 is not changed
+      - selfsigned_subject_key_identifier_5 is changed
+  when: select_crypto_backend != 'pyopenssl'
diff --git a/test/integration/targets/openssl_certificate_info/tasks/impl.yml b/test/integration/targets/openssl_certificate_info/tasks/impl.yml
index 125b9e85a7f796..19191a27bb905f 100644
--- a/test/integration/targets/openssl_certificate_info/tasks/impl.yml
+++ b/test/integration/targets/openssl_certificate_info/tasks/impl.yml
@@ -18,6 +18,19 @@
       - "['organizationalUnitName', 'Crypto Department'] in result.subject_ordered"
       - "['organizationalUnitName', 'ACME Department'] in result.subject_ordered"
 
+- name: Check SubjectKeyIdentifier and AuthorityKeyIdentifier
+  assert:
+    that:
+      - result.subject_key_identifier == "00:11:22:33"
+      - result.authority_key_identifier == "44:55:66:77"
+      - result.authority_cert_issuer == expected_authority_cert_issuer
+      - result.authority_cert_serial_number == 12345
+  vars:
+    expected_authority_cert_issuer:
+      - "DNS:ca.example.org"
+      - "IP:1.2.3.4"
+  when: select_crypto_backend != 'pyopenssl' and cryptography_version.stdout is version('1.3', '>=')
+
 - name: Update result list
   set_fact:
     info_results: "{{ info_results + [result] }}"
@@ -47,6 +60,18 @@
     select_crypto_backend: '{{ select_crypto_backend }}'
   register: result
 
+- name: Check AuthorityKeyIdentifier
+  assert:
+    that:
+      - result.authority_key_identifier is none
+      - result.authority_cert_issuer == expected_authority_cert_issuer
+      - result.authority_cert_serial_number == 12345
+  vars:
+    expected_authority_cert_issuer:
+      - "DNS:ca.example.org"
+      - "IP:1.2.3.4"
+  when: select_crypto_backend != 'pyopenssl' and cryptography_version.stdout is version('1.3', '>=')
+
 - name: Update result list
   set_fact:
     info_results: "{{ info_results + [result] }}"
@@ -57,6 +82,14 @@
     select_crypto_backend: '{{ select_crypto_backend }}'
   register: result
 
+- name: Check AuthorityKeyIdentifier
+  assert:
+    that:
+      - result.authority_key_identifier == "44:55:66:77"
+      - result.authority_cert_issuer is none
+      - result.authority_cert_serial_number is none
+  when: select_crypto_backend != 'pyopenssl' and cryptography_version.stdout is version('1.3', '>=')
+
 - name: Update result list
   set_fact:
     info_results: "{{ info_results + [result] }}"
diff --git a/test/integration/targets/openssl_certificate_info/tasks/main.yml b/test/integration/targets/openssl_certificate_info/tasks/main.yml
index 36a3681f36c63c..56033b69b21f85 100644
--- a/test/integration/targets/openssl_certificate_info/tasks/main.yml
+++ b/test/integration/targets/openssl_certificate_info/tasks/main.yml
@@ -69,6 +69,14 @@
       - "pathlen:23"
     basic_constraints_critical: yes
     ocsp_must_staple: yes
+    subject_key_identifier: '{{ "00:11:22:33" if cryptography_version.stdout is version("1.3", ">=") else omit }}'
+    authority_key_identifier: '{{ "44:55:66:77" if cryptography_version.stdout is version("1.3", ">=") else omit }}'
+    authority_cert_issuer: '{{ value_for_authority_cert_issuer if cryptography_version.stdout is version("1.3", ">=") else omit }}'
+    authority_cert_serial_number: '{{ 12345 if cryptography_version.stdout is version("1.3", ">=") else omit }}'
+  vars:
+    value_for_authority_cert_issuer:
+      - "DNS:ca.example.org"
+      - "IP:1.2.3.4"
 
 - name: Generate CSR 2
   openssl_csr:
@@ -90,12 +98,19 @@
       - "IP:DEAD:BEEF::1"
     basic_constraints:
       - "CA:FALSE"
+    authority_cert_issuer: '{{ value_for_authority_cert_issuer if cryptography_version.stdout is version("1.3", ">=") else omit }}'
+    authority_cert_serial_number: '{{ 12345 if cryptography_version.stdout is version("1.3", ">=") else omit }}'
+  vars:
+    value_for_authority_cert_issuer:
+      - "DNS:ca.example.org"
+      - "IP:1.2.3.4"
 
 - name: Generate CSR 4
   openssl_csr:
     path: '{{ output_dir }}/csr_4.csr'
     privatekey_path: '{{ output_dir }}/privatekey.pem'
     useCommonNameForSAN: no
+    authority_key_identifier: '{{ "44:55:66:77" if cryptography_version.stdout is version("1.3", ">=") else omit }}'
 
 - name: Generate selfsigned certificates
   openssl_certificate:
@@ -147,7 +162,14 @@
   - name: Compare results
     assert:
       that:
-      - item.0 == item.1
+      - '  (item.0 | dict2items | rejectattr("key", "in", keys_to_ignore) | list | items2dict)
+        == (item.1 | dict2items | rejectattr("key", "in", keys_to_ignore) | list | items2dict)'
       quiet: yes
     loop: "{{ pyopenssl_info_results | zip(cryptography_info_results) | list }}"
   when: pyopenssl_version.stdout is version('0.15', '>=') and cryptography_version.stdout is version('1.6', '>=')
+  vars:
+    keys_to_ignore:
+      - subject_key_identifier
+      - authority_key_identifier
+      - authority_cert_issuer
+      - authority_cert_serial_number
diff --git a/test/integration/targets/openssl_certificate_info/test_plugins/jinja_compatibility.py b/test/integration/targets/openssl_certificate_info/test_plugins/jinja_compatibility.py
new file mode 100644
index 00000000000000..fc2b5f0fcb8659
--- /dev/null
+++ b/test/integration/targets/openssl_certificate_info/test_plugins/jinja_compatibility.py
@@ -0,0 +1,15 @@
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+
+def compatibility_in_test(a, b):
+    return a in b
+
+
+class TestModule:
+    ''' Ansible math jinja2 tests '''
+
+    def tests(self):
+        return {
+            'in': compatibility_in_test,
+        }
diff --git a/test/integration/targets/openssl_csr/tasks/impl.yml b/test/integration/targets/openssl_csr/tasks/impl.yml
index 8978c4c2342585..fe6f4db94e7f95 100644
--- a/test/integration/targets/openssl_csr/tasks/impl.yml
+++ b/test/integration/targets/openssl_csr/tasks/impl.yml
@@ -339,6 +339,179 @@
     select_crypto_backend: '{{ select_crypto_backend }}'
   register: csr_backup_5
 
+- name: Generate CSR with subject key identifier
+  openssl_csr:
+    path: '{{ output_dir }}/csr_ski.csr'
+    privatekey_path: '{{ output_dir }}/privatekey.pem'
+    subject:
+      commonName: www.ansible.com
+    subject_key_identifier: "00:11:22:33"
+    select_crypto_backend: '{{ select_crypto_backend }}'
+  when: select_crypto_backend != 'pyopenssl'
+  register: subject_key_identifier_1
+
+- name: Generate CSR with subject key identifier (idempotency)
+  openssl_csr:
+    path: '{{ output_dir }}/csr_ski.csr'
+    privatekey_path: '{{ output_dir }}/privatekey.pem'
+    subject:
+      commonName: www.ansible.com
+    subject_key_identifier: "00:11:22:33"
+    select_crypto_backend: '{{ select_crypto_backend }}'
+  when: select_crypto_backend != 'pyopenssl'
+  register: subject_key_identifier_2
+
+- name: Generate CSR with subject key identifier (change)
+  openssl_csr:
+    path: '{{ output_dir }}/csr_ski.csr'
+    privatekey_path: '{{ output_dir }}/privatekey.pem'
+    subject:
+      commonName: www.ansible.com
+    subject_key_identifier: "44:55:66:77:88"
+    select_crypto_backend: '{{ select_crypto_backend }}'
+  when: select_crypto_backend != 'pyopenssl'
+  register: subject_key_identifier_3
+
+- name: Generate CSR with subject key identifier (auto-create)
+  openssl_csr:
+    path: '{{ output_dir }}/csr_ski.csr'
+    privatekey_path: '{{ output_dir }}/privatekey.pem'
+    subject:
+      commonName: www.ansible.com
+    create_subject_key_identifier: yes
+    select_crypto_backend: '{{ select_crypto_backend }}'
+  when: select_crypto_backend != 'pyopenssl'
+  register: subject_key_identifier_4
+
+- name: Generate CSR with subject key identifier (auto-create idempotency)
+  openssl_csr:
+    path: '{{ output_dir }}/csr_ski.csr'
+    privatekey_path: '{{ output_dir }}/privatekey.pem'
+    subject:
+      commonName: www.ansible.com
+    create_subject_key_identifier: yes
+    select_crypto_backend: '{{ select_crypto_backend }}'
+  when: select_crypto_backend != 'pyopenssl'
+  register: subject_key_identifier_5
+
+- name: Generate CSR with subject key identifier (remove)
+  openssl_csr:
+    path: '{{ output_dir }}/csr_ski.csr'
+    privatekey_path: '{{ output_dir }}/privatekey.pem'
+    subject:
+      commonName: www.ansible.com
+    select_crypto_backend: '{{ select_crypto_backend }}'
+  when: select_crypto_backend != 'pyopenssl'
+  register: subject_key_identifier_6
+
+- name: Generate CSR with authority key identifier
+  openssl_csr:
+    path: '{{ output_dir }}/csr_aki.csr'
+    privatekey_path: '{{ output_dir }}/privatekey.pem'
+    subject:
+      commonName: www.ansible.com
+    authority_key_identifier: "00:11:22:33"
+    select_crypto_backend: '{{ select_crypto_backend }}'
+  when: select_crypto_backend != 'pyopenssl'
+  register: authority_key_identifier_1
+
+- name: Generate CSR with authority key identifier (idempotency)
+  openssl_csr:
+    path: '{{ output_dir }}/csr_aki.csr'
+    privatekey_path: '{{ output_dir }}/privatekey.pem'
+    subject:
+      commonName: www.ansible.com
+    authority_key_identifier: "00:11:22:33"
+    select_crypto_backend: '{{ select_crypto_backend }}'
+  when: select_crypto_backend != 'pyopenssl'
+  register: authority_key_identifier_2
+
+- name: Generate CSR with authority key identifier (change)
+  openssl_csr:
+    path: '{{ output_dir }}/csr_aki.csr'
+    privatekey_path: '{{ output_dir }}/privatekey.pem'
+    subject:
+      commonName: www.ansible.com
+    authority_key_identifier: "44:55:66:77:88"
+    select_crypto_backend: '{{ select_crypto_backend }}'
+  when: select_crypto_backend != 'pyopenssl'
+  register: authority_key_identifier_3
+
+- name: Generate CSR with authority key identifier (remove)
+  openssl_csr:
+    path: '{{ output_dir }}/csr_aki.csr'
+    privatekey_path: '{{ output_dir }}/privatekey.pem'
+    subject:
+      commonName: www.ansible.com
+    select_crypto_backend: '{{ select_crypto_backend }}'
+  when: select_crypto_backend != 'pyopenssl'
+  register: authority_key_identifier_4
+
+- name: Generate CSR with authority cert issuer / serial number
+  openssl_csr:
+    path: '{{ output_dir }}/csr_acisn.csr'
+    privatekey_path: '{{ output_dir }}/privatekey.pem'
+    subject:
+      commonName: www.ansible.com
+    authority_cert_issuer:
+      - "DNS:ca.example.org"
+      - "IP:1.2.3.4"
+    authority_cert_serial_number: 12345
+    select_crypto_backend: '{{ select_crypto_backend }}'
+  when: select_crypto_backend != 'pyopenssl'
+  register: authority_cert_issuer_sn_1
+
+- name: Generate CSR with authority cert issuer / serial number (idempotency)
+  openssl_csr:
+    path: '{{ output_dir }}/csr_acisn.csr'
+    privatekey_path: '{{ output_dir }}/privatekey.pem'
+    subject:
+      commonName: www.ansible.com
+    authority_cert_issuer:
+      - "DNS:ca.example.org"
+      - "IP:1.2.3.4"
+    authority_cert_serial_number: 12345
+    select_crypto_backend: '{{ select_crypto_backend }}'
+  when: select_crypto_backend != 'pyopenssl'
+  register: authority_cert_issuer_sn_2
+
+- name: Generate CSR with authority cert issuer / serial number (change issuer)
+  openssl_csr:
+    path: '{{ output_dir }}/csr_acisn.csr'
+    privatekey_path: '{{ output_dir }}/privatekey.pem'
+    subject:
+      commonName: www.ansible.com
+    authority_cert_issuer:
+      - "IP:1.2.3.4"
+      - "DNS:ca.example.org"
+    authority_cert_serial_number: 12345
+    select_crypto_backend: '{{ select_crypto_backend }}'
+  when: select_crypto_backend != 'pyopenssl'
+  register: authority_cert_issuer_sn_3
+
+- name: Generate CSR with authority cert issuer / serial number (change serial number)
+  openssl_csr:
+    path: '{{ output_dir }}/csr_acisn.csr'
+    privatekey_path: '{{ output_dir }}/privatekey.pem'
+    subject:
+      commonName: www.ansible.com
+    authority_cert_issuer:
+      - "IP:1.2.3.4"
+      - "DNS:ca.example.org"
+    authority_cert_serial_number: 54321
+    select_crypto_backend: '{{ select_crypto_backend }}'
+  when: select_crypto_backend != 'pyopenssl'
+  register: authority_cert_issuer_sn_4
+
+- name: Generate CSR with authority cert issuer / serial number (remove)
+  openssl_csr:
+    path: '{{ output_dir }}/csr_acisn.csr'
+    privatekey_path: '{{ output_dir }}/privatekey.pem'
+    subject:
+      commonName: www.ansible.com
+  when: select_crypto_backend != 'pyopenssl'
+  register: authority_cert_issuer_sn_5
+
 - name: Generate CSR with everything
   openssl_csr:
     path: '{{ output_dir }}/csr_everything.csr'
@@ -396,7 +569,15 @@
       - "pathlen:23"
     basic_constraints_critical: yes
     ocsp_must_staple: yes
-    select_crypto_backend: '{{ select_crypto_backend }}'
+    subject_key_identifier: '{{ "00:11:22:33" if select_crypto_backend != "pyopenssl" else omit }}'
+    authority_key_identifier: '{{ "44:55:66:77" if select_crypto_backend != "pyopenssl" else omit }}'
+    authority_cert_issuer: '{{ value_for_authority_cert_issuer if select_crypto_backend != "pyopenssl" else omit }}'
+    authority_cert_serial_number: '{{ 12345 if select_crypto_backend != "pyopenssl" else omit }}'
+    select_crypto_backend: '{{ select_crypto_backend }}'
+  vars:
+    value_for_authority_cert_issuer:
+      - "DNS:ca.example.org"
+      - "IP:1.2.3.4"
   register: everything_1
 
 - name: Generate CSR with everything (idempotent, check mode)
@@ -456,7 +637,15 @@
       - "pathlen:23"
     basic_constraints_critical: yes
     ocsp_must_staple: yes
-    select_crypto_backend: '{{ select_crypto_backend }}'
+    subject_key_identifier: '{{ "00:11:22:33" if select_crypto_backend != "pyopenssl" else omit }}'
+    authority_key_identifier: '{{ "44:55:66:77" if select_crypto_backend != "pyopenssl" else omit }}'
+    authority_cert_issuer: '{{ value_for_authority_cert_issuer if select_crypto_backend != "pyopenssl" else omit }}'
+    authority_cert_serial_number: '{{ 12345 if select_crypto_backend != "pyopenssl" else omit }}'
+    select_crypto_backend: '{{ select_crypto_backend }}'
+  vars:
+    value_for_authority_cert_issuer:
+      - "DNS:ca.example.org"
+      - "IP:1.2.3.4"
   check_mode: yes
   register: everything_2
 
@@ -517,5 +706,13 @@
       - "pathlen:23"
     basic_constraints_critical: yes
     ocsp_must_staple: yes
-    select_crypto_backend: '{{ select_crypto_backend }}'
+    subject_key_identifier: '{{ "00:11:22:33" if select_crypto_backend != "pyopenssl" else omit }}'
+    authority_key_identifier: '{{ "44:55:66:77" if select_crypto_backend != "pyopenssl" else omit }}'
+    authority_cert_issuer: '{{ value_for_authority_cert_issuer if select_crypto_backend != "pyopenssl" else omit }}'
+    authority_cert_serial_number: '{{ 12345 if select_crypto_backend != "pyopenssl" else omit }}'
+    select_crypto_backend: '{{ select_crypto_backend }}'
+  vars:
+    value_for_authority_cert_issuer:
+      - "DNS:ca.example.org"
+      - "IP:1.2.3.4"
   register: everything_3
diff --git a/test/integration/targets/openssl_csr/tasks/main.yml b/test/integration/targets/openssl_csr/tasks/main.yml
index 5b0eba3912ba45..d0d5d908788d25 100644
--- a/test/integration/targets/openssl_csr/tasks/main.yml
+++ b/test/integration/targets/openssl_csr/tasks/main.yml
@@ -16,6 +16,8 @@
       select_crypto_backend: pyopenssl
 
   - import_tasks: ../tests/validate.yml
+    vars:
+      select_crypto_backend: pyopenssl
 
   when: pyopenssl_version.stdout is version('0.15', '>=')
 
@@ -36,5 +38,7 @@
       select_crypto_backend: cryptography
 
   - import_tasks: ../tests/validate.yml
+    vars:
+      select_crypto_backend: pyopenssl
 
   when: cryptography_version.stdout is version('1.3', '>=')
diff --git a/test/integration/targets/openssl_csr/tests/validate.yml b/test/integration/targets/openssl_csr/tests/validate.yml
index 43e3b3336b1388..d592a4d236288b 100644
--- a/test/integration/targets/openssl_csr/tests/validate.yml
+++ b/test/integration/targets/openssl_csr/tests/validate.yml
@@ -125,6 +125,36 @@
     that:
       - output_broken is changed
 
+- name: Verify that subject key identifier handling works
+  assert:
+    that:
+      - subject_key_identifier_1 is changed
+      - subject_key_identifier_2 is not changed
+      - subject_key_identifier_3 is changed
+      - subject_key_identifier_4 is changed
+      - subject_key_identifier_5 is not changed
+      - subject_key_identifier_6 is not changed
+  when: select_crypto_backend != 'pyopenssl'
+
+- name: Verify that authority key identifier handling works
+  assert:
+    that:
+      - authority_key_identifier_1 is changed
+      - authority_key_identifier_2 is not changed
+      - authority_key_identifier_3 is changed
+      - authority_key_identifier_4 is changed
+  when: select_crypto_backend != 'pyopenssl'
+
+- name: Verify that authority cert issuer / serial number handling works
+  assert:
+    that:
+      - authority_cert_issuer_sn_1 is changed
+      - authority_cert_issuer_sn_2 is not changed
+      - authority_cert_issuer_sn_3 is changed
+      - authority_cert_issuer_sn_4 is changed
+      - authority_cert_issuer_sn_5 is changed
+  when: select_crypto_backend != 'pyopenssl'
+
 - name: Check backup
   assert:
     that:
diff --git a/test/integration/targets/openssl_csr_info/tasks/impl.yml b/test/integration/targets/openssl_csr_info/tasks/impl.yml
index f783ce9d411b32..9516583ef5d384 100644
--- a/test/integration/targets/openssl_csr_info/tasks/impl.yml
+++ b/test/integration/targets/openssl_csr_info/tasks/impl.yml
@@ -15,6 +15,19 @@
       - "['organizationalUnitName', 'Crypto Department'] in result.subject_ordered"
       - "['organizationalUnitName', 'ACME Department'] in result.subject_ordered"
 
+- name: Check SubjectKeyIdentifier and AuthorityKeyIdentifier
+  assert:
+    that:
+      - result.subject_key_identifier == "00:11:22:33"
+      - result.authority_key_identifier == "44:55:66:77"
+      - result.authority_cert_issuer == expected_authority_cert_issuer
+      - result.authority_cert_serial_number == 12345
+  vars:
+    expected_authority_cert_issuer:
+      - "DNS:ca.example.org"
+      - "IP:1.2.3.4"
+  when: select_crypto_backend != 'pyopenssl' and cryptography_version.stdout is version('1.3', '>=')
+
 - name: Update result list
   set_fact:
     info_results: "{{ info_results + [result] }}"
@@ -35,6 +48,18 @@
     select_crypto_backend: '{{ select_crypto_backend }}'
   register: result
 
+- name: Check AuthorityKeyIdentifier
+  assert:
+    that:
+      - result.authority_key_identifier is none
+      - result.authority_cert_issuer == expected_authority_cert_issuer
+      - result.authority_cert_serial_number == 12345
+  vars:
+    expected_authority_cert_issuer:
+      - "DNS:ca.example.org"
+      - "IP:1.2.3.4"
+  when: select_crypto_backend != 'pyopenssl' and cryptography_version.stdout is version('1.3', '>=')
+
 - name: Update result list
   set_fact:
     info_results: "{{ info_results + [result] }}"
@@ -45,6 +70,14 @@
     select_crypto_backend: '{{ select_crypto_backend }}'
   register: result
 
+- name: Check AuthorityKeyIdentifier
+  assert:
+    that:
+      - result.authority_key_identifier == "44:55:66:77"
+      - result.authority_cert_issuer is none
+      - result.authority_cert_serial_number is none
+  when: select_crypto_backend != 'pyopenssl' and cryptography_version.stdout is version('1.3', '>=')
+
 - name: Update result list
   set_fact:
     info_results: "{{ info_results + [result] }}"
diff --git a/test/integration/targets/openssl_csr_info/tasks/main.yml b/test/integration/targets/openssl_csr_info/tasks/main.yml
index 3621945c56cc25..9686250109f047 100644
--- a/test/integration/targets/openssl_csr_info/tasks/main.yml
+++ b/test/integration/targets/openssl_csr_info/tasks/main.yml
@@ -69,6 +69,14 @@
       - "pathlen:23"
     basic_constraints_critical: yes
     ocsp_must_staple: yes
+    subject_key_identifier: '{{ "00:11:22:33" if cryptography_version.stdout is version("1.3", ">=") else omit }}'
+    authority_key_identifier: '{{ "44:55:66:77" if cryptography_version.stdout is version("1.3", ">=") else omit }}'
+    authority_cert_issuer: '{{ value_for_authority_cert_issuer if cryptography_version.stdout is version("1.3", ">=") else omit }}'
+    authority_cert_serial_number: '{{ 12345 if cryptography_version.stdout is version("1.3", ">=") else omit }}'
+  vars:
+    value_for_authority_cert_issuer:
+      - "DNS:ca.example.org"
+      - "IP:1.2.3.4"
 
 - name: Generate CSR 2
   openssl_csr:
@@ -90,12 +98,19 @@
       - "IP:DEAD:BEEF::1"
     basic_constraints:
       - "CA:FALSE"
+    authority_cert_issuer: '{{ value_for_authority_cert_issuer if cryptography_version.stdout is version("1.3", ">=") else omit }}'
+    authority_cert_serial_number: '{{ 12345 if cryptography_version.stdout is version("1.3", ">=") else omit }}'
+  vars:
+    value_for_authority_cert_issuer:
+      - "DNS:ca.example.org"
+      - "IP:1.2.3.4"
 
 - name: Generate CSR 4
   openssl_csr:
     path: '{{ output_dir }}/csr_4.csr'
     privatekey_path: '{{ output_dir }}/privatekey.pem'
     useCommonNameForSAN: no
+    authority_key_identifier: '{{ "44:55:66:77" if cryptography_version.stdout is version("1.3", ">=") else omit }}'
 
 - name: Prepare result list
   set_fact:
@@ -132,7 +147,14 @@
   - name: Compare results
     assert:
       that:
-      - item.0 == item.1
+      - '  (item.0 | dict2items | rejectattr("key", "in", keys_to_ignore) | list | items2dict)
+        == (item.1 | dict2items | rejectattr("key", "in", keys_to_ignore) | list | items2dict)'
       quiet: yes
     loop: "{{ pyopenssl_info_results | zip(cryptography_info_results) | list }}"
   when: pyopenssl_version.stdout is version('0.15', '>=') and cryptography_version.stdout is version('1.3', '>=')
+  vars:
+    keys_to_ignore:
+      - subject_key_identifier
+      - authority_key_identifier
+      - authority_cert_issuer
+      - authority_cert_serial_number
diff --git a/test/integration/targets/openssl_csr_info/test_plugins/jinja_compatibility.py b/test/integration/targets/openssl_csr_info/test_plugins/jinja_compatibility.py
new file mode 100644
index 00000000000000..fc2b5f0fcb8659
--- /dev/null
+++ b/test/integration/targets/openssl_csr_info/test_plugins/jinja_compatibility.py
@@ -0,0 +1,15 @@
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+
+def compatibility_in_test(a, b):
+    return a in b
+
+
+class TestModule:
+    ''' Ansible math jinja2 tests '''
+
+    def tests(self):
+        return {
+            'in': compatibility_in_test,
+        }


Code review comments:
diff_hunk: @@ -238,10 +238,45 @@
                  or not.
     returned: success
     type: dict
+subject_key_identifier:
+    description:
+        - The CSR's subject key identifier.
+        - The identifier is returned in hexadecial, with C(:) used to separate bytes.
+        - Is C(none) if the C(SubjectKeyIdentifier) extension is not present.
+    returned: success and if the pyOpenSSL backend is I(not) used
+    type: str
+    sample: '00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff:00:11:22:33'
+    version_added: "2.9"
+authority_key_identifier:
+    description:
+        - The CSR's authority key identifier.
+        - The identifier is returned in hexadecial, with C(:) used to separate bytes.
+        - Is C(none) if the C(AuthorityKeyIdentifier) extension is not present.
+    returned: success and if the pyOpenSSL backend is I(not) used
+    type: str
+    sample: '00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff:00:11:22:33'
+    version_added: "2.9"
+authority_cert_issuer:
+    description:
+        - The CSR's authority cert issuer as a list of strings.
path: lib/ansible/modules/crypto/openssl_certificate_info.py
text: @user1:
```suggestion
        - The certificate issuer represented by C(authority_key_identifier).
```

I don't think a certificate can have multiple issuers? Unless it's a list of DN components, in which case it would make more sense to have "as a list of DN components"

@author:
In https://tools.ietf.org/html/rfc5280#section-4.2.1.1 it is defined as `GeneralNames`. No idea why it is defined this way... How about `as a list of general names`?

@user1:
This was one where I tripped up again from being rusty on the RFC, that this isn't the same as the "issuer:" return field.

diff_hunk: @@ -238,10 +238,45 @@
                  or not.
     returned: success
     type: dict
+subject_key_identifier:
+    description:
+        - The CSR's subject key identifier.
+        - The identifier is returned in hexadecial, with C(:) used to separate bytes.
+        - Is C(none) if the C(SubjectKeyIdentifier) extension is not present.
+    returned: success and if the pyOpenSSL backend is I(not) used
+    type: str
+    sample: '00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff:00:11:22:33'
+    version_added: "2.9"
+authority_key_identifier:
+    description:
+        - The CSR's authority key identifier.
+        - The identifier is returned in hexadecial, with C(:) used to separate bytes.
+        - Is C(none) if the C(AuthorityKeyIdentifier) extension is not present.
+    returned: success and if the pyOpenSSL backend is I(not) used
+    type: str
+    sample: '00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff:00:11:22:33'
+    version_added: "2.9"
+authority_cert_issuer:
+    description:
+        - The CSR's authority cert issuer as a list of strings.
+        - Is C(none) if the C(AuthorityKeyIdentifier) extension is not present.
+    returned: success and if the pyOpenSSL backend is I(not) used
+    type: list
+    sample: "[DNS:www.ansible.com, IP:1.2.3.4]"
+    version_added: "2.9"
+authority_cert_serial_number:
+    description:
+        - The CSR's authority cert serial number.
path: lib/ansible/modules/crypto/openssl_certificate_info.py
text: @user1:
```suggestion
        - The certificate issuer certificate serial number.
```

diff_hunk: @@ -238,10 +238,45 @@
                  or not.
     returned: success
     type: dict
+subject_key_identifier:
+    description:
+        - The CSR's subject key identifier.
+        - The identifier is returned in hexadecial, with C(:) used to separate bytes.
+        - Is C(none) if the C(SubjectKeyIdentifier) extension is not present.
+    returned: success and if the pyOpenSSL backend is I(not) used
+    type: str
+    sample: '00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff:00:11:22:33'
+    version_added: "2.9"
+authority_key_identifier:
+    description:
+        - The CSR's authority key identifier.
+        - The identifier is returned in hexadecial, with C(:) used to separate bytes.
path: lib/ansible/modules/crypto/openssl_certificate_info.py
text: @user1:
```suggestion
        - The identifier is returned in hexadecimal, with C(:) used to separate bytes.
```

diff_hunk: @@ -238,10 +238,45 @@
                  or not.
     returned: success
     type: dict
+subject_key_identifier:
+    description:
+        - The CSR's subject key identifier.
+        - The identifier is returned in hexadecial, with C(:) used to separate bytes.
path: lib/ansible/modules/crypto/openssl_certificate_info.py
text: @user1:
```suggestion
        - The identifier is returned in hexadecimal, with C(:) used to separate bytes.
```

diff_hunk: @@ -238,10 +238,45 @@
                  or not.
     returned: success
     type: dict
+subject_key_identifier:
+    description:
+        - The CSR's subject key identifier.
+        - The identifier is returned in hexadecial, with C(:) used to separate bytes.
+        - Is C(none) if the C(SubjectKeyIdentifier) extension is not present.
+    returned: success and if the pyOpenSSL backend is I(not) used
+    type: str
+    sample: '00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff:00:11:22:33'
+    version_added: "2.9"
+authority_key_identifier:
+    description:
+        - The CSR's authority key identifier.
path: lib/ansible/modules/crypto/openssl_certificate_info.py
text: @user1:
```suggestion
        - The certificate's authority key identifier.
```

@author:
Thanks for spotting this (and the related) copy'n'paste errors and typos!

diff_hunk: @@ -238,10 +238,45 @@
                  or not.
     returned: success
     type: dict
+subject_key_identifier:
+    description:
+        - The CSR's subject key identifier.
path: lib/ansible/modules/crypto/openssl_certificate_info.py
text: @user1:
```suggestion
        - The certificate's subject key identifier.
```