1#!/usr/bin/python
2# -*- coding: utf-8 -*-
3
4# Copyright: (c) 2016-2017, Yanis Guenane <yanis+ansible@guenane.org>
5# Copyright: (c) 2017, Markus Teufelberger <mteufelberger+ansible@mgit.at>
6# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
7
8from __future__ import absolute_import, division, print_function
9__metaclass__ = type
10
11ANSIBLE_METADATA = {'metadata_version': '1.1',
12                    'status': ['preview'],
13                    'supported_by': 'community'}
14
15DOCUMENTATION = r'''
16---
17module: openssl_certificate
18version_added: "2.4"
19short_description: Generate and/or check OpenSSL certificates
20description:
21    - This module allows one to (re)generate OpenSSL certificates.
22    - It implements a notion of provider (ie. C(selfsigned), C(ownca), C(acme), C(assertonly), C(entrust))
23      for your certificate.
24    - The C(assertonly) provider is intended for use cases where one is only interested in
25      checking properties of a supplied certificate. Please note that this provider has been
26      deprecated in Ansible 2.9 and will be removed in Ansible 2.13. See the examples on how
27      to emulate C(assertonly) usage with M(openssl_certificate_info), M(openssl_csr_info),
28      M(openssl_privatekey_info) and M(assert). This also allows more flexible checks than
29      the ones offered by the C(assertonly) provider.
30    - The C(ownca) provider is intended for generating OpenSSL certificate signed with your own
31      CA (Certificate Authority) certificate (self-signed certificate).
32    - Many properties that can be specified in this module are for validation of an
33      existing or newly generated certificate. The proper place to specify them, if you
34      want to receive a certificate with these properties is a CSR (Certificate Signing Request).
35    - "Please note that the module regenerates existing certificate if it doesn't match the module's
36      options, or if it seems to be corrupt. If you are concerned that this could overwrite
37      your existing certificate, consider using the I(backup) option."
38    - It uses the pyOpenSSL or cryptography python library to interact with OpenSSL.
39    - If both the cryptography and PyOpenSSL libraries are available (and meet the minimum version requirements)
40      cryptography will be preferred as a backend over PyOpenSSL (unless the backend is forced with C(select_crypto_backend)).
41      Please note that the PyOpenSSL backend was deprecated in Ansible 2.9 and will be removed in Ansible 2.13.
42requirements:
43    - PyOpenSSL >= 0.15 or cryptography >= 1.6 (if using C(selfsigned) or C(assertonly) provider)
44    - acme-tiny (if using the C(acme) provider)
45author:
46  - Yanis Guenane (@Spredzy)
47  - Markus Teufelberger (@MarkusTeufelberger)
48options:
49    state:
50        description:
51            - Whether the certificate should exist or not, taking action if the state is different from what is stated.
52        type: str
53        default: present
54        choices: [ absent, present ]
55
56    path:
57        description:
58            - Remote absolute path where the generated certificate file should be created or is already located.
59        type: path
60        required: true
61
62    provider:
63        description:
64            - Name of the provider to use to generate/retrieve the OpenSSL certificate.
65            - The C(assertonly) provider will not generate files and fail if the certificate file is missing.
66            - The C(assertonly) provider has been deprecated in Ansible 2.9 and will be removed in Ansible 2.13.
67              Please see the examples on how to emulate it with M(openssl_certificate_info), M(openssl_csr_info),
68              M(openssl_privatekey_info) and M(assert).
69            - "The C(entrust) provider was added for Ansible 2.9 and requires credentials for the
70               L(https://www.entrustdatacard.com/products/categories/ssl-certificates,Entrust Certificate Services) (ECS) API."
71            - Required if I(state) is C(present).
72        type: str
73        choices: [ acme, assertonly, entrust, ownca, selfsigned ]
74
75    force:
76        description:
77            - Generate the certificate, even if it already exists.
78        type: bool
79        default: no
80
81    csr_path:
82        description:
83            - Path to the Certificate Signing Request (CSR) used to generate this certificate.
84            - This is not required in C(assertonly) mode.
85        type: path
86
87    privatekey_path:
88        description:
89            - Path to the private key to use when signing the certificate.
90        type: path
91
92    privatekey_passphrase:
93        description:
94            - The passphrase for the I(privatekey_path).
95            - This is required if the private key is password protected.
96        type: str
97
98    selfsigned_version:
99        description:
100            - Version of the C(selfsigned) certificate.
101            - Nowadays it should almost always be C(3).
102            - This is only used by the C(selfsigned) provider.
103        type: int
104        default: 3
105        version_added: "2.5"
106
107    selfsigned_digest:
108        description:
109            - Digest algorithm to be used when self-signing the certificate.
110            - This is only used by the C(selfsigned) provider.
111        type: str
112        default: sha256
113
114    selfsigned_not_before:
115        description:
116            - The point in time the certificate is valid from.
117            - Time can be specified either as relative time or as absolute timestamp.
118            - Time will always be interpreted as UTC.
119            - Valid format is C([+-]timespec | ASN.1 TIME) where timespec can be an integer
120              + C([w | d | h | m | s]) (e.g. C(+32w1d2h).
121            - Note that if using relative time this module is NOT idempotent.
122            - If this value is not specified, the certificate will start being valid from now.
123            - This is only used by the C(selfsigned) provider.
124        type: str
125        default: +0s
126        aliases: [ selfsigned_notBefore ]
127
128    selfsigned_not_after:
129        description:
130            - The point in time at which the certificate stops being valid.
131            - Time can be specified either as relative time or as absolute timestamp.
132            - Time will always be interpreted as UTC.
133            - Valid format is C([+-]timespec | ASN.1 TIME) where timespec can be an integer
134              + C([w | d | h | m | s]) (e.g. C(+32w1d2h).
135            - Note that if using relative time this module is NOT idempotent.
136            - If this value is not specified, the certificate will stop being valid 10 years from now.
137            - This is only used by the C(selfsigned) provider.
138        type: str
139        default: +3650d
140        aliases: [ selfsigned_notAfter ]
141
142    selfsigned_create_subject_key_identifier:
143        description:
144            - Whether to create the Subject Key Identifier (SKI) from the public key.
145            - A value of C(create_if_not_provided) (default) only creates a SKI when the CSR does not
146              provide one.
147            - A value of C(always_create) always creates a SKI. If the CSR provides one, that one is
148              ignored.
149            - A value of C(never_create) never creates a SKI. If the CSR provides one, that one is used.
150            - This is only used by the C(selfsigned) provider.
151            - Note that this is only supported if the C(cryptography) backend is used!
152        type: str
153        choices: [create_if_not_provided, always_create, never_create]
154        default: create_if_not_provided
155        version_added: "2.9"
156
157    ownca_path:
158        description:
159            - Remote absolute path of the CA (Certificate Authority) certificate.
160            - This is only used by the C(ownca) provider.
161        type: path
162        version_added: "2.7"
163
164    ownca_privatekey_path:
165        description:
166            - Path to the CA (Certificate Authority) private key to use when signing the certificate.
167            - This is only used by the C(ownca) provider.
168        type: path
169        version_added: "2.7"
170
171    ownca_privatekey_passphrase:
172        description:
173            - The passphrase for the I(ownca_privatekey_path).
174            - This is only used by the C(ownca) provider.
175        type: str
176        version_added: "2.7"
177
178    ownca_digest:
179        description:
180            - The digest algorithm to be used for the C(ownca) certificate.
181            - This is only used by the C(ownca) provider.
182        type: str
183        default: sha256
184        version_added: "2.7"
185
186    ownca_version:
187        description:
188            - The version of the C(ownca) certificate.
189            - Nowadays it should almost always be C(3).
190            - This is only used by the C(ownca) provider.
191        type: int
192        default: 3
193        version_added: "2.7"
194
195    ownca_not_before:
196        description:
197            - The point in time the certificate is valid from.
198            - Time can be specified either as relative time or as absolute timestamp.
199            - Time will always be interpreted as UTC.
200            - Valid format is C([+-]timespec | ASN.1 TIME) where timespec can be an integer
201              + C([w | d | h | m | s]) (e.g. C(+32w1d2h).
202            - Note that if using relative time this module is NOT idempotent.
203            - If this value is not specified, the certificate will start being valid from now.
204            - This is only used by the C(ownca) provider.
205        type: str
206        default: +0s
207        version_added: "2.7"
208
209    ownca_not_after:
210        description:
211            - The point in time at which the certificate stops being valid.
212            - Time can be specified either as relative time or as absolute timestamp.
213            - Time will always be interpreted as UTC.
214            - Valid format is C([+-]timespec | ASN.1 TIME) where timespec can be an integer
215              + C([w | d | h | m | s]) (e.g. C(+32w1d2h).
216            - Note that if using relative time this module is NOT idempotent.
217            - If this value is not specified, the certificate will stop being valid 10 years from now.
218            - This is only used by the C(ownca) provider.
219        type: str
220        default: +3650d
221        version_added: "2.7"
222
223    ownca_create_subject_key_identifier:
224        description:
225            - Whether to create the Subject Key Identifier (SKI) from the public key.
226            - A value of C(create_if_not_provided) (default) only creates a SKI when the CSR does not
227              provide one.
228            - A value of C(always_create) always creates a SKI. If the CSR provides one, that one is
229              ignored.
230            - A value of C(never_create) never creates a SKI. If the CSR provides one, that one is used.
231            - This is only used by the C(ownca) provider.
232            - Note that this is only supported if the C(cryptography) backend is used!
233        type: str
234        choices: [create_if_not_provided, always_create, never_create]
235        default: create_if_not_provided
236        version_added: "2.9"
237
238    ownca_create_authority_key_identifier:
239        description:
240            - Create a Authority Key Identifier from the CA's certificate. If the CSR provided
241              a authority key identifier, it is ignored.
242            - The Authority Key Identifier is generated from the CA certificate's Subject Key Identifier,
243              if available. If it is not available, the CA certificate's public key will be used.
244            - This is only used by the C(ownca) provider.
245            - Note that this is only supported if the C(cryptography) backend is used!
246        type: bool
247        default: yes
248        version_added: "2.9"
249
250    acme_accountkey_path:
251        description:
252            - The path to the accountkey for the C(acme) provider.
253            - This is only used by the C(acme) provider.
254        type: path
255
256    acme_challenge_path:
257        description:
258            - The path to the ACME challenge directory that is served on U(http://<HOST>:80/.well-known/acme-challenge/)
259            - This is only used by the C(acme) provider.
260        type: path
261
262    acme_chain:
263        description:
264            - Include the intermediate certificate to the generated certificate
265            - This is only used by the C(acme) provider.
266            - Note that this is only available for older versions of C(acme-tiny).
267              New versions include the chain automatically, and setting I(acme_chain) to C(yes) results in an error.
268        type: bool
269        default: no
270        version_added: "2.5"
271
272    signature_algorithms:
273        description:
274            - A list of algorithms that you would accept the certificate to be signed with
275              (e.g. ['sha256WithRSAEncryption', 'sha512WithRSAEncryption']).
276            - This is only used by the C(assertonly) provider.
277            - This option is deprecated since Ansible 2.9 and will be removed with the C(assertonly) provider in Ansible 2.13.
278              For alternatives, see the example on replacing C(assertonly).
279        type: list
280        elements: str
281
282    issuer:
283        description:
284            - The key/value pairs that must be present in the issuer name field of the certificate.
285            - If you need to specify more than one value with the same key, use a list as value.
286            - This is only used by the C(assertonly) provider.
287            - This option is deprecated since Ansible 2.9 and will be removed with the C(assertonly) provider in Ansible 2.13.
288              For alternatives, see the example on replacing C(assertonly).
289        type: dict
290
291    issuer_strict:
292        description:
293            - If set to C(yes), the I(issuer) field must contain only these values.
294            - This is only used by the C(assertonly) provider.
295            - This option is deprecated since Ansible 2.9 and will be removed with the C(assertonly) provider in Ansible 2.13.
296              For alternatives, see the example on replacing C(assertonly).
297        type: bool
298        default: no
299        version_added: "2.5"
300
301    subject:
302        description:
303            - The key/value pairs that must be present in the subject name field of the certificate.
304            - If you need to specify more than one value with the same key, use a list as value.
305            - This is only used by the C(assertonly) provider.
306            - This option is deprecated since Ansible 2.9 and will be removed with the C(assertonly) provider in Ansible 2.13.
307              For alternatives, see the example on replacing C(assertonly).
308        type: dict
309
310    subject_strict:
311        description:
312            - If set to C(yes), the I(subject) field must contain only these values.
313            - This is only used by the C(assertonly) provider.
314            - This option is deprecated since Ansible 2.9 and will be removed with the C(assertonly) provider in Ansible 2.13.
315              For alternatives, see the example on replacing C(assertonly).
316        type: bool
317        default: no
318        version_added: "2.5"
319
320    has_expired:
321        description:
322            - Checks if the certificate is expired/not expired at the time the module is executed.
323            - This is only used by the C(assertonly) provider.
324            - This option is deprecated since Ansible 2.9 and will be removed with the C(assertonly) provider in Ansible 2.13.
325              For alternatives, see the example on replacing C(assertonly).
326        type: bool
327        default: no
328
329    version:
330        description:
331            - The version of the certificate.
332            - Nowadays it should almost always be 3.
333            - This is only used by the C(assertonly) provider.
334            - This option is deprecated since Ansible 2.9 and will be removed with the C(assertonly) provider in Ansible 2.13.
335              For alternatives, see the example on replacing C(assertonly).
336        type: int
337
338    valid_at:
339        description:
340            - The certificate must be valid at this point in time.
341            - The timestamp is formatted as an ASN.1 TIME.
342            - This is only used by the C(assertonly) provider.
343            - This option is deprecated since Ansible 2.9 and will be removed with the C(assertonly) provider in Ansible 2.13.
344              For alternatives, see the example on replacing C(assertonly).
345        type: str
346
347    invalid_at:
348        description:
349            - The certificate must be invalid at this point in time.
350            - The timestamp is formatted as an ASN.1 TIME.
351            - This is only used by the C(assertonly) provider.
352            - This option is deprecated since Ansible 2.9 and will be removed with the C(assertonly) provider in Ansible 2.13.
353              For alternatives, see the example on replacing C(assertonly).
354        type: str
355
356    not_before:
357        description:
358            - The certificate must start to become valid at this point in time.
359            - The timestamp is formatted as an ASN.1 TIME.
360            - This is only used by the C(assertonly) provider.
361            - This option is deprecated since Ansible 2.9 and will be removed with the C(assertonly) provider in Ansible 2.13.
362              For alternatives, see the example on replacing C(assertonly).
363        type: str
364        aliases: [ notBefore ]
365
366    not_after:
367        description:
368            - The certificate must expire at this point in time.
369            - The timestamp is formatted as an ASN.1 TIME.
370            - This is only used by the C(assertonly) provider.
371            - This option is deprecated since Ansible 2.9 and will be removed with the C(assertonly) provider in Ansible 2.13.
372              For alternatives, see the example on replacing C(assertonly).
373        type: str
374        aliases: [ notAfter ]
375
376    valid_in:
377        description:
378            - The certificate must still be valid at this relative time offset from now.
379            - Valid format is C([+-]timespec | number_of_seconds) where timespec can be an integer
380              + C([w | d | h | m | s]) (e.g. C(+32w1d2h).
381            - Note that if using this parameter, this module is NOT idempotent.
382            - This is only used by the C(assertonly) provider.
383            - This option is deprecated since Ansible 2.9 and will be removed with the C(assertonly) provider in Ansible 2.13.
384              For alternatives, see the example on replacing C(assertonly).
385        type: str
386
387    key_usage:
388        description:
389            - The I(key_usage) extension field must contain all these values.
390            - This is only used by the C(assertonly) provider.
391            - This option is deprecated since Ansible 2.9 and will be removed with the C(assertonly) provider in Ansible 2.13.
392              For alternatives, see the example on replacing C(assertonly).
393        type: list
394        elements: str
395        aliases: [ keyUsage ]
396
397    key_usage_strict:
398        description:
399            - If set to C(yes), the I(key_usage) extension field must contain only these values.
400            - This is only used by the C(assertonly) provider.
401            - This option is deprecated since Ansible 2.9 and will be removed with the C(assertonly) provider in Ansible 2.13.
402              For alternatives, see the example on replacing C(assertonly).
403        type: bool
404        default: no
405        aliases: [ keyUsage_strict ]
406
407    extended_key_usage:
408        description:
409            - The I(extended_key_usage) extension field must contain all these values.
410            - This is only used by the C(assertonly) provider.
411            - This option is deprecated since Ansible 2.9 and will be removed with the C(assertonly) provider in Ansible 2.13.
412              For alternatives, see the example on replacing C(assertonly).
413        type: list
414        elements: str
415        aliases: [ extendedKeyUsage ]
416
417    extended_key_usage_strict:
418        description:
419            - If set to C(yes), the I(extended_key_usage) extension field must contain only these values.
420            - This is only used by the C(assertonly) provider.
421            - This option is deprecated since Ansible 2.9 and will be removed with the C(assertonly) provider in Ansible 2.13.
422              For alternatives, see the example on replacing C(assertonly).
423        type: bool
424        default: no
425        aliases: [ extendedKeyUsage_strict ]
426
427    subject_alt_name:
428        description:
429            - The I(subject_alt_name) extension field must contain these values.
430            - This is only used by the C(assertonly) provider.
431            - This option is deprecated since Ansible 2.9 and will be removed with the C(assertonly) provider in Ansible 2.13.
432              For alternatives, see the example on replacing C(assertonly).
433        type: list
434        elements: str
435        aliases: [ subjectAltName ]
436
437    subject_alt_name_strict:
438        description:
439            - If set to C(yes), the I(subject_alt_name) extension field must contain only these values.
440            - This is only used by the C(assertonly) provider.
441            - This option is deprecated since Ansible 2.9 and will be removed with the C(assertonly) provider in Ansible 2.13.
442              For alternatives, see the example on replacing C(assertonly).
443        type: bool
444        default: no
445        aliases: [ subjectAltName_strict ]
446
447    select_crypto_backend:
448        description:
449            - Determines which crypto backend to use.
450            - The default choice is C(auto), which tries to use C(cryptography) if available, and falls back to C(pyopenssl).
451            - If set to C(pyopenssl), will try to use the L(pyOpenSSL,https://pypi.org/project/pyOpenSSL/) library.
452            - If set to C(cryptography), will try to use the L(cryptography,https://cryptography.io/) library.
453            - Please note that the C(pyopenssl) backend has been deprecated in Ansible 2.9, and will be removed in Ansible 2.13.
454              From that point on, only the C(cryptography) backend will be available.
455        type: str
456        default: auto
457        choices: [ auto, cryptography, pyopenssl ]
458        version_added: "2.8"
459
460    backup:
461        description:
462            - Create a backup file including a timestamp so you can get the original
463              certificate back if you overwrote it with a new one by accident.
464            - This is not used by the C(assertonly) provider.
465            - This option is deprecated since Ansible 2.9 and will be removed with the C(assertonly) provider in Ansible 2.13.
466              For alternatives, see the example on replacing C(assertonly).
467        type: bool
468        default: no
469        version_added: "2.8"
470
471    entrust_cert_type:
472        description:
473            - Specify the type of certificate requested.
474            - This is only used by the C(entrust) provider.
475        type: str
476        default: STANDARD_SSL
477        choices: [ 'STANDARD_SSL', 'ADVANTAGE_SSL', 'UC_SSL', 'EV_SSL', 'WILDCARD_SSL', 'PRIVATE_SSL', 'PD_SSL', 'CDS_ENT_LITE', 'CDS_ENT_PRO', 'SMIME_ENT' ]
478        version_added: "2.9"
479
480    entrust_requester_email:
481        description:
482            - The email of the requester of the certificate (for tracking purposes).
483            - This is only used by the C(entrust) provider.
484            - This is required if the provider is C(entrust).
485        type: str
486        version_added: "2.9"
487
488    entrust_requester_name:
489        description:
490            - The name of the requester of the certificate (for tracking purposes).
491            - This is only used by the C(entrust) provider.
492            - This is required if the provider is C(entrust).
493        type: str
494        version_added: "2.9"
495
496    entrust_requester_phone:
497        description:
498            - The phone number of the requester of the certificate (for tracking purposes).
499            - This is only used by the C(entrust) provider.
500            - This is required if the provider is C(entrust).
501        type: str
502        version_added: "2.9"
503
504    entrust_api_user:
505        description:
506            - The username for authentication to the Entrust Certificate Services (ECS) API.
507            - This is only used by the C(entrust) provider.
508            - This is required if the provider is C(entrust).
509        type: str
510        version_added: "2.9"
511
512    entrust_api_key:
513        description:
514            - The key (password) for authentication to the Entrust Certificate Services (ECS) API.
515            - This is only used by the C(entrust) provider.
516            - This is required if the provider is C(entrust).
517        type: str
518        version_added: "2.9"
519
520    entrust_api_client_cert_path:
521        description:
522            - The path to the client certificate used to authenticate to the Entrust Certificate Services (ECS) API.
523            - This is only used by the C(entrust) provider.
524            - This is required if the provider is C(entrust).
525        type: path
526        version_added: "2.9"
527
528    entrust_api_client_cert_key_path:
529        description:
530            - The path to the private key of the client certificate used to authenticate to the Entrust Certificate Services (ECS) API.
531            - This is only used by the C(entrust) provider.
532            - This is required if the provider is C(entrust).
533        type: path
534        version_added: "2.9"
535
536    entrust_not_after:
537        description:
538            - The point in time at which the certificate stops being valid.
539            - Time can be specified either as relative time or as an absolute timestamp.
540            - A valid absolute time format is C(ASN.1 TIME) such as C(2019-06-18).
541            - A valid relative time format is C([+-]timespec) where timespec can be an integer + C([w | d | h | m | s]), such as C(+365d) or C(+32w1d2h)).
542            - Time will always be interpreted as UTC.
543            - Note that only the date (day, month, year) is supported for specifying the expiry date of the issued certificate.
544            - The full date-time is adjusted to EST (GMT -5:00) before issuance, which may result in a certificate with an expiration date one day
545              earlier than expected if a relative time is used.
546            - The minimum certificate lifetime is 90 days, and maximum is three years.
547            - If this value is not specified, the certificate will stop being valid 365 days the date of issue.
548            - This is only used by the C(entrust) provider.
549        type: str
550        default: +365d
551        version_added: "2.9"
552
553    entrust_api_specification_path:
554        description:
555            - The path to the specification file defining the Entrust Certificate Services (ECS) API configuration.
556            - You can use this to keep a local copy of the specification to avoid downloading it every time the module is used.
557            - This is only used by the C(entrust) provider.
558        type: path
559        default: https://cloud.entrust.net/EntrustCloud/documentation/cms-api-2.1.0.yaml
560        version_added: "2.9"
561
562extends_documentation_fragment: files
563notes:
564    - All ASN.1 TIME values should be specified following the YYYYMMDDHHMMSSZ pattern.
565    - Date specified should be UTC. Minutes and seconds are mandatory.
566    - For security reason, when you use C(ownca) provider, you should NOT run M(openssl_certificate) on
567      a target machine, but on a dedicated CA machine. It is recommended not to store the CA private key
568      on the target machine. Once signed, the certificate can be moved to the target machine.
569seealso:
570- module: openssl_csr
571- module: openssl_dhparam
572- module: openssl_pkcs12
573- module: openssl_privatekey
574- module: openssl_publickey
575'''
576
577EXAMPLES = r'''
578- name: Generate a Self Signed OpenSSL certificate
579  openssl_certificate:
580    path: /etc/ssl/crt/ansible.com.crt
581    privatekey_path: /etc/ssl/private/ansible.com.pem
582    csr_path: /etc/ssl/csr/ansible.com.csr
583    provider: selfsigned
584
585- name: Generate an OpenSSL certificate signed with your own CA certificate
586  openssl_certificate:
587    path: /etc/ssl/crt/ansible.com.crt
588    csr_path: /etc/ssl/csr/ansible.com.csr
589    ownca_path: /etc/ssl/crt/ansible_CA.crt
590    ownca_privatekey_path: /etc/ssl/private/ansible_CA.pem
591    provider: ownca
592
593- name: Generate a Let's Encrypt Certificate
594  openssl_certificate:
595    path: /etc/ssl/crt/ansible.com.crt
596    csr_path: /etc/ssl/csr/ansible.com.csr
597    provider: acme
598    acme_accountkey_path: /etc/ssl/private/ansible.com.pem
599    acme_challenge_path: /etc/ssl/challenges/ansible.com/
600
601- name: Force (re-)generate a new Let's Encrypt Certificate
602  openssl_certificate:
603    path: /etc/ssl/crt/ansible.com.crt
604    csr_path: /etc/ssl/csr/ansible.com.csr
605    provider: acme
606    acme_accountkey_path: /etc/ssl/private/ansible.com.pem
607    acme_challenge_path: /etc/ssl/challenges/ansible.com/
608    force: yes
609
610- name: Generate an Entrust certificate via the Entrust Certificate Services (ECS) API
611  openssl_certificate:
612    path: /etc/ssl/crt/ansible.com.crt
613    csr_path: /etc/ssl/csr/ansible.com.csr
614    provider: entrust
615    entrust_requester_name: Jo Doe
616    entrust_requester_email: jdoe@ansible.com
617    entrust_requester_phone: 555-555-5555
618    entrust_cert_type: STANDARD_SSL
619    entrust_api_user: apiusername
620    entrust_api_key: a^lv*32!cd9LnT
621    entrust_api_client_cert_path: /etc/ssl/entrust/ecs-client.crt
622    entrust_api_client_cert_key_path: /etc/ssl/entrust/ecs-key.crt
623    entrust_api_specification_path: /etc/ssl/entrust/api-docs/cms-api-2.1.0.yaml
624
625# The following example shows one assertonly usage using all existing options for
626# assertonly, and shows how to emulate the behavior with the openssl_certificate_info,
627# openssl_csr_info, openssl_privatekey_info and assert modules:
628
629- openssl_certificate:
630    provider: assertonly
631    path: /etc/ssl/crt/ansible.com.crt
632    csr_path: /etc/ssl/csr/ansible.com.csr
633    privatekey_path: /etc/ssl/csr/ansible.com.key
634    signature_algorithms:
635      - sha256WithRSAEncryption
636      - sha512WithRSAEncryption
637    subject:
638      commonName: ansible.com
639    subject_strict: yes
640    issuer:
641      commonName: ansible.com
642    issuer_strict: yes
643    has_expired: no
644    version: 3
645    key_usage:
646      - Data Encipherment
647    key_usage_strict: yes
648    extended_key_usage:
649      - DVCS
650    extended_key_usage_strict: yes
651    subject_alt_name:
652      - dns:ansible.com
653    subject_alt_name_strict: yes
654    not_before: 20190331202428Z
655    not_after: 20190413202428Z
656    valid_at: "+1d10h"
657    invalid_at: 20200331202428Z
658    valid_in: 10  # in ten seconds
659
660- openssl_certificate_info:
661    path: /etc/ssl/crt/ansible.com.crt
662    # for valid_at, invalid_at and valid_in
663    valid_at:
664      one_day_ten_hours: "+1d10h"
665      fixed_timestamp: 20200331202428Z
666      ten_seconds: "+10"
667  register: result
668
669- openssl_csr_info:
670    # Verifies that the CSR signature is valid; module will fail if not
671    path: /etc/ssl/csr/ansible.com.csr
672  register: result_csr
673
674- openssl_privatekey_info:
675    path: /etc/ssl/csr/ansible.com.key
676  register: result_privatekey
677
678- assert:
679    that:
680      # When private key is specified for assertonly, this will be checked:
681      - result.public_key == result_privatekey.public_key
682      # When CSR is specified for assertonly, this will be checked:
683      - result.public_key == result_csr.public_key
684      - result.subject_ordered == result_csr.subject_ordered
685      - result.extensions_by_oid == result_csr.extensions_by_oid
686      # signature_algorithms check
687      - "result.signature_algorithm == 'sha256WithRSAEncryption' or result.signature_algorithm == 'sha512WithRSAEncryption'"
688      # subject and subject_strict
689      - "result.subject.commonName == 'ansible.com'"
690      - "result.subject | length == 1"  # the number must be the number of entries you check for
691      # issuer and issuer_strict
692      - "result.issuer.commonName == 'ansible.com'"
693      - "result.issuer | length == 1"  # the number must be the number of entries you check for
694      # has_expired
695      - not result.expired
696      # version
697      - result.version == 3
698      # key_usage and key_usage_strict
699      - "'Data Encipherment' in result.key_usage"
700      - "result.key_usage | length == 1"  # the number must be the number of entries you check for
701      # extended_key_usage and extended_key_usage_strict
702      - "'DVCS' in result.extended_key_usage"
703      - "result.extended_key_usage | length == 1"  # the number must be the number of entries you check for
704      # subject_alt_name and subject_alt_name_strict
705      - "'dns:ansible.com' in result.subject_alt_name"
706      - "result.subject_alt_name | length == 1"  # the number must be the number of entries you check for
707      # not_before and not_after
708      - "result.not_before == '20190331202428Z'"
709      - "result.not_after == '20190413202428Z'"
710      # valid_at, invalid_at and valid_in
711      - "result.valid_at.one_day_ten_hours"  # for valid_at
712      - "not result.valid_at.fixed_timestamp"  # for invalid_at
713      - "result.valid_at.ten_seconds"  # for valid_in
714
715# Examples for some checks one could use the assertonly provider for:
716# (Please note that assertonly has been deprecated!)
717
718# How to use the assertonly provider to implement and trigger your own custom certificate generation workflow:
719- name: Check if a certificate is currently still valid, ignoring failures
720  openssl_certificate:
721    path: /etc/ssl/crt/example.com.crt
722    provider: assertonly
723    has_expired: no
724  ignore_errors: yes
725  register: validity_check
726
727- name: Run custom task(s) to get a new, valid certificate in case the initial check failed
728  command: superspecialSSL recreate /etc/ssl/crt/example.com.crt
729  when: validity_check.failed
730
731- name: Check the new certificate again for validity with the same parameters, this time failing the play if it is still invalid
732  openssl_certificate:
733    path: /etc/ssl/crt/example.com.crt
734    provider: assertonly
735    has_expired: no
736  when: validity_check.failed
737
738# Some other checks that assertonly could be used for:
739- name: Verify that an existing certificate was issued by the Let's Encrypt CA and is currently still valid
740  openssl_certificate:
741    path: /etc/ssl/crt/example.com.crt
742    provider: assertonly
743    issuer:
744      O: Let's Encrypt
745    has_expired: no
746
747- name: Ensure that a certificate uses a modern signature algorithm (no SHA1, MD5 or DSA)
748  openssl_certificate:
749    path: /etc/ssl/crt/example.com.crt
750    provider: assertonly
751    signature_algorithms:
752      - sha224WithRSAEncryption
753      - sha256WithRSAEncryption
754      - sha384WithRSAEncryption
755      - sha512WithRSAEncryption
756      - sha224WithECDSAEncryption
757      - sha256WithECDSAEncryption
758      - sha384WithECDSAEncryption
759      - sha512WithECDSAEncryption
760
761- name: Ensure that the existing certificate belongs to the specified private key
762  openssl_certificate:
763    path: /etc/ssl/crt/example.com.crt
764    privatekey_path: /etc/ssl/private/example.com.pem
765    provider: assertonly
766
767- name: Ensure that the existing certificate is still valid at the winter solstice 2017
768  openssl_certificate:
769    path: /etc/ssl/crt/example.com.crt
770    provider: assertonly
771    valid_at: 20171221162800Z
772
773- name: Ensure that the existing certificate is still valid 2 weeks (1209600 seconds) from now
774  openssl_certificate:
775    path: /etc/ssl/crt/example.com.crt
776    provider: assertonly
777    valid_in: 1209600
778
779- name: Ensure that the existing certificate is only used for digital signatures and encrypting other keys
780  openssl_certificate:
781    path: /etc/ssl/crt/example.com.crt
782    provider: assertonly
783    key_usage:
784      - digitalSignature
785      - keyEncipherment
786    key_usage_strict: true
787
788- name: Ensure that the existing certificate can be used for client authentication
789  openssl_certificate:
790    path: /etc/ssl/crt/example.com.crt
791    provider: assertonly
792    extended_key_usage:
793      - clientAuth
794
795- name: Ensure that the existing certificate can only be used for client authentication and time stamping
796  openssl_certificate:
797    path: /etc/ssl/crt/example.com.crt
798    provider: assertonly
799    extended_key_usage:
800      - clientAuth
801      - 1.3.6.1.5.5.7.3.8
802    extended_key_usage_strict: true
803
804- name: Ensure that the existing certificate has a certain domain in its subjectAltName
805  openssl_certificate:
806    path: /etc/ssl/crt/example.com.crt
807    provider: assertonly
808    subject_alt_name:
809      - www.example.com
810      - test.example.com
811'''
812
813RETURN = r'''
814filename:
815    description: Path to the generated Certificate
816    returned: changed or success
817    type: str
818    sample: /etc/ssl/crt/www.ansible.com.crt
819backup_file:
820    description: Name of backup file created.
821    returned: changed and if I(backup) is C(yes)
822    type: str
823    sample: /path/to/www.ansible.com.crt.2019-03-09@11:22~
824'''
825
826
827from random import randint
828import abc
829import datetime
830import time
831import os
832import traceback
833from distutils.version import LooseVersion
834
835from ansible.module_utils import crypto as crypto_utils
836from ansible.module_utils.basic import AnsibleModule, missing_required_lib
837from ansible.module_utils._text import to_native, to_bytes, to_text
838from ansible.module_utils.compat import ipaddress as compat_ipaddress
839from ansible.module_utils.ecs.api import ECSClient, RestOperationException, SessionConfigurationException
840
841MINIMAL_CRYPTOGRAPHY_VERSION = '1.6'
842MINIMAL_PYOPENSSL_VERSION = '0.15'
843
844PYOPENSSL_IMP_ERR = None
845try:
846    import OpenSSL
847    from OpenSSL import crypto
848    PYOPENSSL_VERSION = LooseVersion(OpenSSL.__version__)
849except ImportError:
850    PYOPENSSL_IMP_ERR = traceback.format_exc()
851    PYOPENSSL_FOUND = False
852else:
853    PYOPENSSL_FOUND = True
854
855CRYPTOGRAPHY_IMP_ERR = None
856try:
857    import cryptography
858    from cryptography import x509
859    from cryptography.hazmat.backends import default_backend
860    from cryptography.hazmat.primitives.serialization import Encoding
861    from cryptography.x509 import NameAttribute, Name
862    from cryptography.x509.oid import NameOID
863    CRYPTOGRAPHY_VERSION = LooseVersion(cryptography.__version__)
864except ImportError:
865    CRYPTOGRAPHY_IMP_ERR = traceback.format_exc()
866    CRYPTOGRAPHY_FOUND = False
867else:
868    CRYPTOGRAPHY_FOUND = True
869
870
871class CertificateError(crypto_utils.OpenSSLObjectError):
872    pass
873
874
875class Certificate(crypto_utils.OpenSSLObject):
876
877    def __init__(self, module, backend):
878        super(Certificate, self).__init__(
879            module.params['path'],
880            module.params['state'],
881            module.params['force'],
882            module.check_mode
883        )
884
885        self.provider = module.params['provider']
886        self.privatekey_path = module.params['privatekey_path']
887        self.privatekey_passphrase = module.params['privatekey_passphrase']
888        self.csr_path = module.params['csr_path']
889        self.cert = None
890        self.privatekey = None
891        self.csr = None
892        self.backend = backend
893        self.module = module
894
895        # The following are default values which make sure check() works as
896        # before if providers do not explicitly change these properties.
897        self.create_subject_key_identifier = 'never_create'
898        self.create_authority_key_identifier = False
899
900        self.backup = module.params['backup']
901        self.backup_file = None
902
903    def get_relative_time_option(self, input_string, input_name):
904        """Return an ASN1 formatted string if a relative timespec
905           or an ASN1 formatted string is provided."""
906        result = to_native(input_string)
907        if result is None:
908            raise CertificateError(
909                'The timespec "%s" for %s is not valid' %
910                input_string, input_name)
911        if result.startswith("+") or result.startswith("-"):
912            result_datetime = crypto_utils.convert_relative_to_datetime(
913                result)
914            if self.backend == 'pyopenssl':
915                return result_datetime.strftime("%Y%m%d%H%M%SZ")
916            elif self.backend == 'cryptography':
917                return result_datetime
918        if self.backend == 'cryptography':
919            for date_fmt in ['%Y%m%d%H%M%SZ', '%Y%m%d%H%MZ', '%Y%m%d%H%M%S%z', '%Y%m%d%H%M%z']:
920                try:
921                    return datetime.datetime.strptime(result, date_fmt)
922                except ValueError:
923                    pass
924
925            raise CertificateError(
926                'The time spec "%s" for %s is invalid' %
927                (input_string, input_name)
928            )
929        return input_string
930
931    def _validate_privatekey(self):
932        if self.backend == 'pyopenssl':
933            ctx = OpenSSL.SSL.Context(OpenSSL.SSL.TLSv1_2_METHOD)
934            ctx.use_privatekey(self.privatekey)
935            ctx.use_certificate(self.cert)
936            try:
937                ctx.check_privatekey()
938                return True
939            except OpenSSL.SSL.Error:
940                return False
941        elif self.backend == 'cryptography':
942            return crypto_utils.cryptography_compare_public_keys(self.cert.public_key(), self.privatekey.public_key())
943
944    def _validate_csr(self):
945        if self.backend == 'pyopenssl':
946            # Verify that CSR is signed by certificate's private key
947            try:
948                self.csr.verify(self.cert.get_pubkey())
949            except OpenSSL.crypto.Error:
950                return False
951            # Check subject
952            if self.csr.get_subject() != self.cert.get_subject():
953                return False
954            # Check extensions
955            csr_extensions = self.csr.get_extensions()
956            cert_extension_count = self.cert.get_extension_count()
957            if len(csr_extensions) != cert_extension_count:
958                return False
959            for extension_number in range(0, cert_extension_count):
960                cert_extension = self.cert.get_extension(extension_number)
961                csr_extension = filter(lambda extension: extension.get_short_name() == cert_extension.get_short_name(), csr_extensions)
962                if cert_extension.get_data() != list(csr_extension)[0].get_data():
963                    return False
964            return True
965        elif self.backend == 'cryptography':
966            # Verify that CSR is signed by certificate's private key
967            if not self.csr.is_signature_valid:
968                return False
969            if not crypto_utils.cryptography_compare_public_keys(self.csr.public_key(), self.cert.public_key()):
970                return False
971            # Check subject
972            if self.csr.subject != self.cert.subject:
973                return False
974            # Check extensions
975            cert_exts = list(self.cert.extensions)
976            csr_exts = list(self.csr.extensions)
977            if self.create_subject_key_identifier != 'never_create':
978                # Filter out SubjectKeyIdentifier extension before comparison
979                cert_exts = list(filter(lambda x: not isinstance(x.value, x509.SubjectKeyIdentifier), cert_exts))
980                csr_exts = list(filter(lambda x: not isinstance(x.value, x509.SubjectKeyIdentifier), csr_exts))
981            if self.create_authority_key_identifier:
982                # Filter out AuthorityKeyIdentifier extension before comparison
983                cert_exts = list(filter(lambda x: not isinstance(x.value, x509.AuthorityKeyIdentifier), cert_exts))
984                csr_exts = list(filter(lambda x: not isinstance(x.value, x509.AuthorityKeyIdentifier), csr_exts))
985            if len(cert_exts) != len(csr_exts):
986                return False
987            for cert_ext in cert_exts:
988                try:
989                    csr_ext = self.csr.extensions.get_extension_for_oid(cert_ext.oid)
990                    if cert_ext != csr_ext:
991                        return False
992                except cryptography.x509.ExtensionNotFound as dummy:
993                    return False
994            return True
995
996    def remove(self, module):
997        if self.backup:
998            self.backup_file = module.backup_local(self.path)
999        super(Certificate, self).remove(module)
1000
1001    def check(self, module, perms_required=True):
1002        """Ensure the resource is in its desired state."""
1003
1004        state_and_perms = super(Certificate, self).check(module, perms_required)
1005
1006        if not state_and_perms:
1007            return False
1008
1009        try:
1010            self.cert = crypto_utils.load_certificate(self.path, backend=self.backend)
1011        except Exception as dummy:
1012            return False
1013
1014        if self.privatekey_path:
1015            try:
1016                self.privatekey = crypto_utils.load_privatekey(
1017                    self.privatekey_path,
1018                    self.privatekey_passphrase,
1019                    backend=self.backend
1020                )
1021            except crypto_utils.OpenSSLBadPassphraseError as exc:
1022                raise CertificateError(exc)
1023            if not self._validate_privatekey():
1024                return False
1025
1026        if self.csr_path:
1027            self.csr = crypto_utils.load_certificate_request(self.csr_path, backend=self.backend)
1028            if not self._validate_csr():
1029                return False
1030
1031        # Check SubjectKeyIdentifier
1032        if self.backend == 'cryptography' and self.create_subject_key_identifier != 'never_create':
1033            # Get hold of certificate's SKI
1034            try:
1035                ext = self.cert.extensions.get_extension_for_class(x509.SubjectKeyIdentifier)
1036            except cryptography.x509.ExtensionNotFound as dummy:
1037                return False
1038            # Get hold of CSR's SKI for 'create_if_not_provided'
1039            csr_ext = None
1040            if self.create_subject_key_identifier == 'create_if_not_provided':
1041                try:
1042                    csr_ext = self.csr.extensions.get_extension_for_class(x509.SubjectKeyIdentifier)
1043                except cryptography.x509.ExtensionNotFound as dummy:
1044                    pass
1045            if csr_ext is None:
1046                # If CSR had no SKI, or we chose to ignore it ('always_create'), compare with created SKI
1047                if ext.value.digest != x509.SubjectKeyIdentifier.from_public_key(self.cert.public_key()).digest:
1048                    return False
1049            else:
1050                # If CSR had SKI and we didn't ignore it ('create_if_not_provided'), compare SKIs
1051                if ext.value.digest != csr_ext.value.digest:
1052                    return False
1053
1054        return True
1055
1056
1057class CertificateAbsent(Certificate):
1058    def __init__(self, module):
1059        super(CertificateAbsent, self).__init__(module, 'cryptography')  # backend doesn't matter
1060
1061    def generate(self, module):
1062        pass
1063
1064    def dump(self, check_mode=False):
1065        # Use only for absent
1066
1067        result = {
1068            'changed': self.changed,
1069            'filename': self.path,
1070            'privatekey': self.privatekey_path,
1071            'csr': self.csr_path
1072        }
1073        if self.backup_file:
1074            result['backup_file'] = self.backup_file
1075
1076        return result
1077
1078
1079class SelfSignedCertificateCryptography(Certificate):
1080    """Generate the self-signed certificate, using the cryptography backend"""
1081    def __init__(self, module):
1082        super(SelfSignedCertificateCryptography, self).__init__(module, 'cryptography')
1083        self.create_subject_key_identifier = module.params['selfsigned_create_subject_key_identifier']
1084        self.notBefore = self.get_relative_time_option(module.params['selfsigned_not_before'], 'selfsigned_not_before')
1085        self.notAfter = self.get_relative_time_option(module.params['selfsigned_not_after'], 'selfsigned_not_after')
1086        self.digest = crypto_utils.select_message_digest(module.params['selfsigned_digest'])
1087        self.version = module.params['selfsigned_version']
1088        self.serial_number = x509.random_serial_number()
1089
1090        if not os.path.exists(self.csr_path):
1091            raise CertificateError(
1092                'The certificate signing request file {0} does not exist'.format(self.csr_path)
1093            )
1094        if not os.path.exists(self.privatekey_path):
1095            raise CertificateError(
1096                'The private key file {0} does not exist'.format(self.privatekey_path)
1097            )
1098
1099        self.csr = crypto_utils.load_certificate_request(self.csr_path, backend=self.backend)
1100        self._module = module
1101
1102        try:
1103            self.privatekey = crypto_utils.load_privatekey(
1104                self.privatekey_path, self.privatekey_passphrase, backend=self.backend
1105            )
1106        except crypto_utils.OpenSSLBadPassphraseError as exc:
1107            module.fail_json(msg=to_native(exc))
1108
1109        if crypto_utils.cryptography_key_needs_digest_for_signing(self.privatekey):
1110            if self.digest is None:
1111                raise CertificateError(
1112                    'The digest %s is not supported with the cryptography backend' % module.params['selfsigned_digest']
1113                )
1114        else:
1115            self.digest = None
1116
1117    def generate(self, module):
1118        if not os.path.exists(self.privatekey_path):
1119            raise CertificateError(
1120                'The private key %s does not exist' % self.privatekey_path
1121            )
1122        if not os.path.exists(self.csr_path):
1123            raise CertificateError(
1124                'The certificate signing request file %s does not exist' % self.csr_path
1125            )
1126        if not self.check(module, perms_required=False) or self.force:
1127            try:
1128                cert_builder = x509.CertificateBuilder()
1129                cert_builder = cert_builder.subject_name(self.csr.subject)
1130                cert_builder = cert_builder.issuer_name(self.csr.subject)
1131                cert_builder = cert_builder.serial_number(self.serial_number)
1132                cert_builder = cert_builder.not_valid_before(self.notBefore)
1133                cert_builder = cert_builder.not_valid_after(self.notAfter)
1134                cert_builder = cert_builder.public_key(self.privatekey.public_key())
1135                has_ski = False
1136                for extension in self.csr.extensions:
1137                    if isinstance(extension.value, x509.SubjectKeyIdentifier):
1138                        if self.create_subject_key_identifier == 'always_create':
1139                            continue
1140                        has_ski = True
1141                    cert_builder = cert_builder.add_extension(extension.value, critical=extension.critical)
1142                if not has_ski and self.create_subject_key_identifier != 'never_create':
1143                    cert_builder = cert_builder.add_extension(
1144                        x509.SubjectKeyIdentifier.from_public_key(self.privatekey.public_key()),
1145                        critical=False
1146                    )
1147            except ValueError as e:
1148                raise CertificateError(str(e))
1149
1150            try:
1151                certificate = cert_builder.sign(
1152                    private_key=self.privatekey, algorithm=self.digest,
1153                    backend=default_backend()
1154                )
1155            except TypeError as e:
1156                if str(e) == 'Algorithm must be a registered hash algorithm.' and self.digest is None:
1157                    module.fail_json(msg='Signing with Ed25519 and Ed448 keys requires cryptography 2.8 or newer.')
1158                raise
1159
1160            self.cert = certificate
1161
1162            if self.backup:
1163                self.backup_file = module.backup_local(self.path)
1164            crypto_utils.write_file(module, certificate.public_bytes(Encoding.PEM))
1165            self.changed = True
1166        else:
1167            self.cert = crypto_utils.load_certificate(self.path, backend=self.backend)
1168
1169        file_args = module.load_file_common_arguments(module.params)
1170        if module.set_fs_attributes_if_different(file_args, False):
1171            self.changed = True
1172
1173    def dump(self, check_mode=False):
1174
1175        result = {
1176            'changed': self.changed,
1177            'filename': self.path,
1178            'privatekey': self.privatekey_path,
1179            'csr': self.csr_path
1180        }
1181        if self.backup_file:
1182            result['backup_file'] = self.backup_file
1183
1184        if check_mode:
1185            result.update({
1186                'notBefore': self.notBefore.strftime("%Y%m%d%H%M%SZ"),
1187                'notAfter': self.notAfter.strftime("%Y%m%d%H%M%SZ"),
1188                'serial_number': self.serial_number,
1189            })
1190        else:
1191            result.update({
1192                'notBefore': self.cert.not_valid_before.strftime("%Y%m%d%H%M%SZ"),
1193                'notAfter': self.cert.not_valid_after.strftime("%Y%m%d%H%M%SZ"),
1194                'serial_number': self.cert.serial_number,
1195            })
1196
1197        return result
1198
1199
1200class SelfSignedCertificate(Certificate):
1201    """Generate the self-signed certificate."""
1202
1203    def __init__(self, module):
1204        super(SelfSignedCertificate, self).__init__(module, 'pyopenssl')
1205        if module.params['selfsigned_create_subject_key_identifier'] != 'create_if_not_provided':
1206            module.fail_json(msg='selfsigned_create_subject_key_identifier cannot be used with the pyOpenSSL backend!')
1207        self.notBefore = self.get_relative_time_option(module.params['selfsigned_not_before'], 'selfsigned_not_before')
1208        self.notAfter = self.get_relative_time_option(module.params['selfsigned_not_after'], 'selfsigned_not_after')
1209        self.digest = module.params['selfsigned_digest']
1210        self.version = module.params['selfsigned_version']
1211        self.serial_number = randint(1000, 99999)
1212
1213        if not os.path.exists(self.csr_path):
1214            raise CertificateError(
1215                'The certificate signing request file {0} does not exist'.format(self.csr_path)
1216            )
1217        if not os.path.exists(self.privatekey_path):
1218            raise CertificateError(
1219                'The private key file {0} does not exist'.format(self.privatekey_path)
1220            )
1221
1222        self.csr = crypto_utils.load_certificate_request(self.csr_path)
1223        try:
1224            self.privatekey = crypto_utils.load_privatekey(
1225                self.privatekey_path, self.privatekey_passphrase
1226            )
1227        except crypto_utils.OpenSSLBadPassphraseError as exc:
1228            module.fail_json(msg=str(exc))
1229
1230    def generate(self, module):
1231
1232        if not os.path.exists(self.privatekey_path):
1233            raise CertificateError(
1234                'The private key %s does not exist' % self.privatekey_path
1235            )
1236
1237        if not os.path.exists(self.csr_path):
1238            raise CertificateError(
1239                'The certificate signing request file %s does not exist' % self.csr_path
1240            )
1241
1242        if not self.check(module, perms_required=False) or self.force:
1243            cert = crypto.X509()
1244            cert.set_serial_number(self.serial_number)
1245            cert.set_notBefore(to_bytes(self.notBefore))
1246            cert.set_notAfter(to_bytes(self.notAfter))
1247            cert.set_subject(self.csr.get_subject())
1248            cert.set_issuer(self.csr.get_subject())
1249            cert.set_version(self.version - 1)
1250            cert.set_pubkey(self.csr.get_pubkey())
1251            cert.add_extensions(self.csr.get_extensions())
1252
1253            cert.sign(self.privatekey, self.digest)
1254            self.cert = cert
1255
1256            if self.backup:
1257                self.backup_file = module.backup_local(self.path)
1258            crypto_utils.write_file(module, crypto.dump_certificate(crypto.FILETYPE_PEM, self.cert))
1259            self.changed = True
1260
1261        file_args = module.load_file_common_arguments(module.params)
1262        if module.set_fs_attributes_if_different(file_args, False):
1263            self.changed = True
1264
1265    def dump(self, check_mode=False):
1266
1267        result = {
1268            'changed': self.changed,
1269            'filename': self.path,
1270            'privatekey': self.privatekey_path,
1271            'csr': self.csr_path
1272        }
1273        if self.backup_file:
1274            result['backup_file'] = self.backup_file
1275
1276        if check_mode:
1277            result.update({
1278                'notBefore': self.notBefore,
1279                'notAfter': self.notAfter,
1280                'serial_number': self.serial_number,
1281            })
1282        else:
1283            result.update({
1284                'notBefore': self.cert.get_notBefore(),
1285                'notAfter': self.cert.get_notAfter(),
1286                'serial_number': self.cert.get_serial_number(),
1287            })
1288
1289        return result
1290
1291
1292class OwnCACertificateCryptography(Certificate):
1293    """Generate the own CA certificate. Using the cryptography backend"""
1294    def __init__(self, module):
1295        super(OwnCACertificateCryptography, self).__init__(module, 'cryptography')
1296        self.create_subject_key_identifier = module.params['ownca_create_subject_key_identifier']
1297        self.create_authority_key_identifier = module.params['ownca_create_authority_key_identifier']
1298        self.notBefore = self.get_relative_time_option(module.params['ownca_not_before'], 'ownca_not_before')
1299        self.notAfter = self.get_relative_time_option(module.params['ownca_not_after'], 'ownca_not_after')
1300        self.digest = crypto_utils.select_message_digest(module.params['ownca_digest'])
1301        self.version = module.params['ownca_version']
1302        self.serial_number = x509.random_serial_number()
1303        self.ca_cert_path = module.params['ownca_path']
1304        self.ca_privatekey_path = module.params['ownca_privatekey_path']
1305        self.ca_privatekey_passphrase = module.params['ownca_privatekey_passphrase']
1306
1307        if not os.path.exists(self.csr_path):
1308            raise CertificateError(
1309                'The certificate signing request file {0} does not exist'.format(self.csr_path)
1310            )
1311        if not os.path.exists(self.ca_cert_path):
1312            raise CertificateError(
1313                'The CA certificate file {0} does not exist'.format(self.ca_cert_path)
1314            )
1315        if not os.path.exists(self.ca_privatekey_path):
1316            raise CertificateError(
1317                'The CA private key file {0} does not exist'.format(self.ca_privatekey_path)
1318            )
1319
1320        self.csr = crypto_utils.load_certificate_request(self.csr_path, backend=self.backend)
1321        self.ca_cert = crypto_utils.load_certificate(self.ca_cert_path, backend=self.backend)
1322        try:
1323            self.ca_private_key = crypto_utils.load_privatekey(
1324                self.ca_privatekey_path, self.ca_privatekey_passphrase, backend=self.backend
1325            )
1326        except crypto_utils.OpenSSLBadPassphraseError as exc:
1327            module.fail_json(msg=str(exc))
1328
1329        if crypto_utils.cryptography_key_needs_digest_for_signing(self.ca_private_key):
1330            if self.digest is None:
1331                raise CertificateError(
1332                    'The digest %s is not supported with the cryptography backend' % module.params['ownca_digest']
1333                )
1334        else:
1335            self.digest = None
1336
1337    def generate(self, module):
1338
1339        if not os.path.exists(self.ca_cert_path):
1340            raise CertificateError(
1341                'The CA certificate %s does not exist' % self.ca_cert_path
1342            )
1343
1344        if not os.path.exists(self.ca_privatekey_path):
1345            raise CertificateError(
1346                'The CA private key %s does not exist' % self.ca_privatekey_path
1347            )
1348
1349        if not os.path.exists(self.csr_path):
1350            raise CertificateError(
1351                'The certificate signing request file %s does not exist' % self.csr_path
1352            )
1353
1354        if not self.check(module, perms_required=False) or self.force:
1355            cert_builder = x509.CertificateBuilder()
1356            cert_builder = cert_builder.subject_name(self.csr.subject)
1357            cert_builder = cert_builder.issuer_name(self.ca_cert.subject)
1358            cert_builder = cert_builder.serial_number(self.serial_number)
1359            cert_builder = cert_builder.not_valid_before(self.notBefore)
1360            cert_builder = cert_builder.not_valid_after(self.notAfter)
1361            cert_builder = cert_builder.public_key(self.csr.public_key())
1362            has_ski = False
1363            for extension in self.csr.extensions:
1364                if isinstance(extension.value, x509.SubjectKeyIdentifier):
1365                    if self.create_subject_key_identifier == 'always_create':
1366                        continue
1367                    has_ski = True
1368                if self.create_authority_key_identifier and isinstance(extension.value, x509.AuthorityKeyIdentifier):
1369                    continue
1370                cert_builder = cert_builder.add_extension(extension.value, critical=extension.critical)
1371            if not has_ski and self.create_subject_key_identifier != 'never_create':
1372                cert_builder = cert_builder.add_extension(
1373                    x509.SubjectKeyIdentifier.from_public_key(self.csr.public_key()),
1374                    critical=False
1375                )
1376            if self.create_authority_key_identifier:
1377                try:
1378                    ext = self.ca_cert.extensions.get_extension_for_class(x509.SubjectKeyIdentifier)
1379                    cert_builder = cert_builder.add_extension(
1380                        x509.AuthorityKeyIdentifier.from_issuer_subject_key_identifier(ext.value)
1381                        if CRYPTOGRAPHY_VERSION >= LooseVersion('2.7') else
1382                        x509.AuthorityKeyIdentifier.from_issuer_subject_key_identifier(ext),
1383                        critical=False
1384                    )
1385                except cryptography.x509.ExtensionNotFound:
1386                    cert_builder = cert_builder.add_extension(
1387                        x509.AuthorityKeyIdentifier.from_issuer_public_key(self.ca_cert.public_key()),
1388                        critical=False
1389                    )
1390
1391            try:
1392                certificate = cert_builder.sign(
1393                    private_key=self.ca_private_key, algorithm=self.digest,
1394                    backend=default_backend()
1395                )
1396            except TypeError as e:
1397                if str(e) == 'Algorithm must be a registered hash algorithm.' and self.digest is None:
1398                    module.fail_json(msg='Signing with Ed25519 and Ed448 keys requires cryptography 2.8 or newer.')
1399                raise
1400
1401            self.cert = certificate
1402
1403            if self.backup:
1404                self.backup_file = module.backup_local(self.path)
1405            crypto_utils.write_file(module, certificate.public_bytes(Encoding.PEM))
1406            self.changed = True
1407        else:
1408            self.cert = crypto_utils.load_certificate(self.path, backend=self.backend)
1409
1410        file_args = module.load_file_common_arguments(module.params)
1411        if module.set_fs_attributes_if_different(file_args, False):
1412            self.changed = True
1413
1414    def check(self, module, perms_required=True):
1415        """Ensure the resource is in its desired state."""
1416
1417        if not super(OwnCACertificateCryptography, self).check(module, perms_required):
1418            return False
1419
1420        # Check AuthorityKeyIdentifier
1421        if self.create_authority_key_identifier:
1422            try:
1423                ext = self.ca_cert.extensions.get_extension_for_class(x509.SubjectKeyIdentifier)
1424                expected_ext = (
1425                    x509.AuthorityKeyIdentifier.from_issuer_subject_key_identifier(ext.value)
1426                    if CRYPTOGRAPHY_VERSION >= LooseVersion('2.7') else
1427                    x509.AuthorityKeyIdentifier.from_issuer_subject_key_identifier(ext)
1428                )
1429            except cryptography.x509.ExtensionNotFound:
1430                expected_ext = x509.AuthorityKeyIdentifier.from_issuer_public_key(self.ca_cert.public_key())
1431            try:
1432                ext = self.cert.extensions.get_extension_for_class(x509.AuthorityKeyIdentifier)
1433                if ext.value != expected_ext:
1434                    return False
1435            except cryptography.x509.ExtensionNotFound as dummy:
1436                return False
1437
1438        return True
1439
1440    def dump(self, check_mode=False):
1441
1442        result = {
1443            'changed': self.changed,
1444            'filename': self.path,
1445            'privatekey': self.privatekey_path,
1446            'csr': self.csr_path,
1447            'ca_cert': self.ca_cert_path,
1448            'ca_privatekey': self.ca_privatekey_path
1449        }
1450        if self.backup_file:
1451            result['backup_file'] = self.backup_file
1452
1453        if check_mode:
1454            result.update({
1455                'notBefore': self.notBefore.strftime("%Y%m%d%H%M%SZ"),
1456                'notAfter': self.notAfter.strftime("%Y%m%d%H%M%SZ"),
1457                'serial_number': self.serial_number,
1458            })
1459        else:
1460            result.update({
1461                'notBefore': self.cert.not_valid_before.strftime("%Y%m%d%H%M%SZ"),
1462                'notAfter': self.cert.not_valid_after.strftime("%Y%m%d%H%M%SZ"),
1463                'serial_number': self.cert.serial_number,
1464            })
1465
1466        return result
1467
1468
1469class OwnCACertificate(Certificate):
1470    """Generate the own CA certificate."""
1471
1472    def __init__(self, module):
1473        super(OwnCACertificate, self).__init__(module, 'pyopenssl')
1474        self.notBefore = self.get_relative_time_option(module.params['ownca_not_before'], 'ownca_not_before')
1475        self.notAfter = self.get_relative_time_option(module.params['ownca_not_after'], 'ownca_not_after')
1476        self.digest = module.params['ownca_digest']
1477        self.version = module.params['ownca_version']
1478        self.serial_number = randint(1000, 99999)
1479        if module.params['ownca_create_subject_key_identifier'] != 'create_if_not_provided':
1480            module.fail_json(msg='ownca_create_subject_key_identifier cannot be used with the pyOpenSSL backend!')
1481        if module.params['ownca_create_authority_key_identifier']:
1482            module.warn('ownca_create_authority_key_identifier is ignored by the pyOpenSSL backend!')
1483        self.ca_cert_path = module.params['ownca_path']
1484        self.ca_privatekey_path = module.params['ownca_privatekey_path']
1485        self.ca_privatekey_passphrase = module.params['ownca_privatekey_passphrase']
1486
1487        if not os.path.exists(self.csr_path):
1488            raise CertificateError(
1489                'The certificate signing request file {0} does not exist'.format(self.csr_path)
1490            )
1491        if not os.path.exists(self.ca_cert_path):
1492            raise CertificateError(
1493                'The CA certificate file {0} does not exist'.format(self.ca_cert_path)
1494            )
1495        if not os.path.exists(self.ca_privatekey_path):
1496            raise CertificateError(
1497                'The CA private key file {0} does not exist'.format(self.ca_privatekey_path)
1498            )
1499
1500        self.csr = crypto_utils.load_certificate_request(self.csr_path)
1501        self.ca_cert = crypto_utils.load_certificate(self.ca_cert_path)
1502        try:
1503            self.ca_privatekey = crypto_utils.load_privatekey(
1504                self.ca_privatekey_path, self.ca_privatekey_passphrase
1505            )
1506        except crypto_utils.OpenSSLBadPassphraseError as exc:
1507            module.fail_json(msg=str(exc))
1508
1509    def generate(self, module):
1510
1511        if not os.path.exists(self.ca_cert_path):
1512            raise CertificateError(
1513                'The CA certificate %s does not exist' % self.ca_cert_path
1514            )
1515
1516        if not os.path.exists(self.ca_privatekey_path):
1517            raise CertificateError(
1518                'The CA private key %s does not exist' % self.ca_privatekey_path
1519            )
1520
1521        if not os.path.exists(self.csr_path):
1522            raise CertificateError(
1523                'The certificate signing request file %s does not exist' % self.csr_path
1524            )
1525
1526        if not self.check(module, perms_required=False) or self.force:
1527            cert = crypto.X509()
1528            cert.set_serial_number(self.serial_number)
1529            cert.set_notBefore(to_bytes(self.notBefore))
1530            cert.set_notAfter(to_bytes(self.notAfter))
1531            cert.set_subject(self.csr.get_subject())
1532            cert.set_issuer(self.ca_cert.get_subject())
1533            cert.set_version(self.version - 1)
1534            cert.set_pubkey(self.csr.get_pubkey())
1535            cert.add_extensions(self.csr.get_extensions())
1536
1537            cert.sign(self.ca_privatekey, self.digest)
1538            self.cert = cert
1539
1540            if self.backup:
1541                self.backup_file = module.backup_local(self.path)
1542            crypto_utils.write_file(module, crypto.dump_certificate(crypto.FILETYPE_PEM, self.cert))
1543            self.changed = True
1544
1545        file_args = module.load_file_common_arguments(module.params)
1546        if module.set_fs_attributes_if_different(file_args, False):
1547            self.changed = True
1548
1549    def dump(self, check_mode=False):
1550
1551        result = {
1552            'changed': self.changed,
1553            'filename': self.path,
1554            'privatekey': self.privatekey_path,
1555            'csr': self.csr_path,
1556            'ca_cert': self.ca_cert_path,
1557            'ca_privatekey': self.ca_privatekey_path
1558        }
1559        if self.backup_file:
1560            result['backup_file'] = self.backup_file
1561
1562        if check_mode:
1563            result.update({
1564                'notBefore': self.notBefore,
1565                'notAfter': self.notAfter,
1566                'serial_number': self.serial_number,
1567            })
1568        else:
1569            result.update({
1570                'notBefore': self.cert.get_notBefore(),
1571                'notAfter': self.cert.get_notAfter(),
1572                'serial_number': self.cert.get_serial_number(),
1573            })
1574
1575        return result
1576
1577
1578def compare_sets(subset, superset, equality=False):
1579    if equality:
1580        return set(subset) == set(superset)
1581    else:
1582        return all(x in superset for x in subset)
1583
1584
1585def compare_dicts(subset, superset, equality=False):
1586    if equality:
1587        return subset == superset
1588    else:
1589        return all(superset.get(x) == v for x, v in subset.items())
1590
1591
1592NO_EXTENSION = 'no extension'
1593
1594
1595class AssertOnlyCertificateBase(Certificate):
1596
1597    def __init__(self, module, backend):
1598        super(AssertOnlyCertificateBase, self).__init__(module, backend)
1599
1600        self.signature_algorithms = module.params['signature_algorithms']
1601        if module.params['subject']:
1602            self.subject = crypto_utils.parse_name_field(module.params['subject'])
1603        else:
1604            self.subject = []
1605        self.subject_strict = module.params['subject_strict']
1606        if module.params['issuer']:
1607            self.issuer = crypto_utils.parse_name_field(module.params['issuer'])
1608        else:
1609            self.issuer = []
1610        self.issuer_strict = module.params['issuer_strict']
1611        self.has_expired = module.params['has_expired']
1612        self.version = module.params['version']
1613        self.key_usage = module.params['key_usage']
1614        self.key_usage_strict = module.params['key_usage_strict']
1615        self.extended_key_usage = module.params['extended_key_usage']
1616        self.extended_key_usage_strict = module.params['extended_key_usage_strict']
1617        self.subject_alt_name = module.params['subject_alt_name']
1618        self.subject_alt_name_strict = module.params['subject_alt_name_strict']
1619        self.not_before = module.params['not_before']
1620        self.not_after = module.params['not_after']
1621        self.valid_at = module.params['valid_at']
1622        self.invalid_at = module.params['invalid_at']
1623        self.valid_in = module.params['valid_in']
1624        if self.valid_in and not self.valid_in.startswith("+") and not self.valid_in.startswith("-"):
1625            try:
1626                int(self.valid_in)
1627            except ValueError:
1628                module.fail_json(msg='The supplied value for "valid_in" (%s) is not an integer or a valid timespec' % self.valid_in)
1629            self.valid_in = "+" + self.valid_in + "s"
1630
1631        # Load objects
1632        self.cert = crypto_utils.load_certificate(self.path, backend=self.backend)
1633        if self.privatekey_path is not None:
1634            try:
1635                self.privatekey = crypto_utils.load_privatekey(
1636                    self.privatekey_path,
1637                    self.privatekey_passphrase,
1638                    backend=self.backend
1639                )
1640            except crypto_utils.OpenSSLBadPassphraseError as exc:
1641                raise CertificateError(exc)
1642        if self.csr_path is not None:
1643            self.csr = crypto_utils.load_certificate_request(self.csr_path, backend=self.backend)
1644
1645    @abc.abstractmethod
1646    def _validate_privatekey(self):
1647        pass
1648
1649    @abc.abstractmethod
1650    def _validate_csr_signature(self):
1651        pass
1652
1653    @abc.abstractmethod
1654    def _validate_csr_subject(self):
1655        pass
1656
1657    @abc.abstractmethod
1658    def _validate_csr_extensions(self):
1659        pass
1660
1661    @abc.abstractmethod
1662    def _validate_signature_algorithms(self):
1663        pass
1664
1665    @abc.abstractmethod
1666    def _validate_subject(self):
1667        pass
1668
1669    @abc.abstractmethod
1670    def _validate_issuer(self):
1671        pass
1672
1673    @abc.abstractmethod
1674    def _validate_has_expired(self):
1675        pass
1676
1677    @abc.abstractmethod
1678    def _validate_version(self):
1679        pass
1680
1681    @abc.abstractmethod
1682    def _validate_key_usage(self):
1683        pass
1684
1685    @abc.abstractmethod
1686    def _validate_extended_key_usage(self):
1687        pass
1688
1689    @abc.abstractmethod
1690    def _validate_subject_alt_name(self):
1691        pass
1692
1693    @abc.abstractmethod
1694    def _validate_not_before(self):
1695        pass
1696
1697    @abc.abstractmethod
1698    def _validate_not_after(self):
1699        pass
1700
1701    @abc.abstractmethod
1702    def _validate_valid_at(self):
1703        pass
1704
1705    @abc.abstractmethod
1706    def _validate_invalid_at(self):
1707        pass
1708
1709    @abc.abstractmethod
1710    def _validate_valid_in(self):
1711        pass
1712
1713    def assertonly(self, module):
1714        messages = []
1715        if self.privatekey_path is not None:
1716            if not self._validate_privatekey():
1717                messages.append(
1718                    'Certificate %s and private key %s do not match' %
1719                    (self.path, self.privatekey_path)
1720                )
1721
1722        if self.csr_path is not None:
1723            if not self._validate_csr_signature():
1724                messages.append(
1725                    'Certificate %s and CSR %s do not match: private key mismatch' %
1726                    (self.path, self.csr_path)
1727                )
1728            if not self._validate_csr_subject():
1729                messages.append(
1730                    'Certificate %s and CSR %s do not match: subject mismatch' %
1731                    (self.path, self.csr_path)
1732                )
1733            if not self._validate_csr_extensions():
1734                messages.append(
1735                    'Certificate %s and CSR %s do not match: extensions mismatch' %
1736                    (self.path, self.csr_path)
1737                )
1738
1739        if self.signature_algorithms is not None:
1740            wrong_alg = self._validate_signature_algorithms()
1741            if wrong_alg:
1742                messages.append(
1743                    'Invalid signature algorithm (got %s, expected one of %s)' %
1744                    (wrong_alg, self.signature_algorithms)
1745                )
1746
1747        if self.subject is not None:
1748            failure = self._validate_subject()
1749            if failure:
1750                dummy, cert_subject = failure
1751                messages.append(
1752                    'Invalid subject component (got %s, expected all of %s to be present)' %
1753                    (cert_subject, self.subject)
1754                )
1755
1756        if self.issuer is not None:
1757            failure = self._validate_issuer()
1758            if failure:
1759                dummy, cert_issuer = failure
1760                messages.append(
1761                    'Invalid issuer component (got %s, expected all of %s to be present)' % (cert_issuer, self.issuer)
1762                )
1763
1764        if self.has_expired is not None:
1765            cert_expired = self._validate_has_expired()
1766            if cert_expired != self.has_expired:
1767                messages.append(
1768                    'Certificate expiration check failed (certificate expiration is %s, expected %s)' %
1769                    (cert_expired, self.has_expired)
1770                )
1771
1772        if self.version is not None:
1773            cert_version = self._validate_version()
1774            if cert_version != self.version:
1775                messages.append(
1776                    'Invalid certificate version number (got %s, expected %s)' %
1777                    (cert_version, self.version)
1778                )
1779
1780        if self.key_usage is not None:
1781            failure = self._validate_key_usage()
1782            if failure == NO_EXTENSION:
1783                messages.append('Found no keyUsage extension')
1784            elif failure:
1785                dummy, cert_key_usage = failure
1786                messages.append(
1787                    'Invalid keyUsage components (got %s, expected all of %s to be present)' %
1788                    (cert_key_usage, self.key_usage)
1789                )
1790
1791        if self.extended_key_usage is not None:
1792            failure = self._validate_extended_key_usage()
1793            if failure == NO_EXTENSION:
1794                messages.append('Found no extendedKeyUsage extension')
1795            elif failure:
1796                dummy, ext_cert_key_usage = failure
1797                messages.append(
1798                    'Invalid extendedKeyUsage component (got %s, expected all of %s to be present)' % (ext_cert_key_usage, self.extended_key_usage)
1799                )
1800
1801        if self.subject_alt_name is not None:
1802            failure = self._validate_subject_alt_name()
1803            if failure == NO_EXTENSION:
1804                messages.append('Found no subjectAltName extension')
1805            elif failure:
1806                dummy, cert_san = failure
1807                messages.append(
1808                    'Invalid subjectAltName component (got %s, expected all of %s to be present)' %
1809                    (cert_san, self.subject_alt_name)
1810                )
1811
1812        if self.not_before is not None:
1813            cert_not_valid_before = self._validate_not_before()
1814            if cert_not_valid_before != self.get_relative_time_option(self.not_before, 'not_before'):
1815                messages.append(
1816                    'Invalid not_before component (got %s, expected %s to be present)' %
1817                    (cert_not_valid_before, self.not_before)
1818                )
1819
1820        if self.not_after is not None:
1821            cert_not_valid_after = self._validate_not_after()
1822            if cert_not_valid_after != self.get_relative_time_option(self.not_after, 'not_after'):
1823                messages.append(
1824                    'Invalid not_after component (got %s, expected %s to be present)' %
1825                    (cert_not_valid_after, self.not_after)
1826                )
1827
1828        if self.valid_at is not None:
1829            not_before, valid_at, not_after = self._validate_valid_at()
1830            if not (not_before <= valid_at <= not_after):
1831                messages.append(
1832                    'Certificate is not valid for the specified date (%s) - not_before: %s - not_after: %s' %
1833                    (self.valid_at, not_before, not_after)
1834                )
1835
1836        if self.invalid_at is not None:
1837            not_before, invalid_at, not_after = self._validate_invalid_at()
1838            if not_before <= invalid_at <= not_after:
1839                messages.append(
1840                    'Certificate is not invalid for the specified date (%s) - not_before: %s - not_after: %s' %
1841                    (self.invalid_at, not_before, not_after)
1842                )
1843
1844        if self.valid_in is not None:
1845            not_before, valid_in, not_after = self._validate_valid_in()
1846            if not not_before <= valid_in <= not_after:
1847                messages.append(
1848                    'Certificate is not valid in %s from now (that would be %s) - not_before: %s - not_after: %s' %
1849                    (self.valid_in, valid_in, not_before, not_after)
1850                )
1851        return messages
1852
1853    def generate(self, module):
1854        """Don't generate anything - only assert"""
1855        messages = self.assertonly(module)
1856        if messages:
1857            module.fail_json(msg=' | '.join(messages))
1858
1859    def check(self, module, perms_required=False):
1860        """Ensure the resource is in its desired state."""
1861        messages = self.assertonly(module)
1862        return len(messages) == 0
1863
1864    def dump(self, check_mode=False):
1865        result = {
1866            'changed': self.changed,
1867            'filename': self.path,
1868            'privatekey': self.privatekey_path,
1869            'csr': self.csr_path,
1870        }
1871        return result
1872
1873
1874class AssertOnlyCertificateCryptography(AssertOnlyCertificateBase):
1875    """Validate the supplied cert, using the cryptography backend"""
1876    def __init__(self, module):
1877        super(AssertOnlyCertificateCryptography, self).__init__(module, 'cryptography')
1878
1879    def _validate_privatekey(self):
1880        return crypto_utils.cryptography_compare_public_keys(self.cert.public_key(), self.privatekey.public_key())
1881
1882    def _validate_csr_signature(self):
1883        if not self.csr.is_signature_valid:
1884            return False
1885        return crypto_utils.cryptography_compare_public_keys(self.csr.public_key(), self.cert.public_key())
1886
1887    def _validate_csr_subject(self):
1888        return self.csr.subject == self.cert.subject
1889
1890    def _validate_csr_extensions(self):
1891        cert_exts = self.cert.extensions
1892        csr_exts = self.csr.extensions
1893        if len(cert_exts) != len(csr_exts):
1894            return False
1895        for cert_ext in cert_exts:
1896            try:
1897                csr_ext = csr_exts.get_extension_for_oid(cert_ext.oid)
1898                if cert_ext != csr_ext:
1899                    return False
1900            except cryptography.x509.ExtensionNotFound as dummy:
1901                return False
1902        return True
1903
1904    def _validate_signature_algorithms(self):
1905        if self.cert.signature_algorithm_oid._name not in self.signature_algorithms:
1906            return self.cert.signature_algorithm_oid._name
1907
1908    def _validate_subject(self):
1909        expected_subject = Name([NameAttribute(oid=crypto_utils.cryptography_name_to_oid(sub[0]), value=to_text(sub[1]))
1910                                 for sub in self.subject])
1911        cert_subject = self.cert.subject
1912        if not compare_sets(expected_subject, cert_subject, self.subject_strict):
1913            return expected_subject, cert_subject
1914
1915    def _validate_issuer(self):
1916        expected_issuer = Name([NameAttribute(oid=crypto_utils.cryptography_name_to_oid(iss[0]), value=to_text(iss[1]))
1917                                for iss in self.issuer])
1918        cert_issuer = self.cert.issuer
1919        if not compare_sets(expected_issuer, cert_issuer, self.issuer_strict):
1920            return self.issuer, cert_issuer
1921
1922    def _validate_has_expired(self):
1923        cert_not_after = self.cert.not_valid_after
1924        cert_expired = cert_not_after < datetime.datetime.utcnow()
1925        return cert_expired
1926
1927    def _validate_version(self):
1928        if self.cert.version == x509.Version.v1:
1929            return 1
1930        if self.cert.version == x509.Version.v3:
1931            return 3
1932        return "unknown"
1933
1934    def _validate_key_usage(self):
1935        try:
1936            current_key_usage = self.cert.extensions.get_extension_for_class(x509.KeyUsage).value
1937            test_key_usage = dict(
1938                digital_signature=current_key_usage.digital_signature,
1939                content_commitment=current_key_usage.content_commitment,
1940                key_encipherment=current_key_usage.key_encipherment,
1941                data_encipherment=current_key_usage.data_encipherment,
1942                key_agreement=current_key_usage.key_agreement,
1943                key_cert_sign=current_key_usage.key_cert_sign,
1944                crl_sign=current_key_usage.crl_sign,
1945                encipher_only=False,
1946                decipher_only=False
1947            )
1948            if test_key_usage['key_agreement']:
1949                test_key_usage.update(dict(
1950                    encipher_only=current_key_usage.encipher_only,
1951                    decipher_only=current_key_usage.decipher_only
1952                ))
1953
1954            key_usages = crypto_utils.cryptography_parse_key_usage_params(self.key_usage)
1955            if not compare_dicts(key_usages, test_key_usage, self.key_usage_strict):
1956                return self.key_usage, [k for k, v in test_key_usage.items() if v is True]
1957
1958        except cryptography.x509.ExtensionNotFound:
1959            # This is only bad if the user specified a non-empty list
1960            if self.key_usage:
1961                return NO_EXTENSION
1962
1963    def _validate_extended_key_usage(self):
1964        try:
1965            current_ext_keyusage = self.cert.extensions.get_extension_for_class(x509.ExtendedKeyUsage).value
1966            usages = [crypto_utils.cryptography_name_to_oid(usage) for usage in self.extended_key_usage]
1967            expected_ext_keyusage = x509.ExtendedKeyUsage(usages)
1968            if not compare_sets(expected_ext_keyusage, current_ext_keyusage, self.extended_key_usage_strict):
1969                return [eku.value for eku in expected_ext_keyusage], [eku.value for eku in current_ext_keyusage]
1970
1971        except cryptography.x509.ExtensionNotFound:
1972            # This is only bad if the user specified a non-empty list
1973            if self.extended_key_usage:
1974                return NO_EXTENSION
1975
1976    def _validate_subject_alt_name(self):
1977        try:
1978            current_san = self.cert.extensions.get_extension_for_class(x509.SubjectAlternativeName).value
1979            expected_san = [crypto_utils.cryptography_get_name(san) for san in self.subject_alt_name]
1980            if not compare_sets(expected_san, current_san, self.subject_alt_name_strict):
1981                return self.subject_alt_name, current_san
1982        except cryptography.x509.ExtensionNotFound:
1983            # This is only bad if the user specified a non-empty list
1984            if self.subject_alt_name:
1985                return NO_EXTENSION
1986
1987    def _validate_not_before(self):
1988        return self.cert.not_valid_before
1989
1990    def _validate_not_after(self):
1991        return self.cert.not_valid_after
1992
1993    def _validate_valid_at(self):
1994        rt = self.get_relative_time_option(self.valid_at, 'valid_at')
1995        return self.cert.not_valid_before, rt, self.cert.not_valid_after
1996
1997    def _validate_invalid_at(self):
1998        rt = self.get_relative_time_option(self.invalid_at, 'invalid_at')
1999        return self.cert.not_valid_before, rt, self.cert.not_valid_after
2000
2001    def _validate_valid_in(self):
2002        valid_in_date = self.get_relative_time_option(self.valid_in, "valid_in")
2003        return self.cert.not_valid_before, valid_in_date, self.cert.not_valid_after
2004
2005
2006class AssertOnlyCertificate(AssertOnlyCertificateBase):
2007    """validate the supplied certificate."""
2008
2009    def __init__(self, module):
2010        super(AssertOnlyCertificate, self).__init__(module, 'pyopenssl')
2011
2012        # Ensure inputs are properly sanitized before comparison.
2013        for param in ['signature_algorithms', 'key_usage', 'extended_key_usage',
2014                      'subject_alt_name', 'subject', 'issuer', 'not_before',
2015                      'not_after', 'valid_at', 'invalid_at']:
2016            attr = getattr(self, param)
2017            if isinstance(attr, list) and attr:
2018                if isinstance(attr[0], str):
2019                    setattr(self, param, [to_bytes(item) for item in attr])
2020                elif isinstance(attr[0], tuple):
2021                    setattr(self, param, [(to_bytes(item[0]), to_bytes(item[1])) for item in attr])
2022            elif isinstance(attr, tuple):
2023                setattr(self, param, dict((to_bytes(k), to_bytes(v)) for (k, v) in attr.items()))
2024            elif isinstance(attr, dict):
2025                setattr(self, param, dict((to_bytes(k), to_bytes(v)) for (k, v) in attr.items()))
2026            elif isinstance(attr, str):
2027                setattr(self, param, to_bytes(attr))
2028
2029    def _validate_privatekey(self):
2030        ctx = OpenSSL.SSL.Context(OpenSSL.SSL.TLSv1_2_METHOD)
2031        ctx.use_privatekey(self.privatekey)
2032        ctx.use_certificate(self.cert)
2033        try:
2034            ctx.check_privatekey()
2035            return True
2036        except OpenSSL.SSL.Error:
2037            return False
2038
2039    def _validate_csr_signature(self):
2040        try:
2041            self.csr.verify(self.cert.get_pubkey())
2042        except OpenSSL.crypto.Error:
2043            return False
2044
2045    def _validate_csr_subject(self):
2046        if self.csr.get_subject() != self.cert.get_subject():
2047            return False
2048
2049    def _validate_csr_extensions(self):
2050        csr_extensions = self.csr.get_extensions()
2051        cert_extension_count = self.cert.get_extension_count()
2052        if len(csr_extensions) != cert_extension_count:
2053            return False
2054        for extension_number in range(0, cert_extension_count):
2055            cert_extension = self.cert.get_extension(extension_number)
2056            csr_extension = filter(lambda extension: extension.get_short_name() == cert_extension.get_short_name(), csr_extensions)
2057            if cert_extension.get_data() != list(csr_extension)[0].get_data():
2058                return False
2059        return True
2060
2061    def _validate_signature_algorithms(self):
2062        if self.cert.get_signature_algorithm() not in self.signature_algorithms:
2063            return self.cert.get_signature_algorithm()
2064
2065    def _validate_subject(self):
2066        expected_subject = [(OpenSSL._util.lib.OBJ_txt2nid(sub[0]), sub[1]) for sub in self.subject]
2067        cert_subject = self.cert.get_subject().get_components()
2068        current_subject = [(OpenSSL._util.lib.OBJ_txt2nid(sub[0]), sub[1]) for sub in cert_subject]
2069        if not compare_sets(expected_subject, current_subject, self.subject_strict):
2070            return expected_subject, current_subject
2071
2072    def _validate_issuer(self):
2073        expected_issuer = [(OpenSSL._util.lib.OBJ_txt2nid(iss[0]), iss[1]) for iss in self.issuer]
2074        cert_issuer = self.cert.get_issuer().get_components()
2075        current_issuer = [(OpenSSL._util.lib.OBJ_txt2nid(iss[0]), iss[1]) for iss in cert_issuer]
2076        if not compare_sets(expected_issuer, current_issuer, self.issuer_strict):
2077            return self.issuer, cert_issuer
2078
2079    def _validate_has_expired(self):
2080        # The following 3 lines are the same as the current PyOpenSSL code for cert.has_expired().
2081        # Older version of PyOpenSSL have a buggy implementation,
2082        # to avoid issues with those we added the code from a more recent release here.
2083
2084        time_string = to_native(self.cert.get_notAfter())
2085        not_after = datetime.datetime.strptime(time_string, "%Y%m%d%H%M%SZ")
2086        cert_expired = not_after < datetime.datetime.utcnow()
2087        return cert_expired
2088
2089    def _validate_version(self):
2090        # Version numbers in certs are off by one:
2091        # v1: 0, v2: 1, v3: 2 ...
2092        return self.cert.get_version() + 1
2093
2094    def _validate_key_usage(self):
2095        found = False
2096        for extension_idx in range(0, self.cert.get_extension_count()):
2097            extension = self.cert.get_extension(extension_idx)
2098            if extension.get_short_name() == b'keyUsage':
2099                found = True
2100                expected_extension = crypto.X509Extension(b"keyUsage", False, b', '.join(self.key_usage))
2101                key_usage = [usage.strip() for usage in to_text(expected_extension, errors='surrogate_or_strict').split(',')]
2102                current_ku = [usage.strip() for usage in to_text(extension, errors='surrogate_or_strict').split(',')]
2103                if not compare_sets(key_usage, current_ku, self.key_usage_strict):
2104                    return self.key_usage, str(extension).split(', ')
2105        if not found:
2106            # This is only bad if the user specified a non-empty list
2107            if self.key_usage:
2108                return NO_EXTENSION
2109
2110    def _validate_extended_key_usage(self):
2111        found = False
2112        for extension_idx in range(0, self.cert.get_extension_count()):
2113            extension = self.cert.get_extension(extension_idx)
2114            if extension.get_short_name() == b'extendedKeyUsage':
2115                found = True
2116                extKeyUsage = [OpenSSL._util.lib.OBJ_txt2nid(keyUsage) for keyUsage in self.extended_key_usage]
2117                current_xku = [OpenSSL._util.lib.OBJ_txt2nid(usage.strip()) for usage in
2118                               to_bytes(extension, errors='surrogate_or_strict').split(b',')]
2119                if not compare_sets(extKeyUsage, current_xku, self.extended_key_usage_strict):
2120                    return self.extended_key_usage, str(extension).split(', ')
2121        if not found:
2122            # This is only bad if the user specified a non-empty list
2123            if self.extended_key_usage:
2124                return NO_EXTENSION
2125
2126    def _normalize_san(self, san):
2127        # Apparently OpenSSL returns 'IP address' not 'IP' as specifier when converting the subjectAltName to string
2128        # although it won't accept this specifier when generating the CSR. (https://github.com/openssl/openssl/issues/4004)
2129        if san.startswith('IP Address:'):
2130            san = 'IP:' + san[len('IP Address:'):]
2131        if san.startswith('IP:'):
2132            ip = compat_ipaddress.ip_address(san[3:])
2133            san = 'IP:{0}'.format(ip.compressed)
2134        return san
2135
2136    def _validate_subject_alt_name(self):
2137        found = False
2138        for extension_idx in range(0, self.cert.get_extension_count()):
2139            extension = self.cert.get_extension(extension_idx)
2140            if extension.get_short_name() == b'subjectAltName':
2141                found = True
2142                l_altnames = [self._normalize_san(altname.strip()) for altname in
2143                              to_text(extension, errors='surrogate_or_strict').split(', ')]
2144                sans = [self._normalize_san(to_text(san, errors='surrogate_or_strict')) for san in self.subject_alt_name]
2145                if not compare_sets(sans, l_altnames, self.subject_alt_name_strict):
2146                    return self.subject_alt_name, l_altnames
2147        if not found:
2148            # This is only bad if the user specified a non-empty list
2149            if self.subject_alt_name:
2150                return NO_EXTENSION
2151
2152    def _validate_not_before(self):
2153        return self.cert.get_notBefore()
2154
2155    def _validate_not_after(self):
2156        return self.cert.get_notAfter()
2157
2158    def _validate_valid_at(self):
2159        rt = self.get_relative_time_option(self.valid_at, "valid_at")
2160        rt = to_bytes(rt, errors='surrogate_or_strict')
2161        return self.cert.get_notBefore(), rt, self.cert.get_notAfter()
2162
2163    def _validate_invalid_at(self):
2164        rt = self.get_relative_time_option(self.invalid_at, "invalid_at")
2165        rt = to_bytes(rt, errors='surrogate_or_strict')
2166        return self.cert.get_notBefore(), rt, self.cert.get_notAfter()
2167
2168    def _validate_valid_in(self):
2169        valid_in_asn1 = self.get_relative_time_option(self.valid_in, "valid_in")
2170        valid_in_date = to_bytes(valid_in_asn1, errors='surrogate_or_strict')
2171        return self.cert.get_notBefore(), valid_in_date, self.cert.get_notAfter()
2172
2173
2174class EntrustCertificate(Certificate):
2175    """Retrieve a certificate using Entrust (ECS)."""
2176
2177    def __init__(self, module, backend):
2178        super(EntrustCertificate, self).__init__(module, backend)
2179        self.trackingId = None
2180        self.notAfter = self.get_relative_time_option(module.params['entrust_not_after'], 'entrust_not_after')
2181
2182        if not os.path.exists(self.csr_path):
2183            raise CertificateError(
2184                'The certificate signing request file {0} does not exist'.format(self.csr_path)
2185            )
2186
2187        self.csr = crypto_utils.load_certificate_request(self.csr_path, backend=self.backend)
2188
2189        # ECS API defaults to using the validated organization tied to the account.
2190        # We want to always force behavior of trying to use the organization provided in the CSR.
2191        # To that end we need to parse out the organization from the CSR.
2192        self.csr_org = None
2193        if self.backend == 'pyopenssl':
2194            csr_subject = self.csr.get_subject()
2195            csr_subject_components = csr_subject.get_components()
2196            for k, v in csr_subject_components:
2197                if k.upper() == 'O':
2198                    # Entrust does not support multiple validated organizations in a single certificate
2199                    if self.csr_org is not None:
2200                        module.fail_json(msg=("Entrust provider does not currently support multiple validated organizations. Multiple organizations found in "
2201                                              "Subject DN: '{0}'. ".format(csr_subject)))
2202                    else:
2203                        self.csr_org = v
2204        elif self.backend == 'cryptography':
2205            csr_subject_orgs = self.csr.subject.get_attributes_for_oid(NameOID.ORGANIZATION_NAME)
2206            if len(csr_subject_orgs) == 1:
2207                self.csr_org = csr_subject_orgs[0].value
2208            elif len(csr_subject_orgs) > 1:
2209                module.fail_json(msg=("Entrust provider does not currently support multiple validated organizations. Multiple organizations found in "
2210                                      "Subject DN: '{0}'. ".format(self.csr.subject)))
2211        # If no organization in the CSR, explicitly tell ECS that it should be blank in issued cert, not defaulted to
2212        # organization tied to the account.
2213        if self.csr_org is None:
2214            self.csr_org = ''
2215
2216        try:
2217            self.ecs_client = ECSClient(
2218                entrust_api_user=module.params.get('entrust_api_user'),
2219                entrust_api_key=module.params.get('entrust_api_key'),
2220                entrust_api_cert=module.params.get('entrust_api_client_cert_path'),
2221                entrust_api_cert_key=module.params.get('entrust_api_client_cert_key_path'),
2222                entrust_api_specification_path=module.params.get('entrust_api_specification_path')
2223            )
2224        except SessionConfigurationException as e:
2225            module.fail_json(msg='Failed to initialize Entrust Provider: {0}'.format(to_native(e.message)))
2226
2227    def generate(self, module):
2228
2229        if not self.check(module, perms_required=False) or self.force:
2230            # Read the CSR that was generated for us
2231            body = {}
2232            with open(self.csr_path, 'r') as csr_file:
2233                body['csr'] = csr_file.read()
2234
2235            body['certType'] = module.params['entrust_cert_type']
2236
2237            # Handle expiration (30 days if not specified)
2238            expiry = self.notAfter
2239            if not expiry:
2240                gmt_now = datetime.datetime.fromtimestamp(time.mktime(time.gmtime()))
2241                expiry = gmt_now + datetime.timedelta(days=365)
2242
2243            expiry_iso3339 = expiry.strftime("%Y-%m-%dT%H:%M:%S.00Z")
2244            body['certExpiryDate'] = expiry_iso3339
2245            body['org'] = self.csr_org
2246            body['tracking'] = {
2247                'requesterName': module.params['entrust_requester_name'],
2248                'requesterEmail': module.params['entrust_requester_email'],
2249                'requesterPhone': module.params['entrust_requester_phone'],
2250            }
2251
2252            try:
2253                result = self.ecs_client.NewCertRequest(Body=body)
2254                self.trackingId = result.get('trackingId')
2255            except RestOperationException as e:
2256                module.fail_json(msg='Failed to request new certificate from Entrust Certificate Services (ECS): {0}'.format(to_native(e.message)))
2257
2258            if self.backup:
2259                self.backup_file = module.backup_local(self.path)
2260            crypto_utils.write_file(module, to_bytes(result.get('endEntityCert')))
2261            self.cert = crypto_utils.load_certificate(self.path, backend=self.backend)
2262            self.changed = True
2263
2264    def check(self, module, perms_required=True):
2265        """Ensure the resource is in its desired state."""
2266
2267        parent_check = super(EntrustCertificate, self).check(module, perms_required)
2268
2269        try:
2270            cert_details = self._get_cert_details()
2271        except RestOperationException as e:
2272            module.fail_json(msg='Failed to get status of existing certificate from Entrust Certificate Services (ECS): {0}.'.format(to_native(e.message)))
2273
2274        # Always issue a new certificate if the certificate is expired, suspended or revoked
2275        status = cert_details.get('status', False)
2276        if status == 'EXPIRED' or status == 'SUSPENDED' or status == 'REVOKED':
2277            return False
2278
2279        # If the requested cert type was specified and it is for a different certificate type than the initial certificate, a new one is needed
2280        if module.params['entrust_cert_type'] and cert_details.get('certType') and module.params['entrust_cert_type'] != cert_details.get('certType'):
2281            return False
2282
2283        return parent_check
2284
2285    def _get_cert_details(self):
2286        cert_details = {}
2287        if self.cert:
2288            serial_number = None
2289            expiry = None
2290            if self.backend == 'pyopenssl':
2291                serial_number = "{0:X}".format(self.cert.get_serial_number())
2292                time_string = to_native(self.cert.get_notAfter())
2293                expiry = datetime.datetime.strptime(time_string, "%Y%m%d%H%M%SZ")
2294            elif self.backend == 'cryptography':
2295                serial_number = "{0:X}".format(self.cert.serial_number)
2296                expiry = self.cert.not_valid_after
2297
2298            # get some information about the expiry of this certificate
2299            expiry_iso3339 = expiry.strftime("%Y-%m-%dT%H:%M:%S.00Z")
2300            cert_details['expiresAfter'] = expiry_iso3339
2301
2302            # If a trackingId is not already defined (from the result of a generate)
2303            # use the serial number to identify the tracking Id
2304            if self.trackingId is None and serial_number is not None:
2305                cert_results = self.ecs_client.GetCertificates(serialNumber=serial_number).get('certificates', {})
2306
2307                # Finding 0 or more than 1 result is a very unlikely use case, it simply means we cannot perform additional checks
2308                # on the 'state' as returned by Entrust Certificate Services (ECS). The general certificate validity is
2309                # still checked as it is in the rest of the module.
2310                if len(cert_results) == 1:
2311                    self.trackingId = cert_results[0].get('trackingId')
2312
2313        if self.trackingId is not None:
2314            cert_details.update(self.ecs_client.GetCertificate(trackingId=self.trackingId))
2315
2316        return cert_details
2317
2318    def dump(self, check_mode=False):
2319
2320        result = {
2321            'changed': self.changed,
2322            'filename': self.path,
2323            'privatekey': self.privatekey_path,
2324            'csr': self.csr_path,
2325        }
2326
2327        if self.backup_file:
2328            result['backup_file'] = self.backup_file
2329
2330        result.update(self._get_cert_details())
2331
2332        return result
2333
2334
2335class AcmeCertificate(Certificate):
2336    """Retrieve a certificate using the ACME protocol."""
2337
2338    # Since there's no real use of the backend,
2339    # other than the 'self.check' function, we just pass the backend to the constructor
2340
2341    def __init__(self, module, backend):
2342        super(AcmeCertificate, self).__init__(module, backend)
2343        self.accountkey_path = module.params['acme_accountkey_path']
2344        self.challenge_path = module.params['acme_challenge_path']
2345        self.use_chain = module.params['acme_chain']
2346
2347    def generate(self, module):
2348
2349        if not os.path.exists(self.privatekey_path):
2350            raise CertificateError(
2351                'The private key %s does not exist' % self.privatekey_path
2352            )
2353
2354        if not os.path.exists(self.csr_path):
2355            raise CertificateError(
2356                'The certificate signing request file %s does not exist' % self.csr_path
2357            )
2358
2359        if not os.path.exists(self.accountkey_path):
2360            raise CertificateError(
2361                'The account key %s does not exist' % self.accountkey_path
2362            )
2363
2364        if not os.path.exists(self.challenge_path):
2365            raise CertificateError(
2366                'The challenge path %s does not exist' % self.challenge_path
2367            )
2368
2369        if not self.check(module, perms_required=False) or self.force:
2370            acme_tiny_path = self.module.get_bin_path('acme-tiny', required=True)
2371            command = [acme_tiny_path]
2372            if self.use_chain:
2373                command.append('--chain')
2374            command.extend(['--account-key', self.accountkey_path])
2375            command.extend(['--csr', self.csr_path])
2376            command.extend(['--acme-dir', self.challenge_path])
2377
2378            try:
2379                crt = module.run_command(command, check_rc=True)[1]
2380                if self.backup:
2381                    self.backup_file = module.backup_local(self.path)
2382                crypto_utils.write_file(module, to_bytes(crt))
2383                self.changed = True
2384            except OSError as exc:
2385                raise CertificateError(exc)
2386
2387        file_args = module.load_file_common_arguments(module.params)
2388        if module.set_fs_attributes_if_different(file_args, False):
2389            self.changed = True
2390
2391    def dump(self, check_mode=False):
2392
2393        result = {
2394            'changed': self.changed,
2395            'filename': self.path,
2396            'privatekey': self.privatekey_path,
2397            'accountkey': self.accountkey_path,
2398            'csr': self.csr_path,
2399        }
2400        if self.backup_file:
2401            result['backup_file'] = self.backup_file
2402
2403        return result
2404
2405
2406def main():
2407    module = AnsibleModule(
2408        argument_spec=dict(
2409            state=dict(type='str', default='present', choices=['present', 'absent']),
2410            path=dict(type='path', required=True),
2411            provider=dict(type='str', choices=['acme', 'assertonly', 'entrust', 'ownca', 'selfsigned']),
2412            force=dict(type='bool', default=False,),
2413            csr_path=dict(type='path'),
2414            backup=dict(type='bool', default=False),
2415            select_crypto_backend=dict(type='str', default='auto', choices=['auto', 'cryptography', 'pyopenssl']),
2416
2417            # General properties of a certificate
2418            privatekey_path=dict(type='path'),
2419            privatekey_passphrase=dict(type='str', no_log=True),
2420
2421            # provider: assertonly
2422            signature_algorithms=dict(type='list', elements='str', removed_in_version='2.13'),
2423            subject=dict(type='dict', removed_in_version='2.13'),
2424            subject_strict=dict(type='bool', default=False, removed_in_version='2.13'),
2425            issuer=dict(type='dict', removed_in_version='2.13'),
2426            issuer_strict=dict(type='bool', default=False, removed_in_version='2.13'),
2427            has_expired=dict(type='bool', default=False, removed_in_version='2.13'),
2428            version=dict(type='int', removed_in_version='2.13'),
2429            key_usage=dict(type='list', elements='str', aliases=['keyUsage'], removed_in_version='2.13'),
2430            key_usage_strict=dict(type='bool', default=False, aliases=['keyUsage_strict'], removed_in_version='2.13'),
2431            extended_key_usage=dict(type='list', elements='str', aliases=['extendedKeyUsage'], removed_in_version='2.13'),
2432            extended_key_usage_strict=dict(type='bool', default=False, aliases=['extendedKeyUsage_strict'], removed_in_version='2.13'),
2433            subject_alt_name=dict(type='list', elements='str', aliases=['subjectAltName'], removed_in_version='2.13'),
2434            subject_alt_name_strict=dict(type='bool', default=False, aliases=['subjectAltName_strict'], removed_in_version='2.13'),
2435            not_before=dict(type='str', aliases=['notBefore'], removed_in_version='2.13'),
2436            not_after=dict(type='str', aliases=['notAfter'], removed_in_version='2.13'),
2437            valid_at=dict(type='str', removed_in_version='2.13'),
2438            invalid_at=dict(type='str', removed_in_version='2.13'),
2439            valid_in=dict(type='str', removed_in_version='2.13'),
2440
2441            # provider: selfsigned
2442            selfsigned_version=dict(type='int', default=3),
2443            selfsigned_digest=dict(type='str', default='sha256'),
2444            selfsigned_not_before=dict(type='str', default='+0s', aliases=['selfsigned_notBefore']),
2445            selfsigned_not_after=dict(type='str', default='+3650d', aliases=['selfsigned_notAfter']),
2446            selfsigned_create_subject_key_identifier=dict(
2447                type='str',
2448                default='create_if_not_provided',
2449                choices=['create_if_not_provided', 'always_create', 'never_create']
2450            ),
2451
2452            # provider: ownca
2453            ownca_path=dict(type='path'),
2454            ownca_privatekey_path=dict(type='path'),
2455            ownca_privatekey_passphrase=dict(type='str', no_log=True),
2456            ownca_digest=dict(type='str', default='sha256'),
2457            ownca_version=dict(type='int', default=3),
2458            ownca_not_before=dict(type='str', default='+0s'),
2459            ownca_not_after=dict(type='str', default='+3650d'),
2460            ownca_create_subject_key_identifier=dict(
2461                type='str',
2462                default='create_if_not_provided',
2463                choices=['create_if_not_provided', 'always_create', 'never_create']
2464            ),
2465            ownca_create_authority_key_identifier=dict(type='bool', default=True),
2466
2467            # provider: acme
2468            acme_accountkey_path=dict(type='path'),
2469            acme_challenge_path=dict(type='path'),
2470            acme_chain=dict(type='bool', default=False),
2471
2472            # provider: entrust
2473            entrust_cert_type=dict(type='str', default='STANDARD_SSL',
2474                                   choices=['STANDARD_SSL', 'ADVANTAGE_SSL', 'UC_SSL', 'EV_SSL', 'WILDCARD_SSL',
2475                                            'PRIVATE_SSL', 'PD_SSL', 'CDS_ENT_LITE', 'CDS_ENT_PRO', 'SMIME_ENT']),
2476            entrust_requester_email=dict(type='str'),
2477            entrust_requester_name=dict(type='str'),
2478            entrust_requester_phone=dict(type='str'),
2479            entrust_api_user=dict(type='str'),
2480            entrust_api_key=dict(type='str', no_log=True),
2481            entrust_api_client_cert_path=dict(type='path'),
2482            entrust_api_client_cert_key_path=dict(type='path', no_log=True),
2483            entrust_api_specification_path=dict(type='path', default='https://cloud.entrust.net/EntrustCloud/documentation/cms-api-2.1.0.yaml'),
2484            entrust_not_after=dict(type='str', default='+365d'),
2485        ),
2486        supports_check_mode=True,
2487        add_file_common_args=True,
2488        required_if=[
2489            ['state', 'present', ['provider']],
2490            ['provider', 'entrust', ['entrust_requester_email', 'entrust_requester_name', 'entrust_requester_phone',
2491                                     'entrust_api_user', 'entrust_api_key', 'entrust_api_client_cert_path',
2492                                     'entrust_api_client_cert_key_path']],
2493        ]
2494    )
2495
2496    try:
2497        if module.params['state'] == 'absent':
2498            certificate = CertificateAbsent(module)
2499
2500        else:
2501            if module.params['provider'] != 'assertonly' and module.params['csr_path'] is None:
2502                module.fail_json(msg='csr_path is required when provider is not assertonly')
2503
2504            base_dir = os.path.dirname(module.params['path']) or '.'
2505            if not os.path.isdir(base_dir):
2506                module.fail_json(
2507                    name=base_dir,
2508                    msg='The directory %s does not exist or the file is not a directory' % base_dir
2509                )
2510
2511            provider = module.params['provider']
2512            if provider == 'assertonly':
2513                module.deprecate("The 'assertonly' provider is deprecated; please see the examples of "
2514                                 "the 'openssl_certificate' module on how to replace it with other modules",
2515                                 version='2.13')
2516
2517            backend = module.params['select_crypto_backend']
2518            if backend == 'auto':
2519                # Detect what backend we can use
2520                can_use_cryptography = CRYPTOGRAPHY_FOUND and CRYPTOGRAPHY_VERSION >= LooseVersion(MINIMAL_CRYPTOGRAPHY_VERSION)
2521                can_use_pyopenssl = PYOPENSSL_FOUND and PYOPENSSL_VERSION >= LooseVersion(MINIMAL_PYOPENSSL_VERSION)
2522
2523                # If cryptography is available we'll use it
2524                if can_use_cryptography:
2525                    backend = 'cryptography'
2526                elif can_use_pyopenssl:
2527                    backend = 'pyopenssl'
2528
2529                if module.params['selfsigned_version'] == 2 or module.params['ownca_version'] == 2:
2530                    module.warn('crypto backend forced to pyopenssl. The cryptography library does not support v2 certificates')
2531                    backend = 'pyopenssl'
2532
2533                # Fail if no backend has been found
2534                if backend == 'auto':
2535                    module.fail_json(msg=("Can't detect any of the required Python libraries "
2536                                          "cryptography (>= {0}) or PyOpenSSL (>= {1})").format(
2537                                              MINIMAL_CRYPTOGRAPHY_VERSION,
2538                                              MINIMAL_PYOPENSSL_VERSION))
2539
2540            if backend == 'pyopenssl':
2541                if not PYOPENSSL_FOUND:
2542                    module.fail_json(msg=missing_required_lib('pyOpenSSL >= {0}'.format(MINIMAL_PYOPENSSL_VERSION)),
2543                                     exception=PYOPENSSL_IMP_ERR)
2544                if module.params['provider'] in ['selfsigned', 'ownca', 'assertonly']:
2545                    try:
2546                        getattr(crypto.X509Req, 'get_extensions')
2547                    except AttributeError:
2548                        module.fail_json(msg='You need to have PyOpenSSL>=0.15')
2549
2550                module.deprecate('The module is using the PyOpenSSL backend. This backend has been deprecated', version='2.13')
2551                if provider == 'selfsigned':
2552                    certificate = SelfSignedCertificate(module)
2553                elif provider == 'acme':
2554                    certificate = AcmeCertificate(module, 'pyopenssl')
2555                elif provider == 'ownca':
2556                    certificate = OwnCACertificate(module)
2557                elif provider == 'entrust':
2558                    certificate = EntrustCertificate(module, 'pyopenssl')
2559                else:
2560                    certificate = AssertOnlyCertificate(module)
2561            elif backend == 'cryptography':
2562                if not CRYPTOGRAPHY_FOUND:
2563                    module.fail_json(msg=missing_required_lib('cryptography >= {0}'.format(MINIMAL_CRYPTOGRAPHY_VERSION)),
2564                                     exception=CRYPTOGRAPHY_IMP_ERR)
2565                if module.params['selfsigned_version'] == 2 or module.params['ownca_version'] == 2:
2566                    module.fail_json(msg='The cryptography backend does not support v2 certificates, '
2567                                         'use select_crypto_backend=pyopenssl for v2 certificates')
2568                if provider == 'selfsigned':
2569                    certificate = SelfSignedCertificateCryptography(module)
2570                elif provider == 'acme':
2571                    certificate = AcmeCertificate(module, 'cryptography')
2572                elif provider == 'ownca':
2573                    certificate = OwnCACertificateCryptography(module)
2574                elif provider == 'entrust':
2575                    certificate = EntrustCertificate(module, 'cryptography')
2576                else:
2577                    certificate = AssertOnlyCertificateCryptography(module)
2578
2579        if module.params['state'] == 'present':
2580            if module.check_mode:
2581                result = certificate.dump(check_mode=True)
2582                result['changed'] = module.params['force'] or not certificate.check(module)
2583                module.exit_json(**result)
2584
2585            certificate.generate(module)
2586        else:
2587            if module.check_mode:
2588                result = certificate.dump(check_mode=True)
2589                result['changed'] = os.path.exists(module.params['path'])
2590                module.exit_json(**result)
2591
2592            certificate.remove(module)
2593
2594        result = certificate.dump()
2595        module.exit_json(**result)
2596    except crypto_utils.OpenSSLObjectError as exc:
2597        module.fail_json(msg=to_native(exc))
2598
2599
2600if __name__ == "__main__":
2601    main()
2602