Allow multiple values per key in name fields in openssl_certificate/csr (#30338)

* allow multiple values per key in name fields in openssl_certificate

* check correct side of comparison

* trigger only on lists

* add subject parameter to openssl_csr

* fix key: value mapping not skipping None elements

* temporary fix for undefined "subject" field

* fix iteration over subject entries

* fix docs

* quote sample string

* allow csr with only subject defined

* fix integration test

* look up NIDs before comparing, add hidden _strict params

* deal with empty issuer/subject fields

* adapt integration tests

* also normalize output from pyopenssl

* fix issue with _sanitize_inputs

* don't convert empty lists

* workaround for pyopenssl limitations

* properly encode the input to the txt2nid function

* another to_bytes fix

* make subject, commonname and subjecAltName completely optional

* don't compare hashes of keys in openssl_csr integration tests

* add integration test for old API in openssl_csr

* compare keys directly in certificate and publickey integration tests

* fix typo
This commit is contained in:
MarkusTeufelberger 2017-12-12 13:35:22 +01:00 committed by John R Barker
commit 9ea1b18ff7
8 changed files with 141 additions and 63 deletions

View file

@ -109,11 +109,13 @@ options:
issuer:
description:
- Key/value pairs that must be present in the issuer name field of the certificate
- Key/value pairs that must be present in the issuer name field of the certificate.
If you need to specify more than one value with the same key, use a list as value.
subject:
description:
- Key/value pairs that must be present in the subject name field of the certificate
- Key/value pairs that must be present in the subject name field of the certificate.
If you need to specify more than one value with the same key, use a list as value.
has_expired:
default: False
@ -453,8 +455,16 @@ class AssertOnlyCertificate(Certificate):
def __init__(self, module):
super(AssertOnlyCertificate, self).__init__(module)
self.signature_algorithms = module.params['signature_algorithms']
self.subject = module.params['subject']
self.issuer = module.params['issuer']
if module.params['subject']:
self.subject = crypto_utils.parse_name_field(module.params['subject'])
else:
self.subject = []
self.subject_strict = False
if module.params['issuer']:
self.issuer = crypto_utils.parse_name_field(module.params['issuer'])
else:
self.issuer = []
self.issuer_strict = False
self.has_expired = module.params['has_expired']
self.version = module.params['version']
self.keyUsage = module.params['keyUsage']
@ -479,8 +489,11 @@ class AssertOnlyCertificate(Certificate):
'notAfter', 'valid_at', 'invalid_at']:
attr = getattr(self, param)
if isinstance(attr, list):
setattr(self, param, [to_bytes(item) for item in attr])
if isinstance(attr, list) and attr:
if isinstance(attr[0], str):
setattr(self, param, [to_bytes(item) for item in attr])
elif isinstance(attr[0], tuple):
setattr(self, param, [(to_bytes(item[0]), to_bytes(item[1])) for item in attr])
elif isinstance(attr, tuple):
setattr(self, param, dict((to_bytes(k), to_bytes(v)) for (k, v) in attr.items()))
elif isinstance(attr, dict):
@ -501,20 +514,26 @@ class AssertOnlyCertificate(Certificate):
def _validate_subject():
if self.subject:
expected_subject = [(OpenSSL._util.lib.OBJ_txt2nid(sub[0]), sub[1]) for sub in self.subject]
cert_subject = self.cert.get_subject().get_components()
diff = [item for item in self.subject.items() if item not in cert_subject]
if diff:
current_subject = [(OpenSSL._util.lib.OBJ_txt2nid(sub[0]), sub[1]) for sub in cert_subject]
if (not self.subject_strict and not all(x in current_subject for x in expected_subject)) or \
(self.subject_strict and not set(expected_subject) == set(current_subject)):
diff = [item for item in self.subject if item not in current_subject]
self.message.append(
'Invalid subject component (got %s, expected all of %s to be present)' % (cert_subject, self.subject.items())
'Invalid subject component (got %s, expected all of %s to be present)' % (cert_subject, self.subject)
)
def _validate_issuer():
if self.issuer:
expected_issuer = [(OpenSSL._util.lib.OBJ_txt2nid(iss[0]), iss[1]) for iss in self.issuer]
cert_issuer = self.cert.get_issuer().get_components()
diff = [item for item in self.issuer.items() if item not in cert_issuer]
if diff:
current_issuer = [(OpenSSL._util.lib.OBJ_txt2nid(iss[0]), iss[1]) for iss in cert_issuer]
if (not self.issuer_strict and not all(x in current_issuer for x in expected_issuer)) or \
(self.issuer_strict and not set(expected_issuer) == set(current_issuer)):
diff = [item for item in self.issuer if item not in current_issuer]
self.message.append(
'Invalid issuer component (got %s, expected all of %s to be present)' % (cert_issuer, self.issuer.items())
'Invalid issuer component (got %s, expected all of %s to be present)' % (cert_issuer, self.issuer)
)
def _validate_has_expired():