#region just examples and placeholders for debug
#storeName: fullpath or just name. E.g.: "My" / c:\SOMEHWRE\store.bin / "WinNTServiceName\MY" etc...
#storeProvider: System (a summary map) / SystemRegistry (really is in registry) / File / LDAP
#storeType: LocalMachine / CurrentUser / Services / Users
#PoSh 2.0 was shipped with 2008R2/Win7. In order to have as little dependency on later updates
# as possible this script only uses 2.0 cmdlets
$minimalPSVersion = "2.0"
#region C# Signature
# C# module imports and types where-type variable
# as not all store access options are implemented in System.Security.Cryptography.X509Certificates
$x509HelperSignature = @"
using System;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
namespace SystemCenterCentral
{
namespace Utilities
{
namespace Certificates
{
public class HelperTasks {
[DllImport("crypt32.dll", EntryPoint="CertOpenStore", CharSet=CharSet.Auto, SetLastError=true)]
public static extern IntPtr CertOpenStoreStringPara(
int storeProvider,
int encodingType,
IntPtr hcryptProv,
int flags,
String pvPara);
[DllImport("crypt32.dll", EntryPoint="CertCloseStore", CharSet=CharSet.Auto, SetLastError=true)]
[return : MarshalAs(UnmanagedType.Bool)]
public static extern bool CertCloseStore(
IntPtr storeProvider,
int flags);
}
}
}
}
"@
#endregion
# Get access to the scripting API
$scomAPI = new-object -comObject "MOM.ScriptAPI"
# check if Powershell >= 2.0 is running
if( ($PSVersionTable.PSCompatibleVersions) -contains $minimalPSVersion)
{
#Write-Host Powershell installed: ( $PSVersionTable.PSVersion.ToString() )
#Write-Host It is compatible with version $minimalPSVersion required by this script
}
else
{
#Write-Host Powershell installed: $PSVersionTable.PSVersion.ToString() `t`t`t`t`t`t`t`t -BackgroundColor red
#Write-Host `tIt is not compatible with version $minimalPSVersion required by this script `t -BackgroundColor red
exit
}
# loading crypt32.dll type to [SystemCenterCentral.Utilities.Certificates.HelperTasks]
# NOTE: no exception occurs if type was already loaded. Runtime will then just use the previous one
try
{Add-Type -TypeDefinition $x509HelperSignature}
catch
{
#throw "Unable to load [SystemCenterCentral.Utilities.Certificates.HelperTasks] namespace with crypt32.dll methods"
$scomAPI.LogScriptEvent($scriptName, 129, 2, "Unable to load [SystemCenterCentral.Utilities.Certificates.HelperTasks] namespace with crypt32.dll methods. Retrying on the next script run.")
#exit
}
#ready to rumble
#get certificate store
$certStorePt = [SystemCenterCentral.Utilities.Certificates.HelperTasks]::CertOpenStoreStringPara($storeProv, 0, 0, $storeTp, $storeName)
if ($certStorePt -ne 0)
{
# first see about certificates
#take it from store pointer to full .NET as certificates are exposed there and easier to handle.
# this works perfectly for File, LDAP or WinNT service stores.
$certStore = [System.Security.Cryptography.X509Certificates.X509Store]$certStorePt
$global:certStore = $certStore
# pre-release MP versions used _disable - so make sure that is removed as well
if ($operation -eq 'ENABLE') { Set-X509CertificateEnabled -certStore $certStore -certTumbprint $thumbprint -disableKey ('(' + $disableKey + '|_disabled)') }
if ($operation -eq 'DISABLE') { Set-X509CertificateDisabled -certStore $certStore -certTumbprint $thumbprint -disableKey $disableKey }
if ($operation -eq 'DELETE') { Remove-X509Certificate -certStore $certStore -certTumbprint $thumbprint }
if ($operation -eq 'ARCHIVE') { SET-X509CertificateArchived -certStore $certStore -certTumbprint $thumbprint }
if ($operation -eq 'LIST') { GET-X509Certificate -certStore $certStore -certTumbprint $thumbprint -wideOutput $wideOutput -verify $verify }
if ($operation -eq 'REDISCOVER') {
Write-EventLogEntry -EventLogName 'Operations Manager' -EventSourceName 'Health Service Script' -EventId 121 -EventSeverity 'Information' -EventDescription ("
Task to ask for re-discovery was run.
Computer: %3
Store Name: %4
Store Provider: " + $storeProvider + "
Store Type: " + $storeType) -EventParameter1 $ScriptName -EventParameter2 $computerName -EventParameter3 $storeName
Write-Output ("Asked for re-discovery of the certificate store by writing local event.")
}
# close store
$closeStore = [SystemCenterCentral.Utilities.Certificates.HelperTasks]::CertCloseStore($certStorePt, 0)
}
else
{
$scomAPI.LogScriptEvent($scriptName, 120, 2, ("Failed to open certificate store.`n`nstoreName: {0}`nstoreProvider: {1}`nstoreType: {2}" -f $storeName,$storeProvider,$storeType))
}
#write summary event
}
#removes "disable" flag from certificate's friendly name
function Set-X509CertificateEnabled {
param([System.Security.Cryptography.X509Certificates.X509Store]$certStore = $null,
[string]$certTumbprint = '',
[string]$disableKey = '_(disabled|DoNotMonitor)')
$isEnabled = $true
$cert = $certStore.Certificates | where {$_.Thumbprint -eq $certTumbprint}
if ($cert) {
$certFriendlyName = [string]$cert.get_FriendlyName()
Write-Output ("Certificate with thumbprint $certTumbprint selected...")
if ($certFriendlyName -imatch ( '(?<friendlyNameCore>.*)' + $disableKey + '$')) {
#certificate is currently disabled
try { $cert.set_FriendlyName([string]($matches.friendlyNameCore)) }
catch { Write-Output ("Failed to remove '" + $disableKey + "' from certificate friendly name.") }
}
Write-Output ("Friendly Name set to: " + [string]$cert.get_FriendlyName())
Write-EventLogEntry -EventLogName 'Operations Manager' -EventSourceName 'Health Service Script' -EventId 122 -EventSeverity 'Information' -EventDescription ("
Friendly name tag of certificate was removed via SCOM task '" + $scomTask + "' by user " + $userName + ". Monitoring will resume following the next discovery cycle.
Computer: %3
Store Name: %4
Store Provider: " + $storeProvider + "
Store Type: " + $storeType) -EventParameter1 $ScriptName -EventParameter2 $computerName -EventParameter3 $storeName
}
else { Write-Output ("Certificate with thumbprint $certTumbprint does not exist.") }
}
#adds "disable" flag to certificate's friendly name
function Set-X509CertificateDisabled {
param([System.Security.Cryptography.X509Certificates.X509Store]$certStore = $null,
[string]$certTumbprint = '',
#[string]$disableKey = '_disabled'
[string]$disableKey = '_DoNotMonitor')
$isEnabled = $true
$cert = $certStore.Certificates | where {$_.Thumbprint -eq $certTumbprint}
if ($cert) {
$certFriendlyName = [string]$cert.get_FriendlyName()
Write-Output ("Certificate with thumbprint $certTumbprint selected...")
if ($certFriendlyName -inotmatch ($disableKey + '$')) {
#certificate is currently enabled
try { $cert.set_FriendlyName(($certFriendlyName + $disableKey)) }
catch { Write-Output ("Failed to append '" + $disableKey + "' to certificate friendly name.") }
}
Write-Output ("Friendly Name set to: " + [string]$cert.get_FriendlyName())
Write-EventLogEntry -EventLogName 'Operations Manager' -EventSourceName 'Health Service Script' -EventId 123 -EventSeverity 'Information' -EventDescription ("
Friendly name tag of certificate was added via SCOM task '" + $scomTask + "' by user " + $userName + ". Monitoring will stop following the next discovery cycle.
function Validate-X509Certificate2
# using pure .NET for certificate validation
{
param($X509Certificate2, $X509RevocationFlag, $X509RevocationMode, $X509VerificationFlags)
# EndCertificateOnly: Only the end certificate is checked for revocation.
# EntireChain: The entire chain of certificates is checked for revocation.
# ExcludeRoot: The entire chain, except the root certificate, is checked for revocation.
$X509Chain.ChainPolicy.RevocationFlag = $X509RevocationFlag
# NoCheck: No revocation check is performed on the certificate.
# Offline: A revocation check is made using a cached certificate revocation list (CRL).
# Online: A revocation check is made using an online certificate revocation list (CRL).
$X509Chain.ChainPolicy.RevocationMode = $X509RevocationMode
# AllFlags: All flags pertaining to verification are included.
# AllowUnknownCertificateAuthority: Ignore that the chain cannot be verified due to an unknown certificate authority (CA).
# IgnoreCertificateAuthorityRevocationUnknown: Ignore that the certificate authority revocation is unknown when determining certificate verification.
# IgnoreCtlNotTimeValid: Ignore that the certificate trust list (CTL) is not valid, for reasons such as the CTL has expired, when determining certificate verification.
# IgnoreCtlSignerRevocationUnknown: Ignore that the certificate trust list (CTL) signer revocation is unknown when determining certificate verification.
# IgnoreEndRevocationUnknown: Ignore that the end certificate (the user certificate) revocation is unknown when determining certificate verification.
# IgnoreInvalidBasicConstraints: Ignore that the basic constraints are not valid when determining certificate verification.
# IgnoreInvalidName: Ignore that the certificate has an invalid name when determining certificate verification.
# IgnoreInvalidPolicy: Ignore that the certificate has invalid policy when determining certificate verification.
# IgnoreNotTimeNested: Ignore that the CA (certificate authority) certificate and the issued certificate have validity periods that are not nested when verifying the certificate. For example, the CA cert can be valid from January 1 to December 1 and the issued certificate from January 2 to December 2, which would mean the validity periods are not nested.
# IgnoreNotTimeValid: Ignore certificates in the chain that are not valid either because they have expired or they are not yet in effect when determining certificate validity.
# IgnoreRootRevocationUnknown: Ignore that the root revocation is unknown when determining certificate verification.
# IgnoreWrongUsage: Ignore that the certificate was not issued for the current use when determining certificate verification.
# NoFlag: No flags pertaining to verification are included.
$X509Chain.ChainPolicy.VerificationFlags = $X509VerificationFlags
#explicitly forcing verificationtime to NOW
$X509Chain.ChainPolicy.VerificationTime = (Get-Date).ToUniversalTime()
$statusSummaryChain = @()
#Builds an X.509 chain using the policy specified
# true if the X.509 certificate is valid; otherwise, false
if ($X509Chain.Build($X509Certificate2))
{
#Write-Host -BackgroundColor green $X509Certificate2.Subject is valid
$valid = $true
$statusSummary = $null
$statusSummaryCert = $null
$statusSummaryChain = $null
}
else
{
$valid = $false
#Write-Host -BackgroundColor Yellow $X509Certificate2.Subject is not valid
$statusSummary = $X509Chain.ChainStatus | %{
if ($_.StatusInformation.ToString().Trim() -imatch '^unknown error\.') {($_.Status.ToString().Trim() + ":")}
else {($_.Status.ToString().Trim() + ": " + $_.StatusInformation.ToString().Trim())}
}
if ($X509Chain.ChainElements.Count -gt 1) {
#build verbose string with the chain level status
$chainLevel = ($X509Chain.ChainElements.Count - 1)
$X509Chain.ChainElements | % {
#certificate's status
if ($_.Certificate.Thumbprint -eq $X509Certificate2.Thumbprint) {
if ($_.ChainElementStatus) {
$statusSummaryCert = $_.ChainElementStatus | %{
if ($_.StatusInformation.ToString().Trim() -imatch '^unknown error\.') {($_.Status.ToString().Trim() + ":" + "`n")}
else {($_.Status.ToString().Trim() + ": " + $_.StatusInformation.ToString().Trim() + "`n")}
}
#Write-Host -BackgroundColor yellow CERT: $statusSummaryCert
}
else {
$statusSummaryCert = $CERTVALID
#Write-Host -BackgroundColor green CERT: $statusSummaryCert
}
}
#chain element status
else {
$statusSummaryChainObj = New-Object psobject
$statusSummaryChainObj | Add-Member -MemberType NoteProperty -Name chainLevel -Value $chainLevel
$statusSummaryChainObj | Add-Member -MemberType NoteProperty -Name chainSubject -Value $_.Certificate.Subject
if ($_.ChainElementStatus) {
$statusSummaryChainCert = $_.ChainElementStatus | %{
if ($_.StatusInformation.ToString().Trim() -imatch '^unknown error\.') {($_.Status.ToString().Trim() + ":" +"`n")}
else {($_.Status.ToString().Trim() + ": " + $_.StatusInformation.ToString().Trim() + "`n")}
}
#Write-Host -BackgroundColor yellow CHAIN: $statusSummaryChain
}
else {
$statusSummaryChainCert = $CERTVALID
#Write-Host -BackgroundColor green CHAIN: $statusSummaryChain
}
$statusSummaryChainObj | Add-Member -MemberType NoteProperty -Name chainSummary -Value $statusSummaryChainCert