Skip to content

Commit

Permalink
Migrate win_certificate_info module to ansible.windows repo from ansi…
Browse files Browse the repository at this point in the history
…ble.community repo
  • Loading branch information
ronger4 committed Nov 4, 2024
1 parent 5f859d1 commit 64f5490
Show file tree
Hide file tree
Showing 9 changed files with 590 additions and 0 deletions.
115 changes: 115 additions & 0 deletions plugins/modules/win_certificate_data.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
#!powershell

# Copyright: (c) 2019, Micah Hunsberger
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

#AnsibleRequires -CSharpUtil Ansible.Basic

function ConvertTo-Timestamp($start_date, $end_date) {
if ($start_date -and $end_date) {
return (New-TimeSpan -Start $start_date -End $end_date).TotalSeconds
}
}

function Format-Date([DateTime]$date) {
return $date.ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssK')
}

function Get-CertificateDATA ($cert) {
$epoch_date = Get-Date -Date "01/01/1970"

$cert_info = @{ extensions = @() }
$cert_info.friendly_name = $cert.FriendlyName
$cert_info.thumbprint = $cert.Thumbprint
$cert_info.subject = $cert.Subject
$cert_info.issuer = $cert.Issuer
$cert_info.valid_from = (ConvertTo-Timestamp -start_date $epoch_date -end_date $cert.NotBefore.ToUniversalTime())
$cert_info.valid_from_iso8601 = Format-Date -date $cert.NotBefore
$cert_info.valid_to = (ConvertTo-Timestamp -start_date $epoch_date -end_date $cert.NotAfter.ToUniversalTime())
$cert_info.valid_to_iso8601 = Format-Date -date $cert.NotAfter
$cert_info.serial_number = $cert.SerialNumber
$cert_info.archived = $cert.Archived
$cert_info.version = $cert.Version
$cert_info.has_private_key = $cert.HasPrivateKey
$cert_info.issued_by = $cert.GetNameInfo('SimpleName', $true)
$cert_info.issued_to = $cert.GetNameInfo('SimpleName', $false)
$cert_info.signature_algorithm = $cert.SignatureAlgorithm.FriendlyName
$cert_info.dns_names = [System.Collections.Generic.List`1[String]]@($cert_info.issued_to)
$cert_info.raw = [System.Convert]::ToBase64String($cert.GetRawCertData())
$cert_info.public_key = [System.Convert]::ToBase64String($cert.GetPublicKey())
if ($cert.Extensions.Count -gt 0) {
[array]$cert_info.extensions = foreach ($extension in $cert.Extensions) {
$extension_info = @{
critical = $extension.Critical
field = $extension.Oid.FriendlyName
value = $extension.Format($false)
}
if ($extension -is [System.Security.Cryptography.X509Certificates.X509BasicConstraintsExtension]) {
$cert_info.is_ca = $extension.CertificateAuthority
$cert_info.path_length_constraint = $extension.PathLengthConstraint
}
elseif ($extension -is [System.Security.Cryptography.X509Certificates.X509EnhancedKeyUsageExtension]) {
$cert_info.intended_purposes = $extension.EnhancedKeyUsages.FriendlyName -as [string[]]
}
elseif ($extension -is [System.Security.Cryptography.X509Certificates.X509KeyUsageExtension]) {
$cert_info.key_usages = $extension.KeyUsages.ToString().Split(',').Trim() -as [string[]]
}
elseif ($extension -is [System.Security.Cryptography.X509Certificates.X509SubjectKeyIdentifierExtension]) {
$cert_info.ski = $extension.SubjectKeyIdentifier
}
elseif ($extension.Oid.value -eq '2.5.29.17') {
$sans = $extension.Format($true).Split("`r`n", [System.StringSplitOptions]::RemoveEmptyEntries)
foreach ($san in $sans) {
$san_parts = $san.Split("=")
if ($san_parts.Length -ge 2 -and $san_parts[0].Trim() -eq 'DNS Name') {
$cert_info.dns_names.Add($san_parts[1].Trim())
}
}
}
$extension_info
}
}
return $cert_info
}

$store_location_values = ([System.Security.Cryptography.X509Certificates.StoreLocation]).GetEnumValues() | ForEach-Object { $_.ToString() }

$spec = @{
options = @{
thumbprint = @{ type = "str"; required = $false }
store_name = @{ type = "str"; default = "My"; }
store_location = @{ type = "str"; default = "LocalMachine"; choices = $store_location_values; }
}
supports_check_mode = $true
}

$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec)

$thumbprint = $module.Params.thumbprint
$store_name = $module.Params.store_name
$store_location = [System.Security.Cryptography.X509Certificates.Storelocation]"$($module.Params.store_location)"

$store = New-Object -TypeName System.Security.Cryptography.X509Certificates.X509Store -ArgumentList $store_name, $store_location
$store.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::ReadOnly)

$module.Result.exists = $false
$module.Result.certificates = @()

try {
if ($null -ne $thumbprint) {
$found_certs = $store.Certificates.Find([System.Security.Cryptography.X509Certificates.X509FindType]::FindByThumbprint, $thumbprint, $false)
}
else {
$found_certs = $store.Certificates
}

if ($found_certs.Count -gt 0) {
$module.Result.exists = $true
[array]$module.Result.certificates = $found_certs | ForEach-Object { Get-CertificateData -cert $_ } | Sort-Object -Property { $_.thumbprint }
}
}
finally {
$store.Close()
}

$module.ExitJson()
228 changes: 228 additions & 0 deletions plugins/modules/win_certificate_data.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-

# Copyright: (c) 2016, Ansible, inc
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

DOCUMENTATION = r'''
---
module: win_certificate_data
short_description: Get information on certificates from a Windows Certificate Store
description:
- Returns information about certificates in a Windows Certificate Store.
options:
thumbprint:
description:
- The thumbprint as a hex string of a certificate to find.
- When specified, filters the I(certificates) return value to a single certificate
- See the examples for how to format the thumbprint.
type: str
required: no
store_name:
description:
- The name of the store to search.
- See U(https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.x509certificates.storename)
for a list of built-in store names.
type: str
default: My
store_location:
description:
- The location of the store to search.
type: str
choices: [ CurrentUser, LocalMachine ]
default: LocalMachine
seealso:
- module: ansible.windows.win_certificate_store
author:
- Micah Hunsberger (@mhunsber)
'''

EXAMPLES = r'''
- name: Obtain information about a particular certificate in the computer's personal store
community.windows.win_certificate_data:
thumbprint: BD7AF104CF1872BDB518D95C9534EA941665FD27
register: mycert
# thumbprint can also be lower case
- name: Obtain information about a particular certificate in the computer's personal store
community.windows.win_certificate_data:
thumbprint: bd7af104cf1872bdb518d95c9534ea941665fd27
register: mycert
- name: Obtain information about all certificates in the root store
community.windows.win_certificate_data:
store_name: Root
register: ca
# Import a pfx and then get information on the certificates
- name: Import pfx certificate that is password protected
ansible.windows.win_certificate_store:
path: C:\Temp\cert.pfx
state: present
password: VeryStrongPasswordHere!
become: true
become_method: runas
register: mycert
- name: Obtain information on each certificate that was touched
community.windows.win_certificate_data:
thumbprint: "{{ item }}"
register: mycert_stats
loop: "{{ mycert.thumbprints }}"
'''

RETURN = r'''
exists:
description:
- Whether any certificates were found in the store.
- When I(thumbprint) is specified, returns true only if the certificate mathing the thumbprint exists.
returned: success
type: bool
sample: true
certificates:
description:
- A list of information about certificates found in the store, sorted by thumbprint.
returned: success
type: list
elements: dict
contains:
archived:
description: Indicates that the certificate is archived.
type: bool
sample: false
dns_names:
description: Lists the registered dns names for the certificate.
type: list
elements: str
sample: [ '*.m.wikiquote.org', '*.wikipedia.org' ]
extensions:
description: The collection of the certificates extensions.
type: list
elements: dict
sample: [
{
"critical": false,
"field": "Subject Key Identifier",
"value": "88 27 17 09 a9 b6 18 60 8b ec eb ba f6 47 59 c5 52 54 a3 b7"
},
{
"critical": true,
"field": "Basic Constraints",
"value": "Subject Type=CA, Path Length Constraint=None"
},
{
"critical": false,
"field": "Authority Key Identifier",
"value": "KeyID=2b d0 69 47 94 76 09 fe f4 6b 8d 2e 40 a6 f7 47 4d 7f 08 5e"
},
{
"critical": false,
"field": "CRL Distribution Points",
"value": "[1]CRL Distribution Point: Distribution Point Name:Full Name:URL=http://crl.apple.com/root.crl"
},
{
"critical": true,
"field": "Key Usage",
"value": "Digital Signature, Certificate Signing, Off-line CRL Signing, CRL Signing (86)"
},
{
"critical": false,
"field": null,
"value": "05 00"
}
]
friendly_name:
description: The associated alias for the certificate.
type: str
sample: Microsoft Root Authority
has_private_key:
description: Indicates that the certificate contains a private key.
type: bool
sample: false
intended_purposes:
description: lists the intended applications for the certificate.
returned: enhanced key usages extension exists.
type: list
sample: [ "Server Authentication" ]
is_ca:
description: Indicates that the certificate is a certificate authority (CA) certificate.
returned: basic constraints extension exists.
type: bool
sample: true
issued_by:
description: The certificate issuer's common name.
type: str
sample: Apple Root CA
issued_to:
description: The certificate's common name.
type: str
sample: Apple Worldwide Developer Relations Certification Authority
issuer:
description: The certificate issuer's distinguished name.
type: str
sample: 'CN=Apple Root CA, OU=Apple Certification Authority, O=Apple Inc., C=US'
key_usages:
description:
- Defines how the certificate key can be used.
- If this value is not defined, the key can be used for any purpose.
returned: key usages extension exists.
type: list
elements: str
sample: [ "CrlSign", "KeyCertSign", "DigitalSignature" ]
path_length_constraint:
description:
- The number of levels allowed in a certificates path.
- If this value is 0, the certificate does not have a restriction.
returned: basic constraints extension exists
type: int
sample: 0
public_key:
description: The base64 encoded public key of the certificate.
type: str
cert_data:
description: The base64 encoded data of the entire certificate.
type: str
serial_number:
description: The serial number of the certificate represented as a hexadecimal string
type: str
sample: 01DEBCC4396DA010
signature_algorithm:
description: The algorithm used to create the certificate's signature
type: str
sample: sha1RSA
ski:
description: The certificate's subject key identifier
returned: subject key identifier extension exists.
type: str
sample: 88271709A9B618608BECEBBAF64759C55254A3B7
subject:
description: The certificate's distinguished name.
type: str
sample: 'CN=Apple Worldwide Developer Relations Certification Authority, OU=Apple Worldwide Developer Relations, O=Apple Inc., C=US'
thumbprint:
description:
- The thumbprint as a hex string of the certificate.
- The return format will always be upper case.
type: str
sample: FF6797793A3CD798DC5B2ABEF56F73EDC9F83A64
valid_from:
description: The start date of the certificate represented in seconds since epoch.
type: float
sample: 1360255727
valid_from_iso8601:
description: The start date of the certificate represented as an iso8601 formatted date.
type: str
sample: '2017-12-15T08:39:32Z'
valid_to:
description: The expiry date of the certificate represented in seconds since epoch.
type: float
sample: 1675788527
valid_to_iso8601:
description: The expiry date of the certificate represented as an iso8601 formatted date.
type: str
sample: '2086-01-02T08:39:32Z'
version:
description: The x509 format version of the certificate
type: int
sample: 3
'''
1 change: 1 addition & 0 deletions tests/integration/targets/win_certificate_data/aliases
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
shippable/windows/group3
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
win_cert_dir: '{{ remote_tmp_dir }}\win_certificate .ÅÑŚÌβŁÈ [$!@^&test(;)]'
subj_thumbprint: 'BD7AF104CF1872BDB518D95C9534EA941665FD27'
root_thumbprint: 'BC05633694E675449136679A658281F17A191087'
20 changes: 20 additions & 0 deletions tests/integration/targets/win_certificate_data/files/root-cert.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
-----BEGIN CERTIFICATE-----
MIIDKDCCAhCgAwIBAgIJAP1vIdGgMJv/MA0GCSqGSIb3DQEBCwUAMCgxGTAXBgNV
BAMMEHJvb3QuYW5zaWJsZS5jb20xCzAJBgNVBAYTAlVTMCAXDTE3MTIxNTA4Mzkz
MloYDzIwODYwMTAyMDgzOTMyWjAoMRkwFwYDVQQDDBByb290LmFuc2libGUuY29t
MQswCQYDVQQGEwJVUzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMmq
YT8eZY6rFQKnmScUGnnUH1tLQ+3WQpfKiWygCUSb1CNqO3J1u3pGMEqYM58LK4Kr
Mpskv7K1tCV/EMZqGTqXAIfSLy9umlb/9C3AhL9thBPn5I9dam/EmrIZktI9/w5Y
wBXn4toe+OopA3QkMQh9BUjUCPb9fdOI+ir7OGFZMmxXmiM64+BEeywM2oSGsdZ9
5hU378UBu2IX4+OAV8Fbr2l6VW+Fxg/tKIOo6Bs46Pa4EZgtemOqs3kxYBOltBTb
vFcLsLa4KYVu5Ge5YfB0Axfaem7PoP8IlMs8gxyojZ/r0o5hzxUcYlL/h8GeeoLW
PFFdiAS+UgxWINOqNXMCAwEAAaNTMFEwHQYDVR0OBBYEFLp9k4LmOnAR4ROrqhb+
CFdbk2+oMB8GA1UdIwQYMBaAFLp9k4LmOnAR4ROrqhb+CFdbk2+oMA8GA1UdEwEB
/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAGksycHsjGbXfWfuhQh+CvXk/A2v
MoNgiHtNMTGliVNgoVp1B1rj4x9xyZ8YrO8GAmv8jaCwCShd0B5Ul4aZVk1wglVv
lFAwb4IAZN9jv9+fw5BRzQ2tLhkVWIEwx6pZkhGhhjBvMaplLN5JwBtsdZorFbm7
wuKiUKcFAM28acoOhCmOhgyNNBZpZn5wXaQDY43AthJOhitAV7vph4MPUkwIJnOh
MA5GJXEqS58TE9z9pkhQnn9598G8tmOXyA2erAoM9JAXM3EYHxVpoHBb9QRj6WAw
XVBo6qRXkwjNEM5CbnD4hVIBsdkOGsDrgd4Q5izQZ3x+jFNkdL/zPsXjJFw=
-----END CERTIFICATE-----

19 changes: 19 additions & 0 deletions tests/integration/targets/win_certificate_data/files/subj-cert.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
-----BEGIN CERTIFICATE-----
MIIC0TCCAbkCCQC/MtOBa1UDpzANBgkqhkiG9w0BAQsFADAoMRkwFwYDVQQDDBBy
b290LmFuc2libGUuY29tMQswCQYDVQQGEwJVUzAgFw0xNzEyMTUwODU2MzBaGA8y
MDg2MDEwMjA4NTYzMFowKzEcMBoGA1UEAwwTc3ViamVjdC5hbnNpYmxlLmNvbTEL
MAkGA1UEBhMCVVMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDszqdF
So3GlVP1xUnN4bSPrFRFiOl/Mqup0Zn5UJJUR9wLnRD+OLcq7kKin6hYqozSu7cC
+BnWQoq7vGSSNVqv7BqFMwzGJt9IBUQv0UqIQkA/duUdKdAiMn2PQRsNDnkWEbTj
4xsitItVNv84cDG0lkZBYyTgfyZlZLZWplkpUQkrZhoFCekZRJ+ODrqNW3W560rr
OUIh+HiQeBqocat6OdxgICBqpUh8EVo1iha3DXjGN08q5utg6gmbIl2VBaVJjfyd
wnUSqHylJwh6WCIEh+HXsn4ndfNWSN/fDqvi5I10V1j6Zos7yqQf8qAezUAm6eSq
hLgZz0odq9DsO4HHAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAFK5mVIJ2D+kI0kk
sxnW4ibWFjzlYFYPYrZg+2JFIVTbKBg1YzyhuIKm0uztqRxQq5iLn/C/uponHoqF
7KDQI37KAJIQdgSva+mEuO9bZAXg/eegail2hN6np7HjOKlPu23s40dAbFrbcOWP
VbsBEPDP0HLv6OgbQWzNlE9HO1b7pX6ozk3q4ULO7IR85P6OHYsBBThL+qsOTzg/
gVknuB9+n9hgNqZcAcXBLDetOM9aEmYJCGk0enYP5UGLYpseE+rTXFbRuHTPr1o6
e8BetiSWS/wcrV4ZF5qr9NiYt5eD6JzTB5Rn5awxxj0FwMtrBu003lLQUWxsuTzz
35/RLY4=
-----END CERTIFICATE-----

2 changes: 2 additions & 0 deletions tests/integration/targets/win_certificate_data/meta/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
dependencies:
- setup_remote_tmp_dir
Loading

0 comments on commit 64f5490

Please sign in to comment.