New Windows Module: win_certificate_info (#64035)
* win_cert_stat initial commit with tests * documentation fix. first attempt windows server 2008 compatibility * add formatted dates removed debug tests * make choices generic list * return a list of certificates use .net x509 store instead of PS cert provider * fixed tests file * fix timestamps returning null * rename to win_certificate_info * rename tests win_certificate_info * return certificates as a sorted array open the store with readonly privileges * extensions always returned as an array
This commit is contained in:
parent
ae6fc265c9
commit
a54e77193b
9 changed files with 591 additions and 0 deletions
132
lib/ansible/modules/windows/win_certificate_info.ps1
Normal file
132
lib/ansible/modules/windows/win_certificate_info.ps1
Normal file
|
@ -0,0 +1,132 @@
|
||||||
|
#!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-CertificateInfo ($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; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$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-CertificateInfo -cert $_ } | Sort-Object -Property { $_.thumbprint }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
$store.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
$module.ExitJson()
|
236
lib/ansible/modules/windows/win_certificate_info.py
Normal file
236
lib/ansible/modules/windows/win_certificate_info.py
Normal file
|
@ -0,0 +1,236 @@
|
||||||
|
#!/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)
|
||||||
|
|
||||||
|
# this is a windows documentation stub. actual code lives in the .ps1
|
||||||
|
# file of the same name
|
||||||
|
|
||||||
|
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||||
|
'status': ['preview'],
|
||||||
|
'supported_by': 'community'}
|
||||||
|
|
||||||
|
DOCUMENTATION = r'''
|
||||||
|
---
|
||||||
|
module: win_certificate_info
|
||||||
|
version_added: "2.10"
|
||||||
|
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: win_certificate_store
|
||||||
|
author:
|
||||||
|
- Micah Hunsberger (@mhunsber)
|
||||||
|
'''
|
||||||
|
|
||||||
|
EXAMPLES = r'''
|
||||||
|
- name: Obtain information about a particular certificate in the computer's personal store
|
||||||
|
win_certificate_info:
|
||||||
|
thumbprint: BD7AF104CF1872BDB518D95C9534EA941665FD27
|
||||||
|
register: mycert
|
||||||
|
|
||||||
|
# thumbprint can also be lower case
|
||||||
|
- name: Obtain information about a particular certificate in the computer's personal store
|
||||||
|
win_certificate_info:
|
||||||
|
thumbprint: bd7af104cf1872bdb518d95c9534ea941665fd27
|
||||||
|
register: mycert
|
||||||
|
|
||||||
|
- name: Obtain information about all certificates in the root store
|
||||||
|
win_certificate_info:
|
||||||
|
store_name: Root
|
||||||
|
register: ca
|
||||||
|
|
||||||
|
# Import a pfx and then get information on the certificates
|
||||||
|
- name: Import pfx certificate that is password protected
|
||||||
|
win_certificate_store:
|
||||||
|
path: C:\Temp\cert.pfx
|
||||||
|
state: present
|
||||||
|
password: VeryStrongPasswordHere!
|
||||||
|
become: yes
|
||||||
|
become_method: runas
|
||||||
|
register: mycert
|
||||||
|
|
||||||
|
- name: Obtain information on each certificate that was touched
|
||||||
|
win_certificate_info:
|
||||||
|
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
test/integration/targets/win_certificate_info/aliases
Normal file
1
test/integration/targets/win_certificate_info/aliases
Normal file
|
@ -0,0 +1 @@
|
||||||
|
shippable/windows/group7
|
|
@ -0,0 +1,3 @@
|
||||||
|
win_cert_dir: '{{win_output_dir}}\win_certificate .ÅÑŚÌβŁÈ [$!@^&test(;)]'
|
||||||
|
subj_thumbprint: 'BD7AF104CF1872BDB518D95C9534EA941665FD27'
|
||||||
|
root_thumbprint: 'BC05633694E675449136679A658281F17A191087'
|
|
@ -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-----
|
||||||
|
|
|
@ -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-----
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
dependencies:
|
||||||
|
- prepare_win_tests
|
88
test/integration/targets/win_certificate_info/tasks/main.yml
Normal file
88
test/integration/targets/win_certificate_info/tasks/main.yml
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
### keys in files/ have been generated with
|
||||||
|
# generate root private key
|
||||||
|
# openssl genrsa -aes256 -out enckey.pem 2048
|
||||||
|
# openssl rsa -in envkey.pem -out root-key.pem
|
||||||
|
#
|
||||||
|
# generate root certificate
|
||||||
|
# openssl req -x509 -key root-key.pem -days 24855 -out root-vert.pem -subj "/CN=root.ansible.com/C=US"
|
||||||
|
#
|
||||||
|
# generate subject private key
|
||||||
|
# openssl genrsa -aes256 -out enckey.pem 2048
|
||||||
|
# openssl rsa -in enckey.pem -out subj-key.pem
|
||||||
|
#
|
||||||
|
# generate subject certificate
|
||||||
|
# openssl req -new -key subj-key.pem -out cert.csr -subj "/CN=subject.ansible.com/C=US"
|
||||||
|
# openssl x509 -req -in cert.csr -CA root-cert.pem -CAkey root-key.pem -CAcreateserial -out subj-cert.pem -days 24855
|
||||||
|
###
|
||||||
|
---
|
||||||
|
- name: ensure test dir is present
|
||||||
|
win_file:
|
||||||
|
path: '{{win_cert_dir}}\exported'
|
||||||
|
state: directory
|
||||||
|
|
||||||
|
- name: copy across test cert files
|
||||||
|
win_copy:
|
||||||
|
src: files/
|
||||||
|
dest: '{{win_cert_dir}}'
|
||||||
|
|
||||||
|
- name: subject cert imported to personal store
|
||||||
|
win_certificate_store:
|
||||||
|
path: '{{win_cert_dir}}\subj-cert.pem'
|
||||||
|
state: present
|
||||||
|
store_name: My
|
||||||
|
|
||||||
|
- name: root certificate imported to trusted root
|
||||||
|
win_certificate_store:
|
||||||
|
path: '{{win_cert_dir}}\root-cert.pem'
|
||||||
|
store_name: Root
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: get raw root certificate
|
||||||
|
shell: 'cat root-cert.pem | grep "^[^-]"'
|
||||||
|
args:
|
||||||
|
chdir: '{{ role_path }}/files'
|
||||||
|
register: root_raw
|
||||||
|
delegate_to: localhost
|
||||||
|
|
||||||
|
- name: get public key of root certificate
|
||||||
|
shell: 'openssl x509 -pubkey -noout -in root-cert.pem | grep "^[^-]"'
|
||||||
|
args:
|
||||||
|
chdir: '{{ role_path }}/files'
|
||||||
|
register: root_pub
|
||||||
|
delegate_to: localhost
|
||||||
|
|
||||||
|
- name: get subject certificate
|
||||||
|
shell: 'cat subj-cert.pem | grep "^[^-]"'
|
||||||
|
args:
|
||||||
|
chdir: '{{ role_path }}/files'
|
||||||
|
register: subj_raw
|
||||||
|
delegate_to: localhost
|
||||||
|
|
||||||
|
- name: get public key of subject certificate
|
||||||
|
shell: 'openssl x509 -pubkey -noout -in subj-cert.pem | grep "^[^-]"'
|
||||||
|
args:
|
||||||
|
chdir: '{{ role_path }}/files'
|
||||||
|
register: subj_pub
|
||||||
|
delegate_to: localhost
|
||||||
|
|
||||||
|
- block:
|
||||||
|
- name: run tests
|
||||||
|
include_tasks: tests.yml
|
||||||
|
|
||||||
|
always:
|
||||||
|
- name: ensure subject cert removed from personal store
|
||||||
|
win_certificate_store:
|
||||||
|
thumbprint: '{{subj_thumbprint}}'
|
||||||
|
state: absent
|
||||||
|
store_name: My
|
||||||
|
|
||||||
|
- name: ensure root cert removed from trusted root
|
||||||
|
win_certificate_store:
|
||||||
|
thumbprint: '{{root_thumbprint}}'
|
||||||
|
state: absent
|
||||||
|
store_name: Root
|
||||||
|
|
||||||
|
- name: ensure test dir is deleted
|
||||||
|
win_file:
|
||||||
|
path: '{{win_cert_dir}}'
|
||||||
|
state: absent
|
|
@ -0,0 +1,90 @@
|
||||||
|
---
|
||||||
|
|
||||||
|
- name: get stats on a store that doesn't exist
|
||||||
|
win_certificate_info:
|
||||||
|
store_name: teststore
|
||||||
|
register: test_store
|
||||||
|
|
||||||
|
- name: ensure exists is false
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- test_store.exists == false
|
||||||
|
|
||||||
|
- name: get stats on the root certificate store
|
||||||
|
win_certificate_info:
|
||||||
|
store_name: Root
|
||||||
|
register: root_store
|
||||||
|
|
||||||
|
- name: at least one certificate is returned
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- "root_store.exists"
|
||||||
|
- "root_store.certificates | length > 0"
|
||||||
|
|
||||||
|
- name: get stats on a certificate that doesn't exist
|
||||||
|
win_certificate_info:
|
||||||
|
thumbprint: ABC
|
||||||
|
register: actual
|
||||||
|
|
||||||
|
- name: ensure exists is false
|
||||||
|
assert:
|
||||||
|
that: actual.exists == false
|
||||||
|
|
||||||
|
- name: get stats on root certificate
|
||||||
|
win_certificate_info:
|
||||||
|
thumbprint: '{{ root_thumbprint }}'
|
||||||
|
store_name: Root
|
||||||
|
register: root_stats
|
||||||
|
|
||||||
|
- name: root certificate stats returned are expected values
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- root_stats.exists
|
||||||
|
- root_stats.certificates[0].archived == false
|
||||||
|
- root_stats.certificates[0].dns_names == [ 'root.ansible.com' ]
|
||||||
|
- root_stats.certificates[0].extensions|count == 3
|
||||||
|
- root_stats.certificates[0].has_private_key == false
|
||||||
|
- root_stats.certificates[0].issued_by == 'root.ansible.com'
|
||||||
|
- root_stats.certificates[0].issued_to == 'root.ansible.com'
|
||||||
|
- root_stats.certificates[0].issuer == 'C=US, CN=root.ansible.com'
|
||||||
|
- root_stats.certificates[0].path_length_constraint == 0
|
||||||
|
# - root_stats.certificates[0].public_key == (root_pub.stdout_lines|join())
|
||||||
|
- root_stats.certificates[0].raw == root_raw.stdout_lines|join()
|
||||||
|
- root_stats.certificates[0].serial_number == '00FD6F21D1A0309BFF'
|
||||||
|
- root_stats.certificates[0].signature_algorithm == 'sha256RSA'
|
||||||
|
- root_stats.certificates[0].ski == 'BA7D9382E63A7011E113ABAA16FE08575B936FA8'
|
||||||
|
- root_stats.certificates[0].subject == 'C=US, CN=root.ansible.com'
|
||||||
|
- root_stats.certificates[0].valid_from == 1513327172
|
||||||
|
- root_stats.certificates[0].valid_from_iso8601 == '2017-12-15T08:39:32Z'
|
||||||
|
- root_stats.certificates[0].valid_to == 3660799172
|
||||||
|
- root_stats.certificates[0].valid_to_iso8601 == '2086-01-02T08:39:32Z'
|
||||||
|
- root_stats.certificates[0].version == 3
|
||||||
|
|
||||||
|
- name: get stats on subject certificate
|
||||||
|
win_certificate_info:
|
||||||
|
thumbprint: '{{ subj_thumbprint }}'
|
||||||
|
register: subj_stats
|
||||||
|
|
||||||
|
- name: subject certificate stats returned are expected values
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- subj_stats.exists
|
||||||
|
- subj_stats.certificates[0].archived == false
|
||||||
|
- subj_stats.certificates[0].dns_names == [ 'subject.ansible.com' ]
|
||||||
|
- subj_stats.certificates[0].extensions|count == 0
|
||||||
|
- subj_stats.certificates[0].has_private_key == false
|
||||||
|
- subj_stats.certificates[0].issued_by == 'root.ansible.com'
|
||||||
|
- subj_stats.certificates[0].issued_to == 'subject.ansible.com'
|
||||||
|
- subj_stats.certificates[0].issuer == 'C=US, CN=root.ansible.com'
|
||||||
|
- subj_stats.certificates[0].path_length_constraint is undefined
|
||||||
|
# - subj_stats.certificates[0].public_key == subj_pub.stdout_lines|join()
|
||||||
|
- subj_stats.certificates[0].raw == subj_raw.stdout_lines|join()
|
||||||
|
- subj_stats.certificates[0].serial_number == '00BF32D3816B5503A7'
|
||||||
|
- subj_stats.certificates[0].signature_algorithm == 'sha256RSA'
|
||||||
|
- subj_stats.certificates[0].ski is undefined
|
||||||
|
- subj_stats.certificates[0].subject == 'C=US, CN=subject.ansible.com'
|
||||||
|
- subj_stats.certificates[0].valid_from == 1513328190
|
||||||
|
- subj_stats.certificates[0].valid_from_iso8601 == '2017-12-15T08:56:30Z'
|
||||||
|
- subj_stats.certificates[0].valid_to == 3660800190
|
||||||
|
- subj_stats.certificates[0].valid_to_iso8601 == '2086-01-02T08:56:30Z'
|
||||||
|
- subj_stats.certificates[0].version == 1
|
Loading…
Reference in a new issue