Skip to content

Commit

Permalink
openssl_csr_info and x509_certificate_info: return more public key in…
Browse files Browse the repository at this point in the history
…formation (#233)

* Return more public key information.

* Make sure bit size is converted to int first.

* Apply suggestions from code review

Co-authored-by: Ajpantuso <[email protected]>

* Remove no longer necessary code.

* Use correct return value's name.

* Add trailing commas.

Co-authored-by: Ajpantuso <[email protected]>
  • Loading branch information
felixfontein and Ajpantuso authored May 19, 2021
1 parent 3293b77 commit 0a0d0f2
Show file tree
Hide file tree
Showing 7 changed files with 209 additions and 28 deletions.
3 changes: 3 additions & 0 deletions changelogs/fragments/233-public-key-info.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
minor_changes:
- "openssl_csr_info - now returns ``public_key_type`` and ``public_key_data`` (https:/ansible-collections/community.crypto/pull/233)."
- "x509_certificate_info - now returns ``public_key_type`` and ``public_key_data`` (https:/ansible-collections/community.crypto/pull/233)."
44 changes: 30 additions & 14 deletions plugins/module_utils/crypto/module_backends/certificate_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@
pyopenssl_normalize_name_attribute,
)

from ansible_collections.community.crypto.plugins.module_utils.crypto.module_backends.publickey_info import (
get_publickey_info,
)

MINIMAL_CRYPTOGRAPHY_VERSION = '1.6'
MINIMAL_PYOPENSSL_VERSION = '0.15'

Expand Down Expand Up @@ -134,7 +138,11 @@ def get_not_after(self):
pass

@abc.abstractmethod
def _get_public_key(self, binary):
def _get_public_key_pem(self):
pass

@abc.abstractmethod
def _get_public_key_object(self):
pass

@abc.abstractmethod
Expand Down Expand Up @@ -185,9 +193,14 @@ def get_info(self):
result['not_after'] = not_after.strftime(TIMESTAMP_FORMAT)
result['expired'] = not_after < datetime.datetime.utcnow()

result['public_key'] = self._get_public_key(binary=False)
pk = self._get_public_key(binary=True)
result['public_key_fingerprints'] = get_fingerprint_of_bytes(pk) if pk is not None else dict()
result['public_key'] = self._get_public_key_pem()

public_key_info = get_publickey_info(self.module, self.backend, key=self._get_public_key_object())
result.update({
'public_key_type': public_key_info['type'],
'public_key_data': public_key_info['public_data'],
'public_key_fingerprints': public_key_info['fingerprints'],
})

result['fingerprints'] = get_fingerprint_of_bytes(self._get_der_bytes())

Expand Down Expand Up @@ -330,12 +343,15 @@ def get_not_before(self):
def get_not_after(self):
return self.cert.not_valid_after

def _get_public_key(self, binary):
def _get_public_key_pem(self):
return self.cert.public_key().public_bytes(
serialization.Encoding.DER if binary else serialization.Encoding.PEM,
serialization.PublicFormat.SubjectPublicKeyInfo
serialization.Encoding.PEM,
serialization.PublicFormat.SubjectPublicKeyInfo,
)

def _get_public_key_object(self):
return self.cert.public_key()

def _get_subject_key_identifier(self):
try:
ext = self.cert.extensions.get_extension_for_class(x509.SubjectKeyIdentifier)
Expand Down Expand Up @@ -450,27 +466,27 @@ def get_not_after(self):
time_string = to_native(self.cert.get_notAfter())
return datetime.datetime.strptime(time_string, "%Y%m%d%H%M%SZ")

def _get_public_key(self, binary):
def _get_public_key_pem(self):
try:
return crypto.dump_publickey(
crypto.FILETYPE_ASN1 if binary else crypto.FILETYPE_PEM,
self.cert.get_pubkey()
crypto.FILETYPE_PEM,
self.cert.get_pubkey(),
)
except AttributeError:
try:
# pyOpenSSL < 16.0:
bio = crypto._new_mem_buf()
if binary:
rc = crypto._lib.i2d_PUBKEY_bio(bio, self.cert.get_pubkey()._pkey)
else:
rc = crypto._lib.PEM_write_bio_PUBKEY(bio, self.cert.get_pubkey()._pkey)
rc = crypto._lib.PEM_write_bio_PUBKEY(bio, self.cert.get_pubkey()._pkey)
if rc != 1:
crypto._raise_current_error()
return crypto._bio_to_string(bio)
except AttributeError:
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_public_key_object(self):
return self.cert.get_pubkey()

def _get_subject_key_identifier(self):
# Won't be implemented
return None
Expand Down
44 changes: 30 additions & 14 deletions plugins/module_utils/crypto/module_backends/csr_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@
pyopenssl_parse_name_constraints,
)

from ansible_collections.community.crypto.plugins.module_utils.crypto.module_backends.publickey_info import (
get_publickey_info,
)

MINIMAL_CRYPTOGRAPHY_VERSION = '1.3'
MINIMAL_PYOPENSSL_VERSION = '0.15'

Expand Down Expand Up @@ -113,7 +117,11 @@ def _get_name_constraints(self):
pass

@abc.abstractmethod
def _get_public_key(self, binary):
def _get_public_key_pem(self):
pass

@abc.abstractmethod
def _get_public_key_object(self):
pass

@abc.abstractmethod
Expand Down Expand Up @@ -152,9 +160,14 @@ def get_info(self):
result['name_constraints_critical'],
) = self._get_name_constraints()

result['public_key'] = self._get_public_key(binary=False)
pk = self._get_public_key(binary=True)
result['public_key_fingerprints'] = get_fingerprint_of_bytes(pk) if pk is not None else dict()
result['public_key'] = self._get_public_key_pem()

public_key_info = get_publickey_info(self.module, self.backend, key=self._get_public_key_object())
result.update({
'public_key_type': public_key_info['type'],
'public_key_data': public_key_info['public_data'],
'public_key_fingerprints': public_key_info['fingerprints'],
})

if self.backend != 'pyopenssl':
ski = self._get_subject_key_identifier()
Expand Down Expand Up @@ -282,12 +295,15 @@ def _get_name_constraints(self):
except cryptography.x509.ExtensionNotFound:
return None, None, False

def _get_public_key(self, binary):
def _get_public_key_pem(self):
return self.csr.public_key().public_bytes(
serialization.Encoding.DER if binary else serialization.Encoding.PEM,
serialization.PublicFormat.SubjectPublicKeyInfo
serialization.Encoding.PEM,
serialization.PublicFormat.SubjectPublicKeyInfo,
)

def _get_public_key_object(self):
return self.csr.public_key()

def _get_subject_key_identifier(self):
try:
ext = self.csr.extensions.get_extension_for_class(x509.SubjectKeyIdentifier)
Expand Down Expand Up @@ -374,26 +390,26 @@ def _get_name_constraints(self):
return permitted, excluded, bool(extension.get_critical())
return None, None, False

def _get_public_key(self, binary):
def _get_public_key_pem(self):
try:
return crypto.dump_publickey(
crypto.FILETYPE_ASN1 if binary else crypto.FILETYPE_PEM,
self.csr.get_pubkey()
crypto.FILETYPE_PEM,
self.csr.get_pubkey(),
)
except AttributeError:
try:
bio = crypto._new_mem_buf()
if binary:
rc = crypto._lib.i2d_PUBKEY_bio(bio, self.csr.get_pubkey()._pkey)
else:
rc = crypto._lib.PEM_write_bio_PUBKEY(bio, self.csr.get_pubkey()._pkey)
rc = crypto._lib.PEM_write_bio_PUBKEY(bio, self.csr.get_pubkey()._pkey)
if rc != 1:
crypto._raise_current_error()
return crypto._bio_to_string(bio)
except AttributeError:
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_public_key_object(self):
return self.csr.get_pubkey()

def _get_subject_key_identifier(self):
# Won't be implemented
return None
Expand Down
71 changes: 71 additions & 0 deletions plugins/modules/openssl_csr_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,77 @@
returned: success
type: str
sample: "-----BEGIN PUBLIC KEY-----\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A..."
public_key_type:
description:
- The CSR's public key's type.
- One of C(RSA), C(DSA), C(ECC), C(Ed25519), C(X25519), C(Ed448), or C(X448).
- Will start with C(unknown) if the key type cannot be determined.
returned: success
type: str
version_added: 1.7.0
sample: RSA
public_key_data:
description:
- Public key data. Depends on the public key's type.
returned: success
type: dict
version_added: 1.7.0
contains:
size:
description:
- Bit size of modulus (RSA) or prime number (DSA).
type: int
returned: When C(public_key_type=RSA) or C(public_key_type=DSA)
modulus:
description:
- The RSA key's modulus.
type: int
returned: When C(public_key_type=RSA)
exponent:
description:
- The RSA key's public exponent.
type: int
returned: When C(public_key_type=RSA)
p:
description:
- The C(p) value for DSA.
- This is the prime modulus upon which arithmetic takes place.
type: int
returned: When C(public_key_type=DSA)
q:
description:
- The C(q) value for DSA.
- This is a prime that divides C(p - 1), and at the same time the order of the subgroup of the
multiplicative group of the prime field used.
type: int
returned: When C(public_key_type=DSA)
g:
description:
- The C(g) value for DSA.
- This is the element spanning the subgroup of the multiplicative group of the prime field used.
type: int
returned: When C(public_key_type=DSA)
curve:
description:
- The curve's name for ECC.
type: str
returned: When C(public_key_type=ECC)
exponent_size:
description:
- The maximum number of bits of a private key. This is basically the bit size of the subgroup used.
type: int
returned: When C(public_key_type=ECC)
x:
description:
- The C(x) coordinate for the public point on the elliptic curve.
type: int
returned: When C(public_key_type=ECC)
y:
description:
- For C(public_key_type=ECC), this is the C(y) coordinate for the public point on the elliptic curve.
- For C(public_key_type=DSA), this is the publicly known group element whose discrete logarithm w.r.t. C(g) is the private key.
type: int
returned: When C(public_key_type=DSA) or C(public_key_type=ECC)
public_key_fingerprints:
description:
- Fingerprints of CSR's public key.
Expand Down
71 changes: 71 additions & 0 deletions plugins/modules/x509_certificate_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,77 @@
returned: success
type: str
sample: "-----BEGIN PUBLIC KEY-----\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A..."
public_key_type:
description:
- The certificate's public key's type.
- One of C(RSA), C(DSA), C(ECC), C(Ed25519), C(X25519), C(Ed448), or C(X448).
- Will start with C(unknown) if the key type cannot be determined.
returned: success
type: str
version_added: 1.7.0
sample: RSA
public_key_data:
description:
- Public key data. Depends on the public key's type.
returned: success
type: dict
version_added: 1.7.0
contains:
size:
description:
- Bit size of modulus (RSA) or prime number (DSA).
type: int
returned: When C(public_key_type=RSA) or C(public_key_type=DSA)
modulus:
description:
- The RSA key's modulus.
type: int
returned: When C(public_key_type=RSA)
exponent:
description:
- The RSA key's public exponent.
type: int
returned: When C(public_key_type=RSA)
p:
description:
- The C(p) value for DSA.
- This is the prime modulus upon which arithmetic takes place.
type: int
returned: When C(public_key_type=DSA)
q:
description:
- The C(q) value for DSA.
- This is a prime that divides C(p - 1), and at the same time the order of the subgroup of the
multiplicative group of the prime field used.
type: int
returned: When C(public_key_type=DSA)
g:
description:
- The C(g) value for DSA.
- This is the element spanning the subgroup of the multiplicative group of the prime field used.
type: int
returned: When C(public_key_type=DSA)
curve:
description:
- The curve's name for ECC.
type: str
returned: When C(public_key_type=ECC)
exponent_size:
description:
- The maximum number of bits of a private key. This is basically the bit size of the subgroup used.
type: int
returned: When C(public_key_type=ECC)
x:
description:
- The C(x) coordinate for the public point on the elliptic curve.
type: int
returned: When C(public_key_type=ECC)
y:
description:
- For C(public_key_type=ECC), this is the C(y) coordinate for the public point on the elliptic curve.
- For C(public_key_type=DSA), this is the publicly known group element whose discrete logarithm w.r.t. C(g) is the private key.
type: int
returned: When C(public_key_type=DSA) or C(public_key_type=ECC)
public_key_fingerprints:
description:
- Fingerprints of certificate's public key.
Expand Down
2 changes: 2 additions & 0 deletions tests/integration/targets/openssl_csr_info/tasks/impl.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
- result.subject.organizationalUnitName == 'ACME Department'
- "['organizationalUnitName', 'Crypto Department'] in result.subject_ordered"
- "['organizationalUnitName', 'ACME Department'] in result.subject_ordered"
- result.public_key_type == 'RSA'
- result.public_key_data.size == default_rsa_key_size

- name: "({{ select_crypto_backend }}) Check SubjectKeyIdentifier and AuthorityKeyIdentifier"
assert:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
- result.subject.organizationalUnitName == 'ACME Department'
- "['organizationalUnitName', 'Crypto Department'] in result.subject_ordered"
- "['organizationalUnitName', 'ACME Department'] in result.subject_ordered"
- result.public_key_type == 'RSA'
- result.public_key_data.size == (default_rsa_key_size_certifiates | int)

- name: Check SubjectKeyIdentifier and AuthorityKeyIdentifier
assert:
Expand Down

0 comments on commit 0a0d0f2

Please sign in to comment.