private void ProcessResolvedCertificates(ResolutionPurpose purpose, List<X509Certificate2> certificatesToProcess, out ErrorRecord error) { error = null; HashSet<String> processedThumbprints = new HashSet<string>(); foreach (X509Certificate2 certificate in certificatesToProcess) { if (!SecuritySupport.CertIsGoodForEncryption(certificate)) { // If they specified a specific cert, generate an error if it isn't good // for encryption. if (!WildcardPattern.ContainsWildcardCharacters(_identifier)) { error = new ErrorRecord( new ArgumentException( String.Format(CultureInfo.InvariantCulture, SecuritySupportStrings.CertificateCannotBeUsedForEncryption, certificate.Thumbprint, CertificateFilterInfo.DocumentEncryptionOid)), "CertificateCannotBeUsedForEncryption", ErrorCategory.InvalidData, certificate); return; } else { continue; } } // When decrypting, only look for certs that have the private key if (purpose == ResolutionPurpose.Decryption) { if (!certificate.HasPrivateKey) { continue; } } if (processedThumbprints.Contains(certificate.Thumbprint)) { continue; } else { processedThumbprints.Add(certificate.Thumbprint); } if (purpose == ResolutionPurpose.Encryption) { // Only let wildcards expand to one recipient. Otherwise, data // may be encrypted to the wrong person on accident. if (Certificates.Count > 0) { error = new ErrorRecord( new ArgumentException( String.Format(CultureInfo.InvariantCulture, SecuritySupportStrings.IdentifierMustReferenceSingleCertificate, _identifier, "To")), "IdentifierMustReferenceSingleCertificate", ErrorCategory.LimitsExceeded, certificatesToProcess); Certificates.Clear(); return; } } Certificates.Add(certificate); } }
private void ResolveFromSubjectName(SessionState sessionState, ResolutionPurpose purpose, out ErrorRecord error) { Collection<PSObject> certificates = new Collection<PSObject>(); WildcardPattern subjectNamePattern = WildcardPattern.Get(_identifier, WildcardOptions.IgnoreCase); try { // Get first from 'My' store, then 'LocalMachine' string[] certificatePaths = new string[] { "Microsoft.PowerShell.Security\\Certificate::CurrentUser\\My", "Microsoft.PowerShell.Security\\Certificate::LocalMachine\\My" }; foreach (string certificatePath in certificatePaths) { foreach (PSObject certificateObject in sessionState.InvokeProvider.ChildItem.Get(certificatePath, false)) { if (subjectNamePattern.IsMatch(certificateObject.Properties["Subject"].Value.ToString())) { certificates.Add(certificateObject); } } } } catch (SessionStateException) { // If we got an ItemNotFound / etc., then this didn't represent a valid path. } List<X509Certificate2> certificatesToProcess = new List<X509Certificate2>(); foreach (PSObject certificateObject in certificates) { X509Certificate2 certificate = certificateObject.BaseObject as X509Certificate2; if (certificate != null) { certificatesToProcess.Add(certificate); } } ProcessResolvedCertificates(purpose, certificatesToProcess, out error); }
private void ResolveFromThumbprint(SessionState sessionState, ResolutionPurpose purpose, out ErrorRecord error) { // Quickly check that this is a thumbprint-like pattern (just hex) if (!System.Text.RegularExpressions.Regex.IsMatch(_identifier, "^[a-f0-9]+$", Text.RegularExpressions.RegexOptions.IgnoreCase)) { error = null; return; } Collection<PSObject> certificates = new Collection<PSObject>(); try { // Get first from 'My' store string certificatePath = sessionState.Path.Combine("Microsoft.PowerShell.Security\\Certificate::CurrentUser\\My", _identifier); if (sessionState.InvokeProvider.Item.Exists(certificatePath)) { foreach (PSObject certificateObject in sessionState.InvokeProvider.Item.Get(certificatePath)) { certificates.Add(certificateObject); } } // Second from 'LocalMachine' store certificatePath = sessionState.Path.Combine("Microsoft.PowerShell.Security\\Certificate::LocalMachine\\My", _identifier); if (sessionState.InvokeProvider.Item.Exists(certificatePath)) { foreach (PSObject certificateObject in sessionState.InvokeProvider.Item.Get(certificatePath)) { certificates.Add(certificateObject); } } } catch (SessionStateException) { // If we got an ItemNotFound / etc., then this didn't represent a valid path. } List<X509Certificate2> certificatesToProcess = new List<X509Certificate2>(); foreach (PSObject certificateObject in certificates) { X509Certificate2 certificate = certificateObject.BaseObject as X509Certificate2; if (certificate != null) { certificatesToProcess.Add(certificate); } } ProcessResolvedCertificates(purpose, certificatesToProcess, out error); }
private void ResolveFromPath(SessionState sessionState, ResolutionPurpose purpose, out ErrorRecord error) { error = null; ProviderInfo pathProvider = null; Collection<string> resolvedPaths = null; try { resolvedPaths = sessionState.Path.GetResolvedProviderPathFromPSPath(_identifier, out pathProvider); } catch (SessionStateException) { // If we got an ItemNotFound / etc., then this didn't represent a valid path. } // If we got a resolved path, try to load certs from that path. if ((resolvedPaths != null) && (resolvedPaths.Count != 0)) { // Ensure the path is from the file system provider if (!String.Equals(pathProvider.Name, "FileSystem", StringComparison.OrdinalIgnoreCase)) { error = new ErrorRecord( new ArgumentException( String.Format(CultureInfo.InvariantCulture, SecuritySupportStrings.CertificatePathMustBeFileSystemPath, _identifier)), "CertificatePathMustBeFileSystemPath", ErrorCategory.ObjectNotFound, pathProvider); return; } // If this is a directory, add all certificates in it. This will be the primary // scenario for decryption via Group Protected PFX files // (http://social.technet.microsoft.com/wiki/contents/articles/13922.certificate-pfx-export-and-import-using-ad-ds-account-protection.aspx) List<string> pathsToAdd = new List<string>(); List<string> pathsToRemove = new List<string>(); foreach (string resolvedPath in resolvedPaths) { if (System.IO.Directory.Exists(resolvedPath)) { // It would be nice to limit this to *.pfx, *.cer, etc., but // the crypto APIs support extracting certificates from arbitrary file types. pathsToAdd.AddRange(System.IO.Directory.GetFiles(resolvedPath)); pathsToRemove.Add(resolvedPath); } } // Update resolved paths foreach (string path in pathsToAdd) { resolvedPaths.Add(path); } foreach (string path in pathsToRemove) { resolvedPaths.Remove(path); } List<X509Certificate2> certificatesToProcess = new List<X509Certificate2>(); foreach (string path in resolvedPaths) { X509Certificate2 certificate = null; try { certificate = new X509Certificate2(path); } catch (Exception e) { // User call-out, catch-all OK CommandProcessorBase.CheckForSevereException(e); continue; } certificatesToProcess.Add(certificate); } ProcessResolvedCertificates(purpose, certificatesToProcess, out error); } }
private void ResolveFromBase64Encoding(ResolutionPurpose purpose, out ErrorRecord error) { error = null; int startIndex, endIndex; byte[] messageBytes = null; try { messageBytes = CmsUtils.RemoveAsciiArmor(_identifier, CmsUtils.BEGIN_CERTIFICATE_SIGIL, CmsUtils.END_CERTIFICATE_SIGIL, out startIndex, out endIndex); } catch (FormatException) { // Not Base-64 encoded return; } // Didn't have the sigil if (messageBytes == null) { return; } List<X509Certificate2> certificatesToProcess = new List<X509Certificate2>(); ; try { X509Certificate2 newCertificate = new X509Certificate2(messageBytes); certificatesToProcess.Add(newCertificate); } catch (Exception e) { // User call-out, catch-all OK CommandProcessorBase.CheckForSevereException(e); // Wasn't certificate data return; } // Now validate the certificate ProcessResolvedCertificates(purpose, certificatesToProcess, out error); }
/// <summary> /// Resolves the provided identifier into a collection of certificates. /// </summary> /// <param name="sessionState">A reference to an instance of Powershell's SessionState class.</param> /// <param name="purpose">The purpose for which this identifier is being resolved (Encryption / Decryption.</param> /// <param name="error">The error generated (if any) for this resolution.</param> public void Resolve(SessionState sessionState, ResolutionPurpose purpose, out ErrorRecord error) { error = null; // Process the certificate if that was supplied exactly if (_pendingCertificate != null) { ProcessResolvedCertificates(purpose, new List<X509Certificate2> { _pendingCertificate }, out error); if ((error != null) || (Certificates.Count != 0)) { return; } } if (_identifier != null) { // First try to resolve assuming that the cert was Base64 encoded. ResolveFromBase64Encoding(purpose, out error); if ((error != null) || (Certificates.Count != 0)) { return; } // Then try to resolve by path. ResolveFromPath(sessionState, purpose, out error); if ((error != null) || (Certificates.Count != 0)) { return; } // Then by thumbprint ResolveFromThumbprint(sessionState, purpose, out error); if ((error != null) || (Certificates.Count != 0)) { return; } // Then by Subject Name ResolveFromSubjectName(sessionState, purpose, out error); if ((error != null) || (Certificates.Count != 0)) { return; } } // Generate an error if no cert was found (and this is an encryption attempt). // If it is only decryption, then the system will always look in the 'My' store anyways, so // don't generate an error if they used wildcards. If they did not use wildcards, // then generate an error because they were expecting something specific. if ((purpose == ResolutionPurpose.Encryption) || (!WildcardPattern.ContainsWildcardCharacters(_identifier))) { error = new ErrorRecord( new ArgumentException( String.Format(CultureInfo.InvariantCulture, SecuritySupportStrings.NoCertificateFound, _identifier)), "NoCertificateFound", ErrorCategory.ObjectNotFound, _identifier); } return; }