1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4 
5 using Microsoft.Win32;
6 using System;
7 using System.Collections;
8 using System.Diagnostics;
9 using System.Globalization;
10 using System.IO;
11 using System.Security;
12 using System.Security.Cryptography;
13 using System.Security.Cryptography.X509Certificates;
14 using System.Security.Permissions;
15 using System.Text;
16 using System.Threading;
17 using System.Xml;
18 
19 namespace System.Security.Cryptography.Xml
20 {
21     internal class Utils
22     {
23         // The maximum number of characters in an XML document (0 means no limit).
24         internal const int MaxCharactersInDocument = 0;
25 
26         // The entity expansion limit. This is used to prevent entity expansion denial of service attacks.
27         internal const long MaxCharactersFromEntities = (long)1e7;
28 
29         // The default XML Dsig recursion limit.
30         // This should be within limits of real world scenarios.
31         // Keeping this number low will preserve some stack space
32         internal const int XmlDsigSearchDepth = 20;
33 
Utils()34         private Utils() { }
35 
HasNamespace(XmlElement element, string prefix, string value)36         private static bool HasNamespace(XmlElement element, string prefix, string value)
37         {
38             if (IsCommittedNamespace(element, prefix, value)) return true;
39             if (element.Prefix == prefix && element.NamespaceURI == value) return true;
40             return false;
41         }
42 
43         // A helper function that determines if a namespace node is a committed attribute
IsCommittedNamespace(XmlElement element, string prefix, string value)44         internal static bool IsCommittedNamespace(XmlElement element, string prefix, string value)
45         {
46             if (element == null)
47                 throw new ArgumentNullException(nameof(element));
48 
49             string name = ((prefix.Length > 0) ? "xmlns:" + prefix : "xmlns");
50             if (element.HasAttribute(name) && element.GetAttribute(name) == value) return true;
51             return false;
52         }
53 
IsRedundantNamespace(XmlElement element, string prefix, string value)54         internal static bool IsRedundantNamespace(XmlElement element, string prefix, string value)
55         {
56             if (element == null)
57                 throw new ArgumentNullException(nameof(element));
58 
59             XmlNode ancestorNode = ((XmlNode)element).ParentNode;
60             while (ancestorNode != null)
61             {
62                 XmlElement ancestorElement = ancestorNode as XmlElement;
63                 if (ancestorElement != null)
64                     if (HasNamespace(ancestorElement, prefix, value)) return true;
65                 ancestorNode = ancestorNode.ParentNode;
66             }
67 
68             return false;
69         }
70 
GetAttribute(XmlElement element, string localName, string namespaceURI)71         internal static string GetAttribute(XmlElement element, string localName, string namespaceURI)
72         {
73             string s = (element.HasAttribute(localName) ? element.GetAttribute(localName) : null);
74             if (s == null && element.HasAttribute(localName, namespaceURI))
75                 s = element.GetAttribute(localName, namespaceURI);
76             return s;
77         }
78 
HasAttribute(XmlElement element, string localName, string namespaceURI)79         internal static bool HasAttribute(XmlElement element, string localName, string namespaceURI)
80         {
81             return element.HasAttribute(localName) || element.HasAttribute(localName, namespaceURI);
82         }
83 
IsNamespaceNode(XmlNode n)84         internal static bool IsNamespaceNode(XmlNode n)
85         {
86             return n.NodeType == XmlNodeType.Attribute && (n.Prefix.Equals("xmlns") || (n.Prefix.Length == 0 && n.LocalName.Equals("xmlns")));
87         }
88 
IsXmlNamespaceNode(XmlNode n)89         internal static bool IsXmlNamespaceNode(XmlNode n)
90         {
91             return n.NodeType == XmlNodeType.Attribute && n.Prefix.Equals("xml");
92         }
93 
94         // We consider xml:space style attributes as default namespace nodes since they obey the same propagation rules
IsDefaultNamespaceNode(XmlNode n)95         internal static bool IsDefaultNamespaceNode(XmlNode n)
96         {
97             bool b1 = n.NodeType == XmlNodeType.Attribute && n.Prefix.Length == 0 && n.LocalName.Equals("xmlns");
98             bool b2 = IsXmlNamespaceNode(n);
99             return b1 || b2;
100         }
101 
IsEmptyDefaultNamespaceNode(XmlNode n)102         internal static bool IsEmptyDefaultNamespaceNode(XmlNode n)
103         {
104             return IsDefaultNamespaceNode(n) && n.Value.Length == 0;
105         }
106 
GetNamespacePrefix(XmlAttribute a)107         internal static string GetNamespacePrefix(XmlAttribute a)
108         {
109             Debug.Assert(IsNamespaceNode(a) || IsXmlNamespaceNode(a));
110             return a.Prefix.Length == 0 ? string.Empty : a.LocalName;
111         }
112 
HasNamespacePrefix(XmlAttribute a, string nsPrefix)113         internal static bool HasNamespacePrefix(XmlAttribute a, string nsPrefix)
114         {
115             return GetNamespacePrefix(a).Equals(nsPrefix);
116         }
117 
IsNonRedundantNamespaceDecl(XmlAttribute a, XmlAttribute nearestAncestorWithSamePrefix)118         internal static bool IsNonRedundantNamespaceDecl(XmlAttribute a, XmlAttribute nearestAncestorWithSamePrefix)
119         {
120             if (nearestAncestorWithSamePrefix == null)
121                 return !IsEmptyDefaultNamespaceNode(a);
122             else
123                 return !nearestAncestorWithSamePrefix.Value.Equals(a.Value);
124         }
125 
IsXmlPrefixDefinitionNode(XmlAttribute a)126         internal static bool IsXmlPrefixDefinitionNode(XmlAttribute a)
127         {
128             return false;
129             //            return a.Prefix.Equals("xmlns") && a.LocalName.Equals("xml") && a.Value.Equals(NamespaceUrlForXmlPrefix);
130         }
131 
DiscardWhiteSpaces(string inputBuffer)132         internal static string DiscardWhiteSpaces(string inputBuffer)
133         {
134             return DiscardWhiteSpaces(inputBuffer, 0, inputBuffer.Length);
135         }
136 
137 
DiscardWhiteSpaces(string inputBuffer, int inputOffset, int inputCount)138         internal static string DiscardWhiteSpaces(string inputBuffer, int inputOffset, int inputCount)
139         {
140             int i, iCount = 0;
141             for (i = 0; i < inputCount; i++)
142                 if (Char.IsWhiteSpace(inputBuffer[inputOffset + i])) iCount++;
143             char[] rgbOut = new char[inputCount - iCount];
144             iCount = 0;
145             for (i = 0; i < inputCount; i++)
146                 if (!Char.IsWhiteSpace(inputBuffer[inputOffset + i]))
147                 {
148                     rgbOut[iCount++] = inputBuffer[inputOffset + i];
149                 }
150             return new string(rgbOut);
151         }
152 
SBReplaceCharWithString(StringBuilder sb, char oldChar, string newString)153         internal static void SBReplaceCharWithString(StringBuilder sb, char oldChar, string newString)
154         {
155             int i = 0;
156             int newStringLength = newString.Length;
157             while (i < sb.Length)
158             {
159                 if (sb[i] == oldChar)
160                 {
161                     sb.Remove(i, 1);
162                     sb.Insert(i, newString);
163                     i += newStringLength;
164                 }
165                 else i++;
166             }
167         }
168 
PreProcessStreamInput(Stream inputStream, XmlResolver xmlResolver, string baseUri)169         internal static XmlReader PreProcessStreamInput(Stream inputStream, XmlResolver xmlResolver, string baseUri)
170         {
171             XmlReaderSettings settings = GetSecureXmlReaderSettings(xmlResolver);
172             XmlReader reader = XmlReader.Create(inputStream, settings, baseUri);
173             return reader;
174         }
175 
GetSecureXmlReaderSettings(XmlResolver xmlResolver)176         internal static XmlReaderSettings GetSecureXmlReaderSettings(XmlResolver xmlResolver)
177         {
178             XmlReaderSettings settings = new XmlReaderSettings();
179             settings.XmlResolver = xmlResolver;
180             settings.DtdProcessing = DtdProcessing.Parse;
181             settings.MaxCharactersFromEntities = MaxCharactersFromEntities;
182             settings.MaxCharactersInDocument = MaxCharactersInDocument;
183             return settings;
184         }
185 
PreProcessDocumentInput(XmlDocument document, XmlResolver xmlResolver, string baseUri)186         internal static XmlDocument PreProcessDocumentInput(XmlDocument document, XmlResolver xmlResolver, string baseUri)
187         {
188             if (document == null)
189                 throw new ArgumentNullException(nameof(document));
190 
191             MyXmlDocument doc = new MyXmlDocument();
192             doc.PreserveWhitespace = document.PreserveWhitespace;
193 
194             // Normalize the document
195             using (TextReader stringReader = new StringReader(document.OuterXml))
196             {
197                 XmlReaderSettings settings = new XmlReaderSettings();
198                 settings.XmlResolver = xmlResolver;
199                 settings.DtdProcessing = DtdProcessing.Parse;
200                 settings.MaxCharactersFromEntities = MaxCharactersFromEntities;
201                 settings.MaxCharactersInDocument = MaxCharactersInDocument;
202                 XmlReader reader = XmlReader.Create(stringReader, settings, baseUri);
203                 doc.Load(reader);
204             }
205             return doc;
206         }
207 
PreProcessElementInput(XmlElement elem, XmlResolver xmlResolver, string baseUri)208         internal static XmlDocument PreProcessElementInput(XmlElement elem, XmlResolver xmlResolver, string baseUri)
209         {
210             if (elem == null)
211                 throw new ArgumentNullException(nameof(elem));
212 
213             MyXmlDocument doc = new MyXmlDocument();
214             doc.PreserveWhitespace = true;
215             // Normalize the document
216             using (TextReader stringReader = new StringReader(elem.OuterXml))
217             {
218                 XmlReaderSettings settings = new XmlReaderSettings();
219                 settings.XmlResolver = xmlResolver;
220                 settings.DtdProcessing = DtdProcessing.Parse;
221                 settings.MaxCharactersFromEntities = MaxCharactersFromEntities;
222                 settings.MaxCharactersInDocument = MaxCharactersInDocument;
223                 XmlReader reader = XmlReader.Create(stringReader, settings, baseUri);
224                 doc.Load(reader);
225             }
226             return doc;
227         }
228 
DiscardComments(XmlDocument document)229         internal static XmlDocument DiscardComments(XmlDocument document)
230         {
231             XmlNodeList nodeList = document.SelectNodes("//comment()");
232             if (nodeList != null)
233             {
234                 foreach (XmlNode node1 in nodeList)
235                 {
236                     node1.ParentNode.RemoveChild(node1);
237                 }
238             }
239             return document;
240         }
241 
AllDescendantNodes(XmlNode node, bool includeComments)242         internal static XmlNodeList AllDescendantNodes(XmlNode node, bool includeComments)
243         {
244             CanonicalXmlNodeList nodeList = new CanonicalXmlNodeList();
245             CanonicalXmlNodeList elementList = new CanonicalXmlNodeList();
246             CanonicalXmlNodeList attribList = new CanonicalXmlNodeList();
247             CanonicalXmlNodeList namespaceList = new CanonicalXmlNodeList();
248 
249             int index = 0;
250             elementList.Add(node);
251 
252             do
253             {
254                 XmlNode rootNode = (XmlNode)elementList[index];
255                 // Add the children nodes
256                 XmlNodeList childNodes = rootNode.ChildNodes;
257                 if (childNodes != null)
258                 {
259                     foreach (XmlNode node1 in childNodes)
260                     {
261                         if (includeComments || (!(node1 is XmlComment)))
262                         {
263                             elementList.Add(node1);
264                         }
265                     }
266                 }
267                 // Add the attribute nodes
268                 XmlAttributeCollection attribNodes = rootNode.Attributes;
269                 if (attribNodes != null)
270                 {
271                     foreach (XmlNode attribNode in rootNode.Attributes)
272                     {
273                         if (attribNode.LocalName == "xmlns" || attribNode.Prefix == "xmlns")
274                             namespaceList.Add(attribNode);
275                         else
276                             attribList.Add(attribNode);
277                     }
278                 }
279                 index++;
280             } while (index < elementList.Count);
281             foreach (XmlNode elementNode in elementList)
282             {
283                 nodeList.Add(elementNode);
284             }
285             foreach (XmlNode attribNode in attribList)
286             {
287                 nodeList.Add(attribNode);
288             }
289             foreach (XmlNode namespaceNode in namespaceList)
290             {
291                 nodeList.Add(namespaceNode);
292             }
293 
294             return nodeList;
295         }
296 
NodeInList(XmlNode node, XmlNodeList nodeList)297         internal static bool NodeInList(XmlNode node, XmlNodeList nodeList)
298         {
299             foreach (XmlNode nodeElem in nodeList)
300             {
301                 if (nodeElem == node) return true;
302             }
303             return false;
304         }
305 
GetIdFromLocalUri(string uri, out bool discardComments)306         internal static string GetIdFromLocalUri(string uri, out bool discardComments)
307         {
308             string idref = uri.Substring(1);
309             // initialize the return value
310             discardComments = true;
311 
312             // Deal with XPointer of type #xpointer(id("ID")). Other XPointer support isn't handled here and is anyway optional
313             if (idref.StartsWith("xpointer(id(", StringComparison.Ordinal))
314             {
315                 int startId = idref.IndexOf("id(", StringComparison.Ordinal);
316                 int endId = idref.IndexOf(")", StringComparison.Ordinal);
317                 if (endId < 0 || endId < startId + 3)
318                     throw new CryptographicException(SR.Cryptography_Xml_InvalidReference);
319                 idref = idref.Substring(startId + 3, endId - startId - 3);
320                 idref = idref.Replace("\'", "");
321                 idref = idref.Replace("\"", "");
322                 discardComments = false;
323             }
324             return idref;
325         }
326 
ExtractIdFromLocalUri(string uri)327         internal static string ExtractIdFromLocalUri(string uri)
328         {
329             string idref = uri.Substring(1);
330 
331             // Deal with XPointer of type #xpointer(id("ID")). Other XPointer support isn't handled here and is anyway optional
332             if (idref.StartsWith("xpointer(id(", StringComparison.Ordinal))
333             {
334                 int startId = idref.IndexOf("id(", StringComparison.Ordinal);
335                 int endId = idref.IndexOf(")", StringComparison.Ordinal);
336                 if (endId < 0 || endId < startId + 3)
337                     throw new CryptographicException(SR.Cryptography_Xml_InvalidReference);
338                 idref = idref.Substring(startId + 3, endId - startId - 3);
339                 idref = idref.Replace("\'", "");
340                 idref = idref.Replace("\"", "");
341             }
342             return idref;
343         }
344 
345         // This removes all children of an element.
RemoveAllChildren(XmlElement inputElement)346         internal static void RemoveAllChildren(XmlElement inputElement)
347         {
348             XmlNode child = inputElement.FirstChild;
349             XmlNode sibling = null;
350 
351             while (child != null)
352             {
353                 sibling = child.NextSibling;
354                 inputElement.RemoveChild(child);
355                 child = sibling;
356             }
357         }
358 
359         // Writes one stream (starting from the current position) into
360         // an output stream, connecting them up and reading until
361         // hitting the end of the input stream.
362         // returns the number of bytes copied
Pump(Stream input, Stream output)363         internal static long Pump(Stream input, Stream output)
364         {
365             // Use MemoryStream's WriteTo(Stream) method if possible
366             MemoryStream inputMS = input as MemoryStream;
367             if (inputMS != null && inputMS.Position == 0)
368             {
369                 inputMS.WriteTo(output);
370                 return inputMS.Length;
371             }
372 
373             const int count = 4096;
374             byte[] bytes = new byte[count];
375             int numBytes;
376             long totalBytes = 0;
377 
378             while ((numBytes = input.Read(bytes, 0, count)) > 0)
379             {
380                 output.Write(bytes, 0, numBytes);
381                 totalBytes += numBytes;
382             }
383 
384             return totalBytes;
385         }
386 
TokenizePrefixListString(string s)387         internal static Hashtable TokenizePrefixListString(string s)
388         {
389             Hashtable set = new Hashtable();
390             if (s != null)
391             {
392                 string[] prefixes = s.Split(null);
393                 foreach (string prefix in prefixes)
394                 {
395                     if (prefix.Equals("#default"))
396                     {
397                         set.Add(string.Empty, true);
398                     }
399                     else if (prefix.Length > 0)
400                     {
401                         set.Add(prefix, true);
402                     }
403                 }
404             }
405             return set;
406         }
407 
EscapeWhitespaceData(string data)408         internal static string EscapeWhitespaceData(string data)
409         {
410             StringBuilder sb = new StringBuilder();
411             sb.Append(data);
412             Utils.SBReplaceCharWithString(sb, (char)13, "&#xD;");
413             return sb.ToString(); ;
414         }
415 
EscapeTextData(string data)416         internal static string EscapeTextData(string data)
417         {
418             StringBuilder sb = new StringBuilder();
419             sb.Append(data);
420             sb.Replace("&", "&amp;");
421             sb.Replace("<", "&lt;");
422             sb.Replace(">", "&gt;");
423             SBReplaceCharWithString(sb, (char)13, "&#xD;");
424             return sb.ToString(); ;
425         }
426 
EscapeCData(string data)427         internal static string EscapeCData(string data)
428         {
429             return EscapeTextData(data);
430         }
431 
EscapeAttributeValue(string value)432         internal static string EscapeAttributeValue(string value)
433         {
434             StringBuilder sb = new StringBuilder();
435             sb.Append(value);
436             sb.Replace("&", "&amp;");
437             sb.Replace("<", "&lt;");
438             sb.Replace("\"", "&quot;");
439             SBReplaceCharWithString(sb, (char)9, "&#x9;");
440             SBReplaceCharWithString(sb, (char)10, "&#xA;");
441             SBReplaceCharWithString(sb, (char)13, "&#xD;");
442             return sb.ToString();
443         }
444 
GetOwnerDocument(XmlNodeList nodeList)445         internal static XmlDocument GetOwnerDocument(XmlNodeList nodeList)
446         {
447             foreach (XmlNode node in nodeList)
448             {
449                 if (node.OwnerDocument != null)
450                     return node.OwnerDocument;
451             }
452             return null;
453         }
454 
AddNamespaces(XmlElement elem, CanonicalXmlNodeList namespaces)455         internal static void AddNamespaces(XmlElement elem, CanonicalXmlNodeList namespaces)
456         {
457             if (namespaces != null)
458             {
459                 foreach (XmlNode attrib in namespaces)
460                 {
461                     string name = ((attrib.Prefix.Length > 0) ? attrib.Prefix + ":" + attrib.LocalName : attrib.LocalName);
462                     // Skip the attribute if one with the same qualified name already exists
463                     if (elem.HasAttribute(name) || (name.Equals("xmlns") && elem.Prefix.Length == 0)) continue;
464                     XmlAttribute nsattrib = (XmlAttribute)elem.OwnerDocument.CreateAttribute(name);
465                     nsattrib.Value = attrib.Value;
466                     elem.SetAttributeNode(nsattrib);
467                 }
468             }
469         }
470 
AddNamespaces(XmlElement elem, Hashtable namespaces)471         internal static void AddNamespaces(XmlElement elem, Hashtable namespaces)
472         {
473             if (namespaces != null)
474             {
475                 foreach (string key in namespaces.Keys)
476                 {
477                     if (elem.HasAttribute(key)) continue;
478                     XmlAttribute nsattrib = (XmlAttribute)elem.OwnerDocument.CreateAttribute(key);
479                     nsattrib.Value = namespaces[key] as string;
480                     elem.SetAttributeNode(nsattrib);
481                 }
482             }
483         }
484 
485         // This method gets the attributes that should be propagated
GetPropagatedAttributes(XmlElement elem)486         internal static CanonicalXmlNodeList GetPropagatedAttributes(XmlElement elem)
487         {
488             if (elem == null)
489                 return null;
490 
491             CanonicalXmlNodeList namespaces = new CanonicalXmlNodeList();
492             XmlNode ancestorNode = elem;
493 
494             if (ancestorNode == null) return null;
495 
496             bool bDefNamespaceToAdd = true;
497 
498             while (ancestorNode != null)
499             {
500                 XmlElement ancestorElement = ancestorNode as XmlElement;
501                 if (ancestorElement == null)
502                 {
503                     ancestorNode = ancestorNode.ParentNode;
504                     continue;
505                 }
506                 if (!Utils.IsCommittedNamespace(ancestorElement, ancestorElement.Prefix, ancestorElement.NamespaceURI))
507                 {
508                     // Add the namespace attribute to the collection if needed
509                     if (!Utils.IsRedundantNamespace(ancestorElement, ancestorElement.Prefix, ancestorElement.NamespaceURI))
510                     {
511                         string name = ((ancestorElement.Prefix.Length > 0) ? "xmlns:" + ancestorElement.Prefix : "xmlns");
512                         XmlAttribute nsattrib = elem.OwnerDocument.CreateAttribute(name);
513                         nsattrib.Value = ancestorElement.NamespaceURI;
514                         namespaces.Add(nsattrib);
515                     }
516                 }
517                 if (ancestorElement.HasAttributes)
518                 {
519                     XmlAttributeCollection attribs = ancestorElement.Attributes;
520                     foreach (XmlAttribute attrib in attribs)
521                     {
522                         // Add a default namespace if necessary
523                         if (bDefNamespaceToAdd && attrib.LocalName == "xmlns")
524                         {
525                             XmlAttribute nsattrib = elem.OwnerDocument.CreateAttribute("xmlns");
526                             nsattrib.Value = attrib.Value;
527                             namespaces.Add(nsattrib);
528                             bDefNamespaceToAdd = false;
529                             continue;
530                         }
531                         // retain the declarations of type 'xml:*' as well
532                         if (attrib.Prefix == "xmlns" || attrib.Prefix == "xml")
533                         {
534                             namespaces.Add(attrib);
535                             continue;
536                         }
537                         if (attrib.NamespaceURI.Length > 0)
538                         {
539                             if (!Utils.IsCommittedNamespace(ancestorElement, attrib.Prefix, attrib.NamespaceURI))
540                             {
541                                 // Add the namespace attribute to the collection if needed
542                                 if (!Utils.IsRedundantNamespace(ancestorElement, attrib.Prefix, attrib.NamespaceURI))
543                                 {
544                                     string name = ((attrib.Prefix.Length > 0) ? "xmlns:" + attrib.Prefix : "xmlns");
545                                     XmlAttribute nsattrib = elem.OwnerDocument.CreateAttribute(name);
546                                     nsattrib.Value = attrib.NamespaceURI;
547                                     namespaces.Add(nsattrib);
548                                 }
549                             }
550                         }
551                     }
552                 }
553                 ancestorNode = ancestorNode.ParentNode;
554             }
555 
556             return namespaces;
557         }
558 
559         // output of this routine is always big endian
ConvertIntToByteArray(int dwInput)560         internal static byte[] ConvertIntToByteArray(int dwInput)
561         {
562             byte[] rgbTemp = new byte[8]; // int can never be greater than Int64
563             int t1;  // t1 is remaining value to account for
564             int t2;  // t2 is t1 % 256
565             int i = 0;
566 
567             if (dwInput == 0) return new byte[1];
568             t1 = dwInput;
569             while (t1 > 0)
570             {
571                 t2 = t1 % 256;
572                 rgbTemp[i] = (byte)t2;
573                 t1 = (t1 - t2) / 256;
574                 i++;
575             }
576             // Now, copy only the non-zero part of rgbTemp and reverse
577             byte[] rgbOutput = new byte[i];
578             // copy and reverse in one pass
579             for (int j = 0; j < i; j++)
580             {
581                 rgbOutput[j] = rgbTemp[i - j - 1];
582             }
583             return rgbOutput;
584         }
585 
ConvertByteArrayToInt(byte[] input)586         internal static int ConvertByteArrayToInt(byte[] input)
587         {
588             // Input to this routine is always big endian
589             int dwOutput = 0;
590             for (int i = 0; i < input.Length; i++)
591             {
592                 dwOutput *= 256;
593                 dwOutput += input[i];
594             }
595             return (dwOutput);
596         }
597 
GetHexArraySize(byte[] hex)598         internal static int GetHexArraySize(byte[] hex)
599         {
600             int index = hex.Length;
601             while (index-- > 0)
602             {
603                 if (hex[index] != 0)
604                     break;
605             }
606             return index + 1;
607         }
608 
BuildBagOfCerts(KeyInfoX509Data keyInfoX509Data, CertUsageType certUsageType)609         internal static X509Certificate2Collection BuildBagOfCerts(KeyInfoX509Data keyInfoX509Data, CertUsageType certUsageType)
610         {
611             X509Certificate2Collection collection = new X509Certificate2Collection();
612             ArrayList decryptionIssuerSerials = (certUsageType == CertUsageType.Decryption ? new ArrayList() : null);
613             if (keyInfoX509Data.Certificates != null)
614             {
615                 foreach (X509Certificate2 certificate in keyInfoX509Data.Certificates)
616                 {
617                     switch (certUsageType)
618                     {
619                         case CertUsageType.Verification:
620                             collection.Add(certificate);
621                             break;
622                         case CertUsageType.Decryption:
623                             decryptionIssuerSerials.Add(new X509IssuerSerial(certificate.IssuerName.Name, certificate.SerialNumber));
624                             break;
625                     }
626                 }
627             }
628 
629             if (keyInfoX509Data.SubjectNames == null && keyInfoX509Data.IssuerSerials == null &&
630                 keyInfoX509Data.SubjectKeyIds == null && decryptionIssuerSerials == null)
631                 return collection;
632 
633             // Open LocalMachine and CurrentUser "Other People"/"My" stores.
634 
635             X509Store[] stores = new X509Store[2];
636             string storeName = (certUsageType == CertUsageType.Verification ? "AddressBook" : "My");
637             stores[0] = new X509Store(storeName, StoreLocation.CurrentUser);
638             stores[1] = new X509Store(storeName, StoreLocation.LocalMachine);
639 
640             for (int index = 0; index < stores.Length; index++)
641             {
642                 if (stores[index] != null)
643                 {
644                     X509Certificate2Collection filters = null;
645                     // We don't care if we can't open the store.
646                     try
647                     {
648                         stores[index].Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
649                         filters = stores[index].Certificates;
650                         stores[index].Close();
651                         if (keyInfoX509Data.SubjectNames != null)
652                         {
653                             foreach (string subjectName in keyInfoX509Data.SubjectNames)
654                             {
655                                 filters = filters.Find(X509FindType.FindBySubjectDistinguishedName, subjectName, false);
656                             }
657                         }
658                         if (keyInfoX509Data.IssuerSerials != null)
659                         {
660                             foreach (X509IssuerSerial issuerSerial in keyInfoX509Data.IssuerSerials)
661                             {
662                                 filters = filters.Find(X509FindType.FindByIssuerDistinguishedName, issuerSerial.IssuerName, false);
663                                 filters = filters.Find(X509FindType.FindBySerialNumber, issuerSerial.SerialNumber, false);
664                             }
665                         }
666                         if (keyInfoX509Data.SubjectKeyIds != null)
667                         {
668                             foreach (byte[] ski in keyInfoX509Data.SubjectKeyIds)
669                             {
670                                 string hex = EncodeHexString(ski);
671                                 filters = filters.Find(X509FindType.FindBySubjectKeyIdentifier, hex, false);
672                             }
673                         }
674                         if (decryptionIssuerSerials != null)
675                         {
676                             foreach (X509IssuerSerial issuerSerial in decryptionIssuerSerials)
677                             {
678                                 filters = filters.Find(X509FindType.FindByIssuerDistinguishedName, issuerSerial.IssuerName, false);
679                                 filters = filters.Find(X509FindType.FindBySerialNumber, issuerSerial.SerialNumber, false);
680                             }
681                         }
682                     }
683                     // Store doesn't exist, no read permissions, other system error
684                     catch (CryptographicException) { }
685                     // Opening LocalMachine stores (other than Root or CertificateAuthority) on Linux
686                     catch (PlatformNotSupportedException) { }
687 
688                     if (filters != null)
689                         collection.AddRange(filters);
690                 }
691             }
692 
693             return collection;
694         }
695 
696         private static readonly char[] s_hexValues = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
EncodeHexString(byte[] sArray)697         internal static string EncodeHexString(byte[] sArray)
698         {
699             return EncodeHexString(sArray, 0, (uint)sArray.Length);
700         }
701 
EncodeHexString(byte[] sArray, uint start, uint end)702         internal static string EncodeHexString(byte[] sArray, uint start, uint end)
703         {
704             string result = null;
705             if (sArray != null)
706             {
707                 char[] hexOrder = new char[(end - start) * 2];
708                 uint digit;
709                 for (uint i = start, j = 0; i < end; i++)
710                 {
711                     digit = (uint)((sArray[i] & 0xf0) >> 4);
712                     hexOrder[j++] = s_hexValues[digit];
713                     digit = (uint)(sArray[i] & 0x0f);
714                     hexOrder[j++] = s_hexValues[digit];
715                 }
716                 result = new String(hexOrder);
717             }
718             return result;
719         }
720 
DecodeHexString(string s)721         internal static byte[] DecodeHexString(string s)
722         {
723             string hexString = Utils.DiscardWhiteSpaces(s);
724             uint cbHex = (uint)hexString.Length / 2;
725             byte[] hex = new byte[cbHex];
726             int i = 0;
727             for (int index = 0; index < cbHex; index++)
728             {
729                 hex[index] = (byte)((HexToByte(hexString[i]) << 4) | HexToByte(hexString[i + 1]));
730                 i += 2;
731             }
732             return hex;
733         }
734 
HexToByte(char val)735         internal static byte HexToByte(char val)
736         {
737             if (val <= '9' && val >= '0')
738                 return (byte)(val - '0');
739             else if (val >= 'a' && val <= 'f')
740                 return (byte)((val - 'a') + 10);
741             else if (val >= 'A' && val <= 'F')
742                 return (byte)((val - 'A') + 10);
743             else
744                 return 0xFF;
745         }
746 
IsSelfSigned(X509Chain chain)747         internal static bool IsSelfSigned(X509Chain chain)
748         {
749             X509ChainElementCollection elements = chain.ChainElements;
750             if (elements.Count != 1)
751                 return false;
752             X509Certificate2 certificate = elements[0].Certificate;
753             if (String.Compare(certificate.SubjectName.Name, certificate.IssuerName.Name, StringComparison.OrdinalIgnoreCase) == 0)
754                 return true;
755             return false;
756         }
757 
GetAnyPublicKey(X509Certificate2 certificate)758         internal static AsymmetricAlgorithm GetAnyPublicKey(X509Certificate2 certificate)
759         {
760             return (AsymmetricAlgorithm)certificate.GetRSAPublicKey();
761         }
762     }
763 }
764