diff --git a/src/Microsoft.PowerShell.Security/security/CertificateProvider.cs b/src/Microsoft.PowerShell.Security/security/CertificateProvider.cs index 1449afbedcb..b8bcee8bd4b 100644 --- a/src/Microsoft.PowerShell.Security/security/CertificateProvider.cs +++ b/src/Microsoft.PowerShell.Security/security/CertificateProvider.cs @@ -15,6 +15,7 @@ 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 @@ public override string ToString() } } - /// - /// 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. - /// - /// - - internal sealed class CertificateProviderDynamicParameters - { - /// - /// switch that controls whether we only return - /// code signing certs. - /// - [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(); - - /// - /// switch that controls whether we only return - /// data encipherment certs. - /// - [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(); - - /// - /// switch that controls whether we only return - /// code signing certs. - /// - [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(); - - /// - /// 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*" - /// - [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; - - /// - /// 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. - /// - [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; - - /// - /// 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). - /// - [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; - } - /// /// Defines the Certificate Provider remove-item dynamic parameters. /// @@ -351,42 +224,6 @@ public IntPtr Handle } } - /// - /// Defines the safe handle class for native cert store handles, - /// HCERTSTORE. - /// - 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; } - } - } - /// /// Defines the Certificate Provider store handle class /// @@ -477,25 +314,8 @@ public void Open(bool includeArchivedCerts) } } - 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 @@ public IntPtr GetNextCert(IntPtr certContext) } 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 @@ public bool Valid 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 @@ protected override bool HasChildItems(string path) 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 @@ private void RemoveCertStore(string storeName, bool fDeleteKey, string sourcePat // // 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 @@ private void DoRemove(X509Certificate2 cert, bool fDeleteKey, bool fMachine, str 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 @@ private void DoMove(string destination, X509Certificate2 cert, X509NativeStore s 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 @@ protected override bool IsItemContainer(string path) /// protected override object GetItemDynamicParameters(string path) { - if (DownLevelHelper.NativeFilteringSupported()) - { - return new CertificateProviderDynamicParameters(); - } - else - { - return new CertificateProviderCodeSigningDynamicParameters(); - } + return new CertificateProviderCodeSigningDynamicParameters(); } /// @@ -2531,14 +2319,7 @@ protected override object GetItemDynamicParameters(string path) /// 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 @@ private void GetCertificatesOrNames(string path, // // 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 @@ private CertificateFilterInfo GetFilter() 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 @@ public List EnhancedKeyUsageList /// public EnhancedKeyUsageProperty(X509Certificate2 cert) { - if (DownLevelHelper.NativeFilteringSupported()) + foreach (X509Extension extension in cert.Extensions) { - Collection 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(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 @@ public EnhancedKeyUsageProperty(X509Certificate2 cert) public sealed class DnsNameProperty { private List _dnsList = new List(); + private System.Globalization.IdnMapping idnMapping = new System.Globalization.IdnMapping(); + private const string dnsNamePrefix = "DNS Name="; + private const string distinguishedNamePrefix = "CN="; /// /// get property of DnsNameList @@ -3598,81 +3321,65 @@ public List DnsNameList /// public DnsNameProperty(X509Certificate2 cert) { - if (DownLevelHelper.NativeFilteringSupported()) + string name; + string unicodeName; + DnsNameRepresentation dnsName; + _dnsList = new List(); + + // 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) + name = cert.Subject.Substring(distinguishedNamePrefix.Length); + try { - //need to get subject alternative name from the certificate context - _dnsList = GetCertNames( - cert.Handle, - Security.NativeMethods.AltNameType.CERT_ALT_NAME_DNS_NAME); + unicodeName = idnMapping.GetUnicode(name); } - } - } - - // Wrapper function for CCGetCertNameList and CCFreeStringArray - private List GetCertNames(IntPtr certHandle, Security.NativeMethods.AltNameType nameType) - { - DWORD cPunycodeName; - IntPtr papwszPunycodeNames = IntPtr.Zero; - IntPtr papwszUnicodeNames = IntPtr.Zero; - List names = new List(); - 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) + catch(System.ArgumentException) { - if (hr != Security.NativeMethods.CRYPT_E_NOT_FOUND) - { - throw Marshal.GetExceptionForHR(hr); - } - cPunycodeName = 0; + // The name is not valid punyCode, assume it's valid ascii. + unicodeName = name; } - try + 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") { - if (0 < cPunycodeName) + string[] names = extension.Format(true).Split(Environment.NewLine); + foreach(string nameLine in names) { - DWORD cUnicodeName; - - hr = Security.NativeMethods.CCGetCertNameList( - certHandle, - nameType, - Security.NativeMethods.CryptDecodeFlags.CRYPT_DECODE_ENABLE_IA5CONVERSION_FLAG, - out cUnicodeName, - out papwszUnicodeNames); - if (hr != Security.NativeConstants.S_OK) - { - throw Marshal.GetExceptionForHR(hr); - } - if (cPunycodeName != cUnicodeName) + // Get the part after 'DNS Name=' + if(nameLine.StartsWith(dnsNamePrefix, System.StringComparison.InvariantCultureIgnoreCase)) { - 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 @@ internal static bool IsWin8AndAbove() { 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 @@ internal static bool IsWin8AndAbove() { 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(); diff --git a/src/System.Management.Automation/security/nativeMethods.cs b/src/System.Management.Automation/security/nativeMethods.cs index 0c26ee17145..6bcd9ee48b1 100644 --- a/src/System.Management.Automation/security/nativeMethods.cs +++ b/src/System.Management.Automation/security/nativeMethods.cs @@ -1453,59 +1453,6 @@ internal enum CryptDecodeFlags : uint 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 diff --git a/test/powershell/Modules/Microsoft.PowerShell.Security/CertificateProvider.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Security/CertificateProvider.Tests.ps1 new file mode 100644 index 00000000000..c9e84f5c99a --- /dev/null +++ b/test/powershell/Modules/Microsoft.PowerShell.Security/CertificateProvider.Tests.ps1 @@ -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: " -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: " -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: " -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: " -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 + } + } +} \ No newline at end of file diff --git a/test/powershell/Modules/Microsoft.PowerShell.Security/CmsMessage.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Security/CmsMessage.Tests.ps1 index 2e021e250c8..e2fb9ddb803 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Security/CmsMessage.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Security/CmsMessage.Tests.ps1 @@ -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 } diff --git a/test/powershell/Modules/Microsoft.PowerShell.Security/certificateCommon.psm1 b/test/powershell/Modules/Microsoft.PowerShell.Security/certificateCommon.psm1 new file mode 100644 index 00000000000..53dbb5d8ec0 --- /dev/null +++ b/test/powershell/Modules/Microsoft.PowerShell.Security/certificateCommon.psm1 @@ -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' + } +} \ No newline at end of file