Remove undocumented certificate Win32 APIs (#3818)

The code paths deleted where using the undocumented Win32 APIs on WIn8 and newer for the following purposes:
* Faster filtering
* Getting two properties (left empty on Win7 and below)
* Logging using Certificate components when a cert is deleted or copied.

After the change, all the code uses public APIs.  Filtering is done in PowerShell using existing code.  I don't believe the logging is needed.
This commit is contained in:
Travis Plunk
2017-05-22 22:33:51 -07:00
committed by Dongbo Wang
parent 7517004052
commit 3496136999
5 changed files with 437 additions and 618 deletions
@@ -15,6 +15,7 @@ using System.Collections.ObjectModel;
using System.Collections;
using System.Runtime.InteropServices;
using System.Management.Automation.Provider;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text.RegularExpressions;
using System.Globalization;
@@ -154,134 +155,6 @@ namespace Microsoft.PowerShell.Commands
}
}
/// <summary>
/// Defines the Certificate Provider dynamic parameters.
///
/// We only support one dynamic parameter for Win 7 and earlier:
/// CodeSigningCert
/// If provided, we only return certificates valid for signing code or
/// scripts.
///
/// For Win 8 and later, we also support:
/// SSLServerAuthentication
/// If provided, only return certificates valid for server authentication.
///
/// DnsName
/// If provided, only return certificates matching the supplied DNS Name.
///
/// Eku
/// If provided, only return certificates containing all of the OIDs
/// supplied.
///
/// ExpiringInDays
/// If provided, only return certificates expiring within the specified
/// number of days.
///
/// </summary>
internal sealed class CertificateProviderDynamicParameters
{
/// <summary>
/// switch that controls whether we only return
/// code signing certs.
/// </summary>
[Parameter()]
public SwitchParameter CodeSigningCert
{
get { return _codeSigningCert; }
[SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "This method is used by command line processing")]
set
{ _codeSigningCert = value; }
}
private SwitchParameter _codeSigningCert = new SwitchParameter();
/// <summary>
/// switch that controls whether we only return
/// data encipherment certs.
/// </summary>
[Parameter()]
public SwitchParameter DocumentEncryptionCert
{
get { return _documentEncryptionCert; }
[SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "This method is used by command line processing")]
set
{ _documentEncryptionCert = value; }
}
private SwitchParameter _documentEncryptionCert = new SwitchParameter();
/// <summary>
/// switch that controls whether we only return
/// code signing certs.
/// </summary>
[Parameter()]
public SwitchParameter SSLServerAuthentication
{
get { return _sslServerAuthentication; }
[SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "This method is used by command line processing")]
set
{ _sslServerAuthentication = value; }
}
private SwitchParameter _sslServerAuthentication = new SwitchParameter();
/// <summary>
/// string to filter certs by DNSName
/// Expected content is a single DNS Name that may start and/or end
/// with '*': "contoso.com" or "*toso.c*"
/// </summary>
[Parameter()]
public DnsNameRepresentation DnsName
{
get { return _dnsName; }
[SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "This method is used by command line processing")]
set
{ _dnsName = value; }
}
private DnsNameRepresentation _dnsName;
/// <summary>
/// string to filter certs by EKU
/// Expected content is one or more OID strings:
/// "1.3.6.1.5.5.7.3.1", etc.
/// For a cert to match, it must be valid for all listed OIDs.
/// </summary>
[Parameter()]
public string[] Eku
{
get { return _eku; }
[SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "This method is used by command line processing")]
set
{ _eku = value; }
}
private string[] _eku = null;
/// <summary>
/// string to filter certs by the number of valid days
/// Expected content is a non-negative integer.
/// "0" matches all certs that have already expired.
/// "1" matches all certs that are currently valid and will expire
/// by midnight tonight (local time).
/// </summary>
[Parameter()]
public int ExpiringInDays
{
get { return _expiringInDays; }
[SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "This method is used by command line processing")]
set
{ _expiringInDays = value; }
}
private int _expiringInDays = -1;
}
/// <summary>
/// Defines the Certificate Provider remove-item dynamic parameters.
///
@@ -351,42 +224,6 @@ namespace Microsoft.PowerShell.Commands
}
}
/// <summary>
/// Defines the safe handle class for native cert store handles,
/// HCERTSTORE.
/// </summary>
internal sealed class CertificateFilterHandle : SafeHandle
{
public CertificateFilterHandle() : base(IntPtr.Zero, true)
{
return;
}
public override bool IsInvalid
{
get { return handle == IntPtr.Zero; }
}
protected override bool ReleaseHandle()
{
bool fResult = false;
if (IntPtr.Zero != handle)
{
Security.NativeMethods.CCFindCertificateFreeFilter(handle);
handle = IntPtr.Zero;
fResult = true;
}
return fResult;
}
public IntPtr Handle
{
get { return handle; }
set { handle = value; }
}
}
/// <summary>
/// Defines the Certificate Provider store handle class
/// </summary>
@@ -477,25 +314,8 @@ namespace Microsoft.PowerShell.Commands
}
}
public IntPtr GetFirstCert(
CertificateFilterInfo filter)
public IntPtr GetFirstCert()
{
_filterHandle = null;
if (DownLevelHelper.NativeFilteringSupported() && filter != null)
{
IntPtr hFilter = IntPtr.Zero;
_filterHandle = new CertificateFilterHandle();
int hr = Security.NativeMethods.CCFindCertificateBuildFilter(
filter.FilterString,
ref hFilter);
if (hr != Security.NativeConstants.S_OK)
{
_filterHandle = null;
throw new System.ComponentModel.Win32Exception(hr);
}
_filterHandle.Handle = hFilter;
}
return GetNextCert(IntPtr.Zero);
}
@@ -508,19 +328,9 @@ namespace Microsoft.PowerShell.Commands
}
if (Valid)
{
if (_filterHandle != null)
{
certContext = Security.NativeMethods.CCFindCertificateFromFilter(
_storeHandle.Handle,
_filterHandle.Handle,
certContext);
}
else
{
certContext = Security.NativeMethods.CertEnumCertificatesInStore(
_storeHandle.Handle,
certContext);
}
certContext = Security.NativeMethods.CertEnumCertificatesInStore(
_storeHandle.Handle,
certContext);
}
else
{
@@ -635,7 +445,6 @@ namespace Microsoft.PowerShell.Commands
private X509StoreLocation _storeLocation = null;
private string _storeName = null;
private CertificateStoreHandle _storeHandle = null;
private CertificateFilterHandle _filterHandle = null;
private bool _valid = false;
private bool _open = false;
}
@@ -1233,7 +1042,7 @@ namespace Microsoft.PowerShell.Commands
if (store != null)
{
store.Open(IncludeArchivedCerts());
IntPtr certContext = store.GetFirstCert(null);
IntPtr certContext = store.GetFirstCert();
if (IntPtr.Zero != certContext)
{
store.FreeCert(certContext);
@@ -1944,7 +1753,7 @@ namespace Microsoft.PowerShell.Commands
//
// enumerate over each cert and remove it
//
IntPtr certContext = store.GetFirstCert(null);
IntPtr certContext = store.GetFirstCert();
while (IntPtr.Zero != certContext)
{
X509Certificate2 cert = new X509Certificate2(certContext);
@@ -2092,10 +1901,7 @@ namespace Microsoft.PowerShell.Commands
CommitUserDS(context.hCertStore);
}
if (DownLevelHelper.LogCertChangeSupported())
{
Security.NativeMethods.LogCertDelete(fMachine, cert.Handle);
}
//TODO: Log Cert Delete
//delete private key
if (fDeleteKey && fHasPrivateKey)
@@ -2173,18 +1979,7 @@ namespace Microsoft.PowerShell.Commands
throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
}
if (DownLevelHelper.LogCertChangeSupported())
{
bool fMachine = store.Location.Location == StoreLocation.LocalMachine;
if (cert.HasPrivateKey &&
String.Equals(store.StoreName, "MY", StringComparison.OrdinalIgnoreCase))
{
Security.NativeMethods.LogCertCopy(fMachine, cert.Handle);
}
Security.NativeMethods.LogCertDelete(fMachine, cert.Handle);
}
//TODO: log cert move
}
//commit the change to physical store
@@ -2499,14 +2294,7 @@ namespace Microsoft.PowerShell.Commands
///
protected override object GetItemDynamicParameters(string path)
{
if (DownLevelHelper.NativeFilteringSupported())
{
return new CertificateProviderDynamicParameters();
}
else
{
return new CertificateProviderCodeSigningDynamicParameters();
}
return new CertificateProviderCodeSigningDynamicParameters();
}
/// <summary>
@@ -2531,14 +2319,7 @@ namespace Microsoft.PowerShell.Commands
/// </returns>
protected override object GetChildItemsDynamicParameters(string path, bool recurse)
{
if (DownLevelHelper.NativeFilteringSupported())
{
return new CertificateProviderDynamicParameters();
}
else
{
return new CertificateProviderCodeSigningDynamicParameters();
}
return new CertificateProviderCodeSigningDynamicParameters();
}
#endregion DriveCmdletProvider overrides
@@ -2718,7 +2499,7 @@ namespace Microsoft.PowerShell.Commands
//
// enumerate over each cert and return it (or its name)
//
IntPtr certContext = store.GetFirstCert(filter);
IntPtr certContext = store.GetFirstCert();
while (IntPtr.Zero != certContext)
{
@@ -2952,62 +2733,14 @@ namespace Microsoft.PowerShell.Commands
if (DynamicParameters != null)
{
if (DownLevelHelper.NativeFilteringSupported())
CertificateProviderCodeSigningDynamicParameters dp =
DynamicParameters as CertificateProviderCodeSigningDynamicParameters;
if (dp != null)
{
CertificateProviderDynamicParameters dp =
DynamicParameters as CertificateProviderDynamicParameters;
if (dp != null)
if (dp.CodeSigningCert)
{
bool filterSpecified = false;
filter = new CertificateFilterInfo();
if (dp.CodeSigningCert)
{
filter.Purpose = CertificatePurpose.CodeSigning;
filterSpecified = true;
}
if (dp.DocumentEncryptionCert)
{
filter.Purpose = CertificatePurpose.DocumentEncryption;
filterSpecified = true;
}
if (dp.SSLServerAuthentication)
{
filter.SSLServerAuthentication = true;
filterSpecified = true;
}
if (dp.DnsName.Punycode != null)
{
filter.DnsName = dp.DnsName.Punycode;
filterSpecified = true;
}
if (dp.Eku != null)
{
filter.Eku = dp.Eku;
filterSpecified = true;
}
if (dp.ExpiringInDays >= 0)
{
filter.ExpiringInDays = dp.ExpiringInDays;
filterSpecified = true;
}
if (!filterSpecified)
{
filter = null;
}
}
}
else
{
CertificateProviderCodeSigningDynamicParameters dp =
DynamicParameters as CertificateProviderCodeSigningDynamicParameters;
if (dp != null)
{
if (dp.CodeSigningCert)
{
filter = new CertificateFilterInfo();
filter.Purpose = CertificatePurpose.CodeSigning;
}
filter.Purpose = CertificatePurpose.CodeSigning;
}
}
}
@@ -3541,33 +3274,20 @@ namespace Microsoft.PowerShell.Commands
/// </summary>
public EnhancedKeyUsageProperty(X509Certificate2 cert)
{
if (DownLevelHelper.NativeFilteringSupported())
foreach (X509Extension extension in cert.Extensions)
{
Collection<string> ekuCollection = System.Management.Automation.Internal.SecuritySupport.GetCertEKU(cert);
foreach (string oidString in ekuCollection)
// Filter to the OID for EKU
if (extension.Oid.Value == "2.5.29.37")
{
if (!String.IsNullOrEmpty(oidString))
X509EnhancedKeyUsageExtension ext = extension as X509EnhancedKeyUsageExtension;
if(ext != null)
{
IntPtr stringAnsi = (IntPtr)Marshal.StringToHGlobalAnsi(oidString);
EnhancedKeyUsageRepresentation ekuString;
IntPtr oidPtr = Security.NativeMethods.CryptFindOIDInfo(
Security.NativeConstants.CRYPT_OID_INFO_OID_KEY,
stringAnsi,
0);
if (oidPtr != IntPtr.Zero)
OidCollection oids = ext.EnhancedKeyUsages;
foreach (Oid oid in oids)
{
Security.NativeMethods.CRYPT_OID_INFO oidInfo =
ClrFacade.PtrToStructure<Security.NativeMethods.CRYPT_OID_INFO>(oidPtr);
ekuString = new EnhancedKeyUsageRepresentation(oidInfo.pwszName, oidString);
EnhancedKeyUsageRepresentation ekuString = new EnhancedKeyUsageRepresentation(oid.FriendlyName, oid.Value);
_ekuList.Add(ekuString);
}
else //if oidInfo is not available
{
ekuString = new EnhancedKeyUsageRepresentation(null, oidString);
}
_ekuList.Add(ekuString);
}
}
}
@@ -3581,6 +3301,9 @@ namespace Microsoft.PowerShell.Commands
public sealed class DnsNameProperty
{
private List<DnsNameRepresentation> _dnsList = new List<DnsNameRepresentation>();
private System.Globalization.IdnMapping idnMapping = new System.Globalization.IdnMapping();
private const string dnsNamePrefix = "DNS Name=";
private const string distinguishedNamePrefix = "CN=";
/// <summary>
/// get property of DnsNameList
@@ -3598,81 +3321,65 @@ namespace Microsoft.PowerShell.Commands
/// </summary>
public DnsNameProperty(X509Certificate2 cert)
{
if (DownLevelHelper.NativeFilteringSupported())
string name;
string unicodeName;
DnsNameRepresentation dnsName;
_dnsList = new List<DnsNameRepresentation>();
// extract DNS name from subject distinguish name
// if it exists and does not contain a comma
// a comma, indicates it is not a DNS name
if(cert.Subject.StartsWith(distinguishedNamePrefix, System.StringComparison.InvariantCultureIgnoreCase) &&
cert.Subject.IndexOf(",",System.StringComparison.InvariantCulture)==-1)
{
if (cert != null)
{
//need to get subject alternative name from the certificate context
_dnsList = GetCertNames(
cert.Handle,
Security.NativeMethods.AltNameType.CERT_ALT_NAME_DNS_NAME);
}
}
}
// Wrapper function for CCGetCertNameList and CCFreeStringArray
private List<DnsNameRepresentation> GetCertNames(IntPtr certHandle, Security.NativeMethods.AltNameType nameType)
{
DWORD cPunycodeName;
IntPtr papwszPunycodeNames = IntPtr.Zero;
IntPtr papwszUnicodeNames = IntPtr.Zero;
List<DnsNameRepresentation> names = new List<DnsNameRepresentation>();
int hr;
if (certHandle != IntPtr.Zero)
{
hr = Security.NativeMethods.CCGetCertNameList(
certHandle,
nameType,
0, // no conversion to Unicode
out cPunycodeName,
out papwszPunycodeNames);
if (hr != Security.NativeConstants.S_OK)
{
if (hr != Security.NativeMethods.CRYPT_E_NOT_FOUND)
{
throw Marshal.GetExceptionForHR(hr);
}
cPunycodeName = 0;
}
name = cert.Subject.Substring(distinguishedNamePrefix.Length);
try
{
if (0 < cPunycodeName)
{
DWORD cUnicodeName;
unicodeName = idnMapping.GetUnicode(name);
}
catch(System.ArgumentException)
{
// The name is not valid punyCode, assume it's valid ascii.
unicodeName = name;
}
hr = Security.NativeMethods.CCGetCertNameList(
certHandle,
nameType,
Security.NativeMethods.CryptDecodeFlags.CRYPT_DECODE_ENABLE_IA5CONVERSION_FLAG,
out cUnicodeName,
out papwszUnicodeNames);
if (hr != Security.NativeConstants.S_OK)
dnsName = new DnsNameRepresentation(name,unicodeName);
_dnsList.Add(dnsName);
}
foreach (X509Extension extension in cert.Extensions)
{
// Filter to the OID for Subject Alternative Name
if (extension.Oid.Value == "2.5.29.17")
{
string[] names = extension.Format(true).Split(Environment.NewLine);
foreach(string nameLine in names)
{
// Get the part after 'DNS Name='
if(nameLine.StartsWith(dnsNamePrefix, System.StringComparison.InvariantCultureIgnoreCase))
{
throw Marshal.GetExceptionForHR(hr);
}
if (cPunycodeName != cUnicodeName)
{
throw Marshal.GetExceptionForHR(
Security.NativeMethods.E_INVALID_DATA);
}
for (int i = 0; i < cPunycodeName; i++)
{
names.Add(new DnsNameRepresentation(
Marshal.PtrToStringUni(Marshal.ReadIntPtr(papwszPunycodeNames, i * Marshal.SizeOf(papwszPunycodeNames))),
Marshal.PtrToStringUni(Marshal.ReadIntPtr(papwszUnicodeNames, i * Marshal.SizeOf(papwszUnicodeNames)))));
name = nameLine.Substring(dnsNamePrefix.Length);
try
{
unicodeName = idnMapping.GetUnicode(name);
}
catch(System.ArgumentException)
{
// The name is not valid punyCode, assume it's valid ascii.
unicodeName = name;
}
dnsName = new DnsNameRepresentation(name,unicodeName);
// Only add the name if it is not the same as an existing name.
if(!_dnsList.Contains(dnsName))
{
_dnsList.Add(dnsName);
}
}
}
}
finally
{
Security.NativeMethods.CCFreeStringArray(papwszPunycodeNames);
Security.NativeMethods.CCFreeStringArray(papwszUnicodeNames);
}
}
return names;
}
}
@@ -3689,9 +3396,6 @@ namespace Microsoft.PowerShell.Commands
{
if (!s_isWin8Set)
{
#if CORECLR
s_isWin8 = true;
#else
System.OperatingSystem osInfo = System.Environment.OSVersion;
PlatformID platform = osInfo.Platform;
Version version = osInfo.Version;
@@ -3702,44 +3406,12 @@ namespace Microsoft.PowerShell.Commands
{
s_isWin8 = true;
}
#endif
s_isWin8Set = true;
}
return s_isWin8;
}
private static bool s_nativeFilteringSet = false;
private static bool s_nativeFiltering = false;
internal static bool NativeFilteringSupported()
{
if (!s_nativeFilteringSet)
{
if (IsWin8AndAbove() && Security.NativeMethods.IsSystem32DllPresent("certca.dll"))
{
s_nativeFiltering = true;
}
s_nativeFilteringSet = true;
}
return s_nativeFiltering;
}
private static bool s_logChangesSet = false;
private static bool s_logChanges = false;
internal static bool LogCertChangeSupported()
{
if (!s_logChangesSet)
{
if (IsWin8AndAbove() && Security.NativeMethods.IsSystem32DllPresent("certenroll.dll"))
{
s_logChanges = true;
}
s_logChangesSet = true;
}
return s_logChanges;
}
internal static bool TrustedIssuerSupported()
{
return IsWin8AndAbove();
@@ -1453,59 +1453,6 @@ namespace System.Management.Automation.Security
CRYPT_DECODE_ENABLE_UTF8PERCENT_FLAG = 0x04000000,
CRYPT_DECODE_ENABLE_IA5CONVERSION_FLAG = (CRYPT_DECODE_ENABLE_PUNYCODE_FLAG | CRYPT_DECODE_ENABLE_UTF8PERCENT_FLAG),
}
// -------------------------------------------------------------------
// certca.dll stuff
//
[DllImport("certca.dll")]
internal static extern
int CCFindCertificateBuildFilter(
[MarshalAsAttribute(UnmanagedType.LPWStr)]
string filter,
ref IntPtr certFilter);
[DllImport("certca.dll")]
internal static extern
void CCFindCertificateFreeFilter(
IntPtr certFilter);
[DllImport("certca.dll")]
internal static extern
IntPtr CCFindCertificateFromFilter(
IntPtr storeHandle,
IntPtr certFilter,
IntPtr prevCertContext);
[DllImport("certca.dll")]
internal static extern
int // HRESULT
CCGetCertNameList(
IntPtr certContext,
AltNameType dwAltNameChoice,
CryptDecodeFlags dwFlags,
out DWORD cName,
out IntPtr papwszName);
[DllImport("certca.dll")]
internal static extern
void CCFreeStringArray(IntPtr papwsz);
}
internal static partial class NativeMethods
{
// HRESULT LogCertDelete(")
// __in bool fMachine,")
// __in PCCERT_CONTEXT pCertContext)
[DllImport("certenroll.dll")]
internal static extern int LogCertDelete(bool fMachine, IntPtr pCertContext);
// HRESULT LogCertCopy(")
// __in bool fMachine,")
// __in PCCERT_CONTEXT pCertContext)
[DllImport("certenroll.dll")]
internal static extern int LogCertCopy(bool fMachine, IntPtr pCertContext);
}
#region Check_UI_Allowed
@@ -0,0 +1,167 @@
# The import and table creation work on non-windows, but are currently not needed
if($IsWindows)
{
Import-Module (Join-Path -Path $PSScriptRoot 'certificateCommon.psm1') -Force
$currentUserMyLocations = @(
@{path = 'Cert:\CurrentUser\my'}
@{path = 'cert:\currentuser\my'}
@{path = 'Microsoft.PowerShell.Security\Certificate::CurrentUser\My'}
@{path = 'Microsoft.PowerShell.Security\certificate::currentuser\my'}
)
$testLocations = @(
@{path = 'cert:\'}
@{path = 'CERT:\'}
@{path = 'Microsoft.PowerShell.Security\Certificate::'}
)
}
# Add CurrentUserMyLocations to TestLocations
foreach($location in $currentUserMyLocations)
{
$testLocations += $location
}
Describe "Certificate Provider tests" -Tags "CI" {
BeforeAll{
if(!$IsWindows)
{
# Skip for non-Windows platforms
$defaultParamValues = $PSdefaultParameterValues.Clone()
$PSdefaultParameterValues = @{ "it:skip" = $true }
}
}
AfterAll {
if(!$IsWindows)
{
$PSdefaultParameterValues = $defaultParamValues
}
}
Context "Get-Item tests" {
it "Should be able to get a certificate store, path: <path>" -TestCases $testLocations {
param([string] $path)
$expectedResolvedPath = Resolve-Path -LiteralPath $path
$result = Get-Item -LiteralPath $path
$result | should not be null
$result | ForEach-Object {
$resolvedPath = Resolve-Path $_.PSPath
$resolvedPath.Provider | should be $expectedResolvedPath.Provider
$resolvedPath.ProviderPath.TrimStart('\') | should be $expectedResolvedPath.ProviderPath.TrimStart('\')
}
}
it "Should return two items at the root of the provider" {
(Get-Item -Path cert:\*).Count | should be 2
}
it "Should be able to get multiple items explictly" {
(get-item cert:\LocalMachine , cert:\CurrentUser).Count | should be 2
}
it "Should return PathNotFound when getting a non-existant certificate store" {
{Get-Item cert:\IDONTEXIST -ErrorAction Stop} | ShouldBeErrorId "PathNotFound,Microsoft.PowerShell.Commands.GetItemCommand"
}
it "Should return PathNotFound when getting a non-existant certificate" {
{Get-Item cert:\currentuser\my\IDONTEXIST -ErrorAction Stop} | ShouldBeErrorId "PathNotFound,Microsoft.PowerShell.Commands.GetItemCommand"
}
}
Context "Get-ChildItem tests"{
it "should be able to get a container using a wildcard" {
(Get-ChildItem Cert:\CurrentUser\M?).PSPath | should be 'Microsoft.PowerShell.Security\Certificate::CurrentUser\My'
}
it "Should return two items at the root of the provider" {
(Get-ChildItem -Path cert:\).Count | should be 2
}
}
}
Describe "Certificate Provider tests" -Tags "Feature" {
BeforeAll{
if($IsWindows)
{
Install-TestCertificates
Push-Location Cert:\
}
else
{
# Skip for non-Windows platforms
$defaultParamValues = $PSdefaultParameterValues.Clone()
$PSdefaultParameterValues = @{ "it:skip" = $true }
}
}
AfterAll {
if($IsWindows)
{
Remove-TestCertificates
Pop-Location
}
else
{
$PSdefaultParameterValues = $defaultParamValues
}
}
Context "Get-Item tests" {
it "Should be able to get certifate by path: <path>" -TestCases $currentUserMyLocations {
param([string] $path)
$expectedThumbprint = (Get-GoodCertificateObject).Thumbprint
$leafPath = Join-Path -Path $path -ChildPath $expectedThumbprint
$cert = (Get-item -LiteralPath $leafPath)
$cert | should not be null
$cert.Thumbprint | should be $expectedThumbprint
}
it "Should be able to get DnsNameList of certifate by path: <path>" -TestCases $currentUserMyLocations {
param([string] $path)
$expectedThumbprint = (Get-GoodCertificateObject).Thumbprint
$expectedName = (Get-GoodCertificateObject).DnsNameList[0].Unicode
$expectedEncodedName = (Get-GoodCertificateObject).DnsNameList[0].Punycode
$leafPath = Join-Path -Path $path -ChildPath $expectedThumbprint
$cert = (Get-item -LiteralPath $leafPath)
$cert | should not be null
$cert.DnsNameList | should not be null
$cert.DnsNameList.Count | should be 1
$cert.DnsNameList[0].Unicode | should be $expectedName
$cert.DnsNameList[0].Punycode | should be $expectedEncodedName
}
it "Should be able to get DNSNameList of certifate by path: <path>" -TestCases $currentUserMyLocations {
param([string] $path)
$expectedThumbprint = (Get-GoodCertificateObject).Thumbprint
$expectedOid = (Get-GoodCertificateObject).EnhancedKeyUsageList[0].ObjectId
$leafPath = Join-Path -Path $path -ChildPath $expectedThumbprint
$cert = (Get-item -LiteralPath $leafPath)
$cert | should not be null
$cert.EnhancedKeyUsageList | should not be null
$cert.EnhancedKeyUsageList.Count | should be 1
$cert.EnhancedKeyUsageList[0].ObjectId.Length | should not be 0
$cert.EnhancedKeyUsageList[0].ObjectId | should be $expectedOid
}
it "Should filter to codesign certificates" {
$allCerts = get-item cert:\CurrentUser\My\*
$codeSignCerts = get-item cert:\CurrentUser\My\* -CodeSigningCert
$codeSignCerts | should not be null
$allCerts | should not be null
$nonCodeSignCertCount = $allCerts.Count - $codeSignCerts.Count
$nonCodeSignCertCount | should not be 0
}
it "Should be able to exclude by thumbprint" {
$allCerts = get-item cert:\CurrentUser\My\*
$testThumbprint = (Get-GoodCertificateObject).Thumbprint
$allCertsExceptOne = (Get-Item "cert:\currentuser\my\*" -Exclude $testThumbprint)
$allCerts | should not be null
$allCertsExceptOne | should not be null
$countDifference = $allCerts.Count - $allCertsExceptOne.Count
$countDifference | should be 1
}
}
Context "Get-ChildItem tests"{
it "Should filter to codesign certificates" {
$allCerts = get-ChildItem cert:\CurrentUser\My
$codeSignCerts = get-ChildItem cert:\CurrentUser\My -CodeSigningCert
$codeSignCerts | should not be null
$allCerts | should not be null
$nonCodeSignCertCount = $allCerts.Count - $codeSignCerts.Count
$nonCodeSignCertCount | should not be 0
}
}
}
@@ -1,96 +1,9 @@
Function Create-GoodCertificate
{
$dataEnciphermentCert = "
MIIKYAIBAzCCCiAGCSqGSIb3DQEHAaCCChEEggoNMIIKCTCCBgoGCSqGSIb3DQEHAaCCBfsEggX3
MIIF8zCCBe8GCyqGSIb3DQEMCgECoIIE/jCCBPowHAYKKoZIhvcNAQwBAzAOBAgPOFDMBkCffQIC
B9AEggTYjY55RrmAhdj1grENxXjiPrVNdS++pb5UOn3M7O78BR0U1i2h5zvjkPjOwdLoOCbq5pgg
F0PKaMjHVu8EoZxSqsib17ptR5Rx5N23hseuJUzS8fTAHiBet9payNOJlPfkpuqMfQEmCAo9gAPz
w4RiyZNOA3NhxkfGl9yU4O9GSEr2koWKCUoCNelkIXVbkV728L7zSiWRSqRb7V4QJAtwtgPLTbl/
zo2SFhdNAGPeXbcOsKCv9trhuxPZ0FH4fukbXkHs0I3b5mYgMUI5Mds7UwT3wCtz+Ev9pLbmYN8X
NfH0tAK8ZGnQS1GcI4xCMEM8T9Tx3uwWY4arvRM3GTLwyt8JZEVZNTuYL9A9+RyeiO5d5xEKG8H4
snOCoTriT45tdl8hzMBCdc3jWxWiydmNRw47irifv5BX7BK/6FLAxkMRwACAxNO63ezG/OxuDDEz
ml+KzeZNvr4u4mTBcgZ49vMSyfRt/my+5+iLnSMGp4Rt0uix8489wctkxGlgyXGk23pA4Cj4hq/6
txopcl2gHn+DAFrHIgLg4JR8lcuOzBw8nFOrhK7iR9aMK21apxwImIaDCKJ1grOfbuElq4pkMpov
SltJe00WB94o69LibOg5LqpTrHW2/DY951sIgElF83FdUBhoZHasfCme/RxgliQf3QHedmENXSjr
8R8PAWX4wC0ZZVC0qcq5XP0PkwtuDKmfqq69R32nmBzpRrfypm9S+PfsYZeCeuROh6YKQ0ZBMnLb
8Y9povpXh0lYwVLuPanvAFiCT4vI7oRd1Mg1Zr9ZooMFAVomall1UnQQ29fvsoADjEDcPymVT80o
kTkw3NXnTX6fGZ94Eh0KZcuMgjTqIO3OKpH0lcaSBxlES4V0sO/mwP4ULy8l3dcnn440Nei33VDo
B7n2jhjJl/HvtltfEEEw1DW9AWDvkJDp878sD6VoyQZehvvxBNT0FwMr20TbVKeAGxf9n+xJ9Alo
VUS3qE7XnpxAAAR5L2OG+tMd4dyDSge1qrkVNZ3/uUzKKCZei2P7ICR9cX1FRsmcINdNfydIA2cA
Pmeq+UkRdqsJxt1NSK5bLvMM4EHRQZMMbXVKxxJ+kQDrzfQtERFPyd3Hdm2F4T/JXUQ+PrTQnRqY
LruTAiZfxygZuFrxJnNTRydRdbEaTAtMjFCMRFZ2wctJsgCb3yN9tt0JDYxIvm0MSehXiF+sCrl4
yZvvUzJqrgppHRTBR4Sao+MZ/rJ30vVU19Q3oBi9ikTqDY+4SrHsp5Y4llnsbrz0Web+h2jLvyJz
LgKuqs87qHhToVMLuULy/HLqY3m661EMwNqh5D76gSFI+TP2/rzT5mVOGglahFoc848o4cshtPWE
9MjAkDfsMbIfeKH3uh3D+eBIxYmZ5Cq2aHzqdQ0pU/nDNX7BDjC3E80VcQnXx4U6tRsQHsGtbcld
MFTp0yHJ2KLkz+inH3WPy/lYuVZ0QJe+LqvGt+bt1DgQmLBMD9WLFML3d0TtkuY3RhD5Y0wr2zt9
tT6WVTn8Hob1cJns4N7tDEr8Q3TdIar0I5Bzj3qoesJt+4lxwnVdUA1bNJ2zxXIkDfX/MTB464FI
2g9uhUs3lIOEiCjeJCwBebgZa1HlfhyCRu0E7fnNnKLaGWRs8LVy7MZIfe1kJoDVgTGB3TATBgkq
hkiG9w0BCRUxBgQEAQAAADBdBgkrBgEEAYI3EQExUB5OAE0AaQBjAHIAbwBzAG8AZgB0ACAAUwB0
AHIAbwBuAGcAIABDAHIAeQBwAHQAbwBnAHIAYQBwAGgAaQBjACAAUAByAG8AdgBpAGQAZQByMGcG
CSqGSIb3DQEJFDFaHlgAQwBlAHIAdABSAGUAcQAtAGEAYgA5AGUAZgA3AGYAOAAtAGUAYwA2AGEA
LQA0ADUAMwA4AC0AOQA4AGQAOAAtADAAYQA4AGUAYwA4ADUAOQBkADkAMgAxMIID9wYJKoZIhvcN
AQcGoIID6DCCA+QCAQAwggPdBgkqhkiG9w0BBwEwHAYKKoZIhvcNAQwBBjAOBAiYL6rZmAGN9QIC
B9CAggOw8eaNgIqx26SlOBKKQZ5O7NDZQHbytHTWn4ifNhVFUkbuaj22/VnYOFB9//8BLY6t0Dvw
X6wqXSMnbr1jOuUYaFdJzOBZBsYQfFTfoJ4iOb2jwwIpZSgTHeqgXbvnI7MIxauu6F4UseWVxt3u
ZhHjEZQjKWeC5mNCb6wX0IOQk96n1RJnJ3v1D4Q5YrVekOVq70VhRNtLOZMkrJV7vDMNlUYXD69D
PbcyPajVvETq0W98YNKB89oNwFWuKoMLNPWmSwIfn10oSEtybNEEr2IVgCBt2w2eb+nIDuA3c/Rc
qKPXwVGMzoUyAiGwTcueCdMRmiuQAuKCUyi9P/JqeIbgHtg15nAoGtw+l4MsFXfdMJjTCDd0WYff
l9ipaNnw8erCPquD0+wXeMnNivXaFzu2+CGwCSwbDl2M49HAQdtpKhNj5jKJBEP1GRQIk173gbEZ
n69IXUCsf0GDZiZVNbAQHBOuRoEHpBhendFgTJFAU2LDHlmV6OA0LYHaSn7CP/vOXOhWXJ1yGL2p
SeUepciwQV7sOHqDExWY82fd1kHSHcgCAkWLSSdIPlWhyeqjC1agSP6b74VK8uLRPkin5F9wGIPi
ewe/LsW3PTtDkDnj3DiSioKlQRUUxVxzi5qPBs+7vJEhbuO7UhtsMCWeUygDbw6n8BKan4w9iLhx
7/z6zVvQmLnK4HZChTPFuThRy1NctupoX7nE+CDgyhcmryaTDXohkviMWl4Od+8uGh8Quv2bHk6Y
UnFqNB/hSqYMkTuMLH4F9sVzoQEsYu96CDwbQaggbLMnPtmHKsPtzdnWQnys+oGT4uD8vl9xFdEW
AZdestrxbDK0La0AgGszUE+6B/GtOs2pv0fMXXYV2h+dAlwfz7oLxzm9E+SFgzviL+6PuI9fDHNd
pWeq/Rr5OpFb3rSotGTl84aIjk3hPd3uHujPQh8GO2EQ5k6p6ukk+a7gOUB+pH8fHihFl5L7pI0z
yRp0FvbZo//hmACYMvINoy2EQxjYLh7QLeE4qEr8bkzJVgEURUvcUpyHFJT6PGzUMqGx/Wjh2jJc
HfEDPMUDoTE/QRzLW7XrmQgJIRuHgPI/cqmOyvpEvuwdRhYyHEKktRO3tGjeflohDCyDW9bxOaJV
ZP64KBordM28ZHCQbnSdU0I5us6qiFX2PiLlBzRMH2ftUNMYReioqZyR+Xv5wjaoydV3//BDMH8M
1lh9GazUO8+OtzQEH0jiBi6ctlzFT8nNI2C+cOB9S3yMAjCEQa8wNzAfMAcGBSsOAwIaBBR96vF2
OksttXT1kXf+aez9EzDlsgQU4ck78h0WTy01zHLwSKNWK4wFFQM=
"
$dataEnciphermentCert = $dataEnciphermentCert -replace '\s',''
$certBytes = [Convert]::FromBase64String($dataEnciphermentCert)
$certLocation = Join-Path $TestDrive "ProtectedEventLogging.pfx"
[IO.File]::WriteAllBytes($certLocation, $certBytes)
return $certLocation
}
Function Create-BadCertificate
{
$codeSigningCert = "
MIIDAjCCAeqgAwIBAgIQW/oHcNaftoFGOYb4w5A0JTANBgkqhkiG9w0BAQsFADAZMRcwFQYDVQQD
DA5DTVNUZXN0QmFkQ2VydDAeFw0xNzAzMDcwNjEyMDNaFw0xODAzMDcwNjMyMDNaMBkxFzAVBgNV
BAMMDkNNU1Rlc3RCYWRDZXJ0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAogOnCBPp
xCQXvCJOe4KUXrTL5hwzKSV/mA4pF/kWCVLseWkeTzll+02S0SzMR1oqyxGZOU+KiakmH8sIxDpS
pBpYi3R+JtaUYeK7IwvM7yMgxzYbVUFupNXDIdFcgd7FwX4R9wJGwd/hEw5fe+ua96G6bBlfQC8j
I8iHfqHZ2GmssIuSt72WhT6tKZhPJIMjwmKaB8j/gm6EC7eH83wNmVW/ss2AsG5cMT0Zmk2vkXPd
7rVYbAh9WcvxYzTYZLsPkXx/s6uanLo7pBPMqQ8fgImSXiD5EBO9d6SzqoagoAkH/l3oKCUztsqU
PAfTu1aTAYRW5O26AcICTbIYOMkDMQIDAQABo0YwRDAOBgNVHQ8BAf8EBAMCB4AwEwYDVR0lBAww
CgYIKwYBBQUHAwMwHQYDVR0OBBYEFLSLHDqLWoDBj0j/UavIf0hAHZ2YMA0GCSqGSIb3DQEBCwUA
A4IBAQB7GJ3ykI07k2D1mfiQ10+Xse4b6KXylbzYfJ1k3K0NEBwT7H/lhMu4yz95A7pXU41yKKVE
OzmpX8QGyczRM269+WpUvVOXwudQL7s/JFeZyEhxPYRP0JC8U9rur1iJeULvsPZJU2kGeLceTl7k
psuZeHouYeNuFeeKR66GcHKzqm+5odAJBxjQ/iGP+CVfNVX56Abhu8mXX6sFiorrBSV/NzPThqja
mtsMC3Fq53xANMjFT4kUqMtK+oehPf0+0jHHra4hpCVZ2KoPLLPxpJPko8hUO5LxATLU+UII7w3c
nMbw+XY4C8xdDnHfS6mF+Hol98dURB/MC/x3sZ3gSjKo
"
$codeSigningCert = $codeSigningCert -replace '\s',''
$certBytes = [Convert]::FromBase64String($codeSigningCert)
$certLocation = Join-Path $TestDrive "CMSTestBadCert"
[IO.File]::WriteAllBytes($certLocation, $certBytes)
return $certLocation
}
Import-Module (Join-Path -Path $PSScriptRoot 'certificateCommon.psm1') -Force
Describe "CmsMessage cmdlets and Get-PfxCertificate basic tests" -Tags "CI" {
BeforeAll {
$certLocation = Create-GoodCertificate
$certLocation = New-GoodCertificate
$certLocation | Should Not BeNullOrEmpty | Out-Null
}
@@ -148,62 +61,23 @@ Describe "CmsMessage cmdlets and Get-PfxCertificate basic tests" -Tags "CI" {
Describe "CmsMessage cmdlets thorough tests" -Tags "Feature" {
BeforeAll {
if ($IsWindows)
BeforeAll{
if($IsWindows)
{
$certLocation = Create-GoodCertificate
$certLocation | Should Not BeNullOrEmpty | Out-Null
$badCertLocation = Create-BadCertificate
$badCertLocation | Should Not BeNullOrEmpty | Out-Null
if ($IsCoreCLR)
{
# PKI module is not available for PowerShell Core, so we need to use Windows PowerShell to import the cert
$fullPowerShell = Join-Path "$env:SystemRoot" "System32\WindowsPowerShell\v1.0\powershell.exe"
try {
$modulePathCopy = $env:PSModulePath
$env:PSModulePath = $null
$command = @"
Import-PfxCertificate $certLocation -CertStoreLocation cert:\CurrentUser\My | % PSPath
Import-Certificate $badCertLocation -CertStoreLocation Cert:\CurrentUser\My | % PSPath
"@
$certPaths = & $fullPowerShell -NoProfile -NonInteractive -Command $command
$certPaths.Count | Should Be 2 | Out-Null
$importedCert = Get-ChildItem $certPaths[0]
$testBadCert = Get-ChildItem $certPaths[1]
} finally {
$env:PSModulePath = $modulePathCopy
}
}
else
{
$importedCert = Import-PfxCertificate $certLocation -CertStoreLocation cert:\CurrentUser\My
$testBadCert = Import-Certificate $badCertLocation -CertStoreLocation Cert:\CurrentUser\My
}
Install-TestCertificates
}
else
{
# Skip for non-Windows platforms
$defaultParamValues = $PSdefaultParameterValues.Clone()
$PSdefaultParameterValues = @{ "it:skip" = $true }
}
}
}
AfterAll {
if ($IsWindows)
if($IsWindows)
{
if ($importedCert)
{
Remove-Item (Join-Path Cert:\CurrentUser\My $importedCert.Thumbprint) -Force -ErrorAction SilentlyContinue
}
if ($testBadCert)
{
Remove-Item (Join-Path Cert:\CurrentUser\My $testBadCert.Thumbprint) -Force -ErrorAction SilentlyContinue
}
Remove-TestCertificates
}
else
{
@@ -246,7 +120,7 @@ Import-Certificate $badCertLocation -CertStoreLocation Cert:\CurrentUser\My | %
It "Verify wildcarded recipient resolution by path [Decryption]" {
$errors = $null
$recipient = [System.Management.Automation.CmsMessageRecipient] ($certLocation + "*")
$recipient = [System.Management.Automation.CmsMessageRecipient] ((Get-GoodCertificateLocation) + "*")
$recipient.Resolve($ExecutionContext.SessionState, "Decryption", [ref] $errors)
# Should have resolved single cert
@@ -255,7 +129,7 @@ Import-Certificate $badCertLocation -CertStoreLocation Cert:\CurrentUser\My | %
It "Verify wildcarded recipient resolution by path [Encryption]" {
$errors = $null
$recipient = [System.Management.Automation.CmsMessageRecipient] ($certLocation + "*")
$recipient = [System.Management.Automation.CmsMessageRecipient] ((Get-GoodCertificateLocation) + "*")
$recipient.Resolve($ExecutionContext.SessionState, "Encryption", [ref] $errors)
$recipient.Certificates.Count | Should Be 1
@@ -264,9 +138,9 @@ Import-Certificate $badCertLocation -CertStoreLocation Cert:\CurrentUser\My | %
It "Verify resolution by directory" {
$protectedEventLoggingCertPath = Join-Path $TestDrive ProtectedEventLoggingDir
$null = New-Item -ItemType Directory $protectedEventLoggingCertPath -Force
Copy-Item $certLocation $protectedEventLoggingCertPath
Copy-Item $certLocation (Join-Path $protectedEventLoggingCertPath "SecondCert.pfx")
Copy-Item $certLocation (Join-Path $protectedEventLoggingCertPath "ThirdCert.pfx")
Copy-Item (Get-GoodCertificateLocation) $protectedEventLoggingCertPath
Copy-Item (Get-GoodCertificateLocation) (Join-Path $protectedEventLoggingCertPath "SecondCert.pfx")
Copy-Item (Get-GoodCertificateLocation) (Join-Path $protectedEventLoggingCertPath "ThirdCert.pfx")
$errors = $null
$recipient = [System.Management.Automation.CmsMessageRecipient] $protectedEventLoggingCertPath
@@ -277,21 +151,21 @@ Import-Certificate $badCertLocation -CertStoreLocation Cert:\CurrentUser\My | %
It "Verify resolution by thumbprint" {
$errors = $null
$recipient = [System.Management.Automation.CmsMessageRecipient] $importedCert.Thumbprint
$recipient = [System.Management.Automation.CmsMessageRecipient] (Get-GoodCertificateObject).Thumbprint
$recipient.Resolve($ExecutionContext.SessionState, "Decryption", [ref] $errors)
# "Should have certs from thumbprint in 'My' store"
$recipient.Certificates.Count | Should Be 1
$recipient.Certificates[0].Thumbprint | Should Be $importedCert.Thumbprint
$recipient.Certificates[0].Thumbprint | Should Be (Get-GoodCertificateObject).Thumbprint
}
It "Verify resolution by subject name" {
$errors = $null
$recipient = [System.Management.Automation.CmsMessageRecipient] $importedCert.Subject
$recipient = [System.Management.Automation.CmsMessageRecipient] (Get-GoodCertificateObject).Subject
$recipient.Resolve($ExecutionContext.SessionState, "Decryption", [ref] $errors)
$recipient.Certificates.Count | Should Be 1
$recipient.Certificates[0].Thumbprint | Should Be $importedCert.Thumbprint
$recipient.Certificates[0].Thumbprint | Should Be (Get-GoodCertificateObject).Thumbprint
}
It "Verify error when no cert found in encryption for encryption" {
@@ -314,7 +188,7 @@ Import-Certificate $badCertLocation -CertStoreLocation Cert:\CurrentUser\My | %
It "Verify error when encrypting to wrong cert" {
$errors = $null
$recipient = [System.Management.Automation.CmsMessageRecipient] $testBadCert.Thumbprint
$recipient = [System.Management.Automation.CmsMessageRecipient] (Get-BadCertificateObject).Thumbprint
$recipient.Resolve($ExecutionContext.SessionState, "Encryption", [ref] $errors)
$errors.Count | Should Be 1
@@ -346,17 +220,17 @@ Import-Certificate $badCertLocation -CertStoreLocation Cert:\CurrentUser\My | %
$encryptedPath = $tempPath + ".encrypted.txt"
"Hello World","How are you?" | Set-Content $tempPath
Protect-CmsMessage -Path $tempPath -To $certLocation -OutFile $encryptedPath
Protect-CmsMessage -Path $tempPath -To (Get-GoodCertificateLocation) -OutFile $encryptedPath
$message = Get-CmsMessage -LiteralPath $encryptedPath
$message.Recipients.Count | Should Be 1
$message.Recipients[0].IssuerName | Should Be "CN=MyDataEnciphermentCert"
$expected = "Hello World" + [System.Environment]::NewLine + "How are you?" + [System.Environment]::NewLine
$decrypted = $message | Unprotect-CmsMessage -To $certLocation
$decrypted = $message | Unprotect-CmsMessage -To (Get-GoodCertificateLocation)
$decrypted | Should Be $expected
$decrypted = Unprotect-CmsMessage -Path $encryptedPath -To $certLocation
$decrypted = Unprotect-CmsMessage -Path $encryptedPath -To (Get-GoodCertificateLocation)
$decrypted | Should Be $expected
} finally {
Remove-Item $tempPath, $encryptedPath -Force -ErrorAction SilentlyContinue
@@ -367,7 +241,7 @@ Import-Certificate $badCertLocation -CertStoreLocation Cert:\CurrentUser\My | %
try {
$randomNum = Get-Random -Minimum 1000 -Maximum 9999
$tempPath = Join-Path $TestDrive "$randomNum-Path-Test-File"
"Hello World" | Protect-CmsMessage -To $certLocation -OutFile $tempPath
"Hello World" | Protect-CmsMessage -To (Get-GoodCertificateLocation) -OutFile $tempPath
# Decrypt using $importedCert in the Cert store
$decrypted = Unprotect-CmsMessage -Path $tempPath
@@ -411,11 +285,11 @@ Import-Certificate $badCertLocation -CertStoreLocation Cert:\CurrentUser\My | %
}
It "Verify Unprotect-CmsMessage lets you include context" {
$protected = "Hello World" | Protect-CmsMessage -To $certLocation
$protected = "Hello World" | Protect-CmsMessage -To (Get-GoodCertificateLocation)
$adjustedProtected = "Pre content" + [System.Environment]::NewLine + $protected + [System.Environment]::NewLine + "Post content"
$decryptedNoContext = $adjustedProtected | Unprotect-CmsMessage -To $certLocation
$decryptedWithContext = $adjustedProtected | Unprotect-CmsMessage -To $certLocation -IncludeContext
$decryptedNoContext = $adjustedProtected | Unprotect-CmsMessage -To (Get-GoodCertificateLocation)
$decryptedWithContext = $adjustedProtected | Unprotect-CmsMessage -To (Get-GoodCertificateLocation) -IncludeContext
$decryptedNoContext | Should Be "Hello World"
@@ -424,16 +298,16 @@ Import-Certificate $badCertLocation -CertStoreLocation Cert:\CurrentUser\My | %
}
It "Verify Unprotect-CmsMessage treats event logs as a first class citizen" {
$protected = "Encrypted Message1","Encrypted Message2" | Protect-CmsMessage -To $certLocation
$protected = "Encrypted Message1","Encrypted Message2" | Protect-CmsMessage -To (Get-GoodCertificateLocation)
$virtualEventLog = Get-WinEvent Microsoft-Windows-PowerShell/Operational -MaxEvents 1
$savedId = $virtualEventLog.Id
$virtualEventLog.Message = $protected
$expected = "Encrypted Message1" + [System.Environment]::NewLine + "Encrypted Message2"
$decrypted = $virtualEventLog | Unprotect-CmsMessage -To $certLocation
$decrypted = $virtualEventLog | Unprotect-CmsMessage -To (Get-GoodCertificateLocation)
$decrypted | Should Be $expected
$processed = $virtualEventLog | Unprotect-CmsMessage -To $certLocation -IncludeContext
$processed = $virtualEventLog | Unprotect-CmsMessage -To (Get-GoodCertificateLocation) -IncludeContext
$processed.Id | Should Be $savedId
$processed.Message | Should Be $expected
}
@@ -451,8 +325,8 @@ Import-Certificate $badCertLocation -CertStoreLocation Cert:\CurrentUser\My | %
}
It "Verify protect message using OutString" {
$protected = Get-Process -Id $pid | Protect-CmsMessage -To $certLocation
$decrypted = $protected | Unprotect-CmsMessage -To $certLocation
$protected = Get-Process -Id $pid | Protect-CmsMessage -To (Get-GoodCertificateLocation)
$decrypted = $protected | Unprotect-CmsMessage -To (Get-GoodCertificateLocation)
# Should have had PID in output
$decrypted | Should Match $pid
}
@@ -0,0 +1,159 @@
Function New-GoodCertificate
{
$dataEnciphermentCert = "
MIIKYAIBAzCCCiAGCSqGSIb3DQEHAaCCChEEggoNMIIKCTCCBgoGCSqGSIb3DQEHAaCCBfsEggX3
MIIF8zCCBe8GCyqGSIb3DQEMCgECoIIE/jCCBPowHAYKKoZIhvcNAQwBAzAOBAgPOFDMBkCffQIC
B9AEggTYjY55RrmAhdj1grENxXjiPrVNdS++pb5UOn3M7O78BR0U1i2h5zvjkPjOwdLoOCbq5pgg
F0PKaMjHVu8EoZxSqsib17ptR5Rx5N23hseuJUzS8fTAHiBet9payNOJlPfkpuqMfQEmCAo9gAPz
w4RiyZNOA3NhxkfGl9yU4O9GSEr2koWKCUoCNelkIXVbkV728L7zSiWRSqRb7V4QJAtwtgPLTbl/
zo2SFhdNAGPeXbcOsKCv9trhuxPZ0FH4fukbXkHs0I3b5mYgMUI5Mds7UwT3wCtz+Ev9pLbmYN8X
NfH0tAK8ZGnQS1GcI4xCMEM8T9Tx3uwWY4arvRM3GTLwyt8JZEVZNTuYL9A9+RyeiO5d5xEKG8H4
snOCoTriT45tdl8hzMBCdc3jWxWiydmNRw47irifv5BX7BK/6FLAxkMRwACAxNO63ezG/OxuDDEz
ml+KzeZNvr4u4mTBcgZ49vMSyfRt/my+5+iLnSMGp4Rt0uix8489wctkxGlgyXGk23pA4Cj4hq/6
txopcl2gHn+DAFrHIgLg4JR8lcuOzBw8nFOrhK7iR9aMK21apxwImIaDCKJ1grOfbuElq4pkMpov
SltJe00WB94o69LibOg5LqpTrHW2/DY951sIgElF83FdUBhoZHasfCme/RxgliQf3QHedmENXSjr
8R8PAWX4wC0ZZVC0qcq5XP0PkwtuDKmfqq69R32nmBzpRrfypm9S+PfsYZeCeuROh6YKQ0ZBMnLb
8Y9povpXh0lYwVLuPanvAFiCT4vI7oRd1Mg1Zr9ZooMFAVomall1UnQQ29fvsoADjEDcPymVT80o
kTkw3NXnTX6fGZ94Eh0KZcuMgjTqIO3OKpH0lcaSBxlES4V0sO/mwP4ULy8l3dcnn440Nei33VDo
B7n2jhjJl/HvtltfEEEw1DW9AWDvkJDp878sD6VoyQZehvvxBNT0FwMr20TbVKeAGxf9n+xJ9Alo
VUS3qE7XnpxAAAR5L2OG+tMd4dyDSge1qrkVNZ3/uUzKKCZei2P7ICR9cX1FRsmcINdNfydIA2cA
Pmeq+UkRdqsJxt1NSK5bLvMM4EHRQZMMbXVKxxJ+kQDrzfQtERFPyd3Hdm2F4T/JXUQ+PrTQnRqY
LruTAiZfxygZuFrxJnNTRydRdbEaTAtMjFCMRFZ2wctJsgCb3yN9tt0JDYxIvm0MSehXiF+sCrl4
yZvvUzJqrgppHRTBR4Sao+MZ/rJ30vVU19Q3oBi9ikTqDY+4SrHsp5Y4llnsbrz0Web+h2jLvyJz
LgKuqs87qHhToVMLuULy/HLqY3m661EMwNqh5D76gSFI+TP2/rzT5mVOGglahFoc848o4cshtPWE
9MjAkDfsMbIfeKH3uh3D+eBIxYmZ5Cq2aHzqdQ0pU/nDNX7BDjC3E80VcQnXx4U6tRsQHsGtbcld
MFTp0yHJ2KLkz+inH3WPy/lYuVZ0QJe+LqvGt+bt1DgQmLBMD9WLFML3d0TtkuY3RhD5Y0wr2zt9
tT6WVTn8Hob1cJns4N7tDEr8Q3TdIar0I5Bzj3qoesJt+4lxwnVdUA1bNJ2zxXIkDfX/MTB464FI
2g9uhUs3lIOEiCjeJCwBebgZa1HlfhyCRu0E7fnNnKLaGWRs8LVy7MZIfe1kJoDVgTGB3TATBgkq
hkiG9w0BCRUxBgQEAQAAADBdBgkrBgEEAYI3EQExUB5OAE0AaQBjAHIAbwBzAG8AZgB0ACAAUwB0
AHIAbwBuAGcAIABDAHIAeQBwAHQAbwBnAHIAYQBwAGgAaQBjACAAUAByAG8AdgBpAGQAZQByMGcG
CSqGSIb3DQEJFDFaHlgAQwBlAHIAdABSAGUAcQAtAGEAYgA5AGUAZgA3AGYAOAAtAGUAYwA2AGEA
LQA0ADUAMwA4AC0AOQA4AGQAOAAtADAAYQA4AGUAYwA4ADUAOQBkADkAMgAxMIID9wYJKoZIhvcN
AQcGoIID6DCCA+QCAQAwggPdBgkqhkiG9w0BBwEwHAYKKoZIhvcNAQwBBjAOBAiYL6rZmAGN9QIC
B9CAggOw8eaNgIqx26SlOBKKQZ5O7NDZQHbytHTWn4ifNhVFUkbuaj22/VnYOFB9//8BLY6t0Dvw
X6wqXSMnbr1jOuUYaFdJzOBZBsYQfFTfoJ4iOb2jwwIpZSgTHeqgXbvnI7MIxauu6F4UseWVxt3u
ZhHjEZQjKWeC5mNCb6wX0IOQk96n1RJnJ3v1D4Q5YrVekOVq70VhRNtLOZMkrJV7vDMNlUYXD69D
PbcyPajVvETq0W98YNKB89oNwFWuKoMLNPWmSwIfn10oSEtybNEEr2IVgCBt2w2eb+nIDuA3c/Rc
qKPXwVGMzoUyAiGwTcueCdMRmiuQAuKCUyi9P/JqeIbgHtg15nAoGtw+l4MsFXfdMJjTCDd0WYff
l9ipaNnw8erCPquD0+wXeMnNivXaFzu2+CGwCSwbDl2M49HAQdtpKhNj5jKJBEP1GRQIk173gbEZ
n69IXUCsf0GDZiZVNbAQHBOuRoEHpBhendFgTJFAU2LDHlmV6OA0LYHaSn7CP/vOXOhWXJ1yGL2p
SeUepciwQV7sOHqDExWY82fd1kHSHcgCAkWLSSdIPlWhyeqjC1agSP6b74VK8uLRPkin5F9wGIPi
ewe/LsW3PTtDkDnj3DiSioKlQRUUxVxzi5qPBs+7vJEhbuO7UhtsMCWeUygDbw6n8BKan4w9iLhx
7/z6zVvQmLnK4HZChTPFuThRy1NctupoX7nE+CDgyhcmryaTDXohkviMWl4Od+8uGh8Quv2bHk6Y
UnFqNB/hSqYMkTuMLH4F9sVzoQEsYu96CDwbQaggbLMnPtmHKsPtzdnWQnys+oGT4uD8vl9xFdEW
AZdestrxbDK0La0AgGszUE+6B/GtOs2pv0fMXXYV2h+dAlwfz7oLxzm9E+SFgzviL+6PuI9fDHNd
pWeq/Rr5OpFb3rSotGTl84aIjk3hPd3uHujPQh8GO2EQ5k6p6ukk+a7gOUB+pH8fHihFl5L7pI0z
yRp0FvbZo//hmACYMvINoy2EQxjYLh7QLeE4qEr8bkzJVgEURUvcUpyHFJT6PGzUMqGx/Wjh2jJc
HfEDPMUDoTE/QRzLW7XrmQgJIRuHgPI/cqmOyvpEvuwdRhYyHEKktRO3tGjeflohDCyDW9bxOaJV
ZP64KBordM28ZHCQbnSdU0I5us6qiFX2PiLlBzRMH2ftUNMYReioqZyR+Xv5wjaoydV3//BDMH8M
1lh9GazUO8+OtzQEH0jiBi6ctlzFT8nNI2C+cOB9S3yMAjCEQa8wNzAfMAcGBSsOAwIaBBR96vF2
OksttXT1kXf+aez9EzDlsgQU4ck78h0WTy01zHLwSKNWK4wFFQM=
"
$dataEnciphermentCert = $dataEnciphermentCert -replace '\s',''
$certBytes = [Convert]::FromBase64String($dataEnciphermentCert)
$certLocation = Join-Path $TestDrive "ProtectedEventLogging.pfx"
[IO.File]::WriteAllBytes($certLocation, $certBytes)
return $certLocation
}
Function New-BadCertificate
{
$codeSigningCert = "
MIIDAjCCAeqgAwIBAgIQW/oHcNaftoFGOYb4w5A0JTANBgkqhkiG9w0BAQsFADAZMRcwFQYDVQQD
DA5DTVNUZXN0QmFkQ2VydDAeFw0xNzAzMDcwNjEyMDNaFw0xODAzMDcwNjMyMDNaMBkxFzAVBgNV
BAMMDkNNU1Rlc3RCYWRDZXJ0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAogOnCBPp
xCQXvCJOe4KUXrTL5hwzKSV/mA4pF/kWCVLseWkeTzll+02S0SzMR1oqyxGZOU+KiakmH8sIxDpS
pBpYi3R+JtaUYeK7IwvM7yMgxzYbVUFupNXDIdFcgd7FwX4R9wJGwd/hEw5fe+ua96G6bBlfQC8j
I8iHfqHZ2GmssIuSt72WhT6tKZhPJIMjwmKaB8j/gm6EC7eH83wNmVW/ss2AsG5cMT0Zmk2vkXPd
7rVYbAh9WcvxYzTYZLsPkXx/s6uanLo7pBPMqQ8fgImSXiD5EBO9d6SzqoagoAkH/l3oKCUztsqU
PAfTu1aTAYRW5O26AcICTbIYOMkDMQIDAQABo0YwRDAOBgNVHQ8BAf8EBAMCB4AwEwYDVR0lBAww
CgYIKwYBBQUHAwMwHQYDVR0OBBYEFLSLHDqLWoDBj0j/UavIf0hAHZ2YMA0GCSqGSIb3DQEBCwUA
A4IBAQB7GJ3ykI07k2D1mfiQ10+Xse4b6KXylbzYfJ1k3K0NEBwT7H/lhMu4yz95A7pXU41yKKVE
OzmpX8QGyczRM269+WpUvVOXwudQL7s/JFeZyEhxPYRP0JC8U9rur1iJeULvsPZJU2kGeLceTl7k
psuZeHouYeNuFeeKR66GcHKzqm+5odAJBxjQ/iGP+CVfNVX56Abhu8mXX6sFiorrBSV/NzPThqja
mtsMC3Fq53xANMjFT4kUqMtK+oehPf0+0jHHra4hpCVZ2KoPLLPxpJPko8hUO5LxATLU+UII7w3c
nMbw+XY4C8xdDnHfS6mF+Hol98dURB/MC/x3sZ3gSjKo
"
$codeSigningCert = $codeSigningCert -replace '\s',''
$certBytes = [Convert]::FromBase64String($codeSigningCert)
$certLocation = Join-Path $TestDrive "CMSTestBadCert"
[IO.File]::WriteAllBytes($certLocation, $certBytes)
return $certLocation
}
function Install-TestCertificates
{
$script:certLocation = New-GoodCertificate
$script:certLocation | Should Not BeNullOrEmpty | Out-Null
$script:badCertLocation = New-BadCertificate
$script:badCertLocation | Should Not BeNullOrEmpty | Out-Null
if ($IsCoreCLR -and $IsWindows)
{
# PKI module is not available for PowerShell Core, so we need to use Windows PowerShell to import the cert
$fullPowerShell = Join-Path "$env:SystemRoot" "System32\WindowsPowerShell\v1.0\powershell.exe"
try {
$modulePathCopy = $env:PSModulePath
$env:PSModulePath = $null
$command = @"
Import-PfxCertificate $script:certLocation -CertStoreLocation cert:\CurrentUser\My | % PSPath
Import-Certificate $script:badCertLocation -CertStoreLocation Cert:\CurrentUser\My | % PSPath
"@
$certPaths = & $fullPowerShell -NoProfile -NonInteractive -Command $command
$certPaths.Count | Should Be 2 | Out-Null
$script:importedCert = Get-ChildItem $certPaths[0]
$script:testBadCert = Get-ChildItem $certPaths[1]
} finally {
$env:PSModulePath = $modulePathCopy
}
}
elseif($IsWindows)
{
$script:importedCert = Import-PfxCertificate $script:certLocation -CertStoreLocation cert:\CurrentUser\My
$script:testBadCert = Import-Certificate $script:badCertLocation -CertStoreLocation Cert:\CurrentUser\My
}
else {
throw 'Not supported on non-windows platforms'
}
}
function Get-GoodCertificateLocation
{
return $script:certLocation
}
function Get-GoodCertificateObject
{
return $script:importedCert
}
function Get-BadCertificateObject
{
return $script:testBadCert
}
function Remove-TestCertificates
{
if($IsWindows)
{
if ($script:importedCert)
{
Remove-Item (Join-Path Cert:\CurrentUser\My $script:importedCert.Thumbprint) -Force -ErrorAction SilentlyContinue
}
if ($script:testBadCert)
{
Remove-Item (Join-Path Cert:\CurrentUser\My $script:testBadCert.Thumbprint) -Force -ErrorAction SilentlyContinue
}
}
else {
throw 'Not supported on non-windows platforms'
}
}