PkixCertPath.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423
  1. #if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
  2. #pragma warning disable
  3. using System;
  4. using System.Collections.Generic;
  5. using System.IO;
  6. using BestHTTP.SecureProtocol.Org.BouncyCastle.Asn1;
  7. using BestHTTP.SecureProtocol.Org.BouncyCastle.Asn1.X509;
  8. using BestHTTP.SecureProtocol.Org.BouncyCastle.Asn1.Pkcs;
  9. using BestHTTP.SecureProtocol.Org.BouncyCastle.X509;
  10. using BestHTTP.SecureProtocol.Org.BouncyCastle.OpenSsl;
  11. using BestHTTP.SecureProtocol.Org.BouncyCastle.Security.Certificates;
  12. using BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities;
  13. using BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities.Collections;
  14. namespace BestHTTP.SecureProtocol.Org.BouncyCastle.Pkix
  15. {
  16. /**
  17. * An immutable sequence of certificates (a certification path).<br />
  18. * <br />
  19. * This is an abstract class that defines the methods common to all CertPaths.
  20. * Subclasses can handle different kinds of certificates (X.509, PGP, etc.).<br />
  21. * <br />
  22. * All CertPath objects have a type, a list of Certificates, and one or more
  23. * supported encodings. Because the CertPath class is immutable, a CertPath
  24. * cannot change in any externally visible way after being constructed. This
  25. * stipulation applies to all public fields and methods of this class and any
  26. * added or overridden by subclasses.<br />
  27. * <br />
  28. * The type is a string that identifies the type of Certificates in the
  29. * certification path. For each certificate cert in a certification path
  30. * certPath, cert.getType().equals(certPath.getType()) must be true.<br />
  31. * <br />
  32. * The list of Certificates is an ordered List of zero or more Certificates.
  33. * This List and all of the Certificates contained in it must be immutable.<br />
  34. * <br />
  35. * Each CertPath object must support one or more encodings so that the object
  36. * can be translated into a byte array for storage or transmission to other
  37. * parties. Preferably, these encodings should be well-documented standards
  38. * (such as PKCS#7). One of the encodings supported by a CertPath is considered
  39. * the default encoding. This encoding is used if no encoding is explicitly
  40. * requested (for the {@link #getEncoded()} method, for instance).<br />
  41. * <br />
  42. * All CertPath objects are also Serializable. CertPath objects are resolved
  43. * into an alternate {@link CertPathRep} object during serialization. This
  44. * allows a CertPath object to be serialized into an equivalent representation
  45. * regardless of its underlying implementation.<br />
  46. * <br />
  47. * CertPath objects can be created with a CertificateFactory or they can be
  48. * returned by other classes, such as a CertPathBuilder.<br />
  49. * <br />
  50. * By convention, X.509 CertPaths (consisting of X509Certificates), are ordered
  51. * starting with the target certificate and ending with a certificate issued by
  52. * the trust anchor. That is, the issuer of one certificate is the subject of
  53. * the following one. The certificate representing the
  54. * {@link TrustAnchor TrustAnchor} should not be included in the certification
  55. * path. Unvalidated X.509 CertPaths may not follow these conventions. PKIX
  56. * CertPathValidators will detect any departure from these conventions that
  57. * cause the certification path to be invalid and throw a
  58. * CertPathValidatorException.<br />
  59. * <br />
  60. * <strong>Concurrent Access</strong><br />
  61. * <br />
  62. * All CertPath objects must be thread-safe. That is, multiple threads may
  63. * concurrently invoke the methods defined in this class on a single CertPath
  64. * object (or more than one) with no ill effects. This is also true for the List
  65. * returned by CertPath.getCertificates.<br />
  66. * <br />
  67. * Requiring CertPath objects to be immutable and thread-safe allows them to be
  68. * passed around to various pieces of code without worrying about coordinating
  69. * access. Providing this thread-safety is generally not difficult, since the
  70. * CertPath and List objects in question are immutable.
  71. *
  72. * @see CertificateFactory
  73. * @see CertPathBuilder
  74. */
  75. /// <summary>
  76. /// CertPath implementation for X.509 certificates.
  77. /// </summary>
  78. public class PkixCertPath
  79. // : CertPath
  80. {
  81. internal static readonly List<string> m_encodings = new List<string>{ "PkiPath", "PEM", "PKCS7" };
  82. private readonly IList<X509Certificate> m_certificates;
  83. private static IList<X509Certificate> SortCerts(IList<X509Certificate> certs)
  84. {
  85. if (certs.Count < 2)
  86. return certs;
  87. X509Name issuer = certs[0].IssuerDN;
  88. bool okay = true;
  89. for (int i = 1; i != certs.Count; i++)
  90. {
  91. X509Certificate cert = certs[i];
  92. if (issuer.Equivalent(cert.SubjectDN, true))
  93. {
  94. issuer = cert.IssuerDN;
  95. }
  96. else
  97. {
  98. okay = false;
  99. break;
  100. }
  101. }
  102. if (okay)
  103. return certs;
  104. // find end-entity cert
  105. var retList = new List<X509Certificate>(certs.Count);
  106. var orig = new List<X509Certificate>(certs);
  107. for (int i = 0; i < certs.Count; i++)
  108. {
  109. X509Certificate cert = certs[i];
  110. bool found = false;
  111. X509Name subject = cert.SubjectDN;
  112. foreach (X509Certificate c in certs)
  113. {
  114. if (c.IssuerDN.Equivalent(subject, true))
  115. {
  116. found = true;
  117. break;
  118. }
  119. }
  120. if (!found)
  121. {
  122. retList.Add(cert);
  123. certs.RemoveAt(i);
  124. }
  125. }
  126. // can only have one end entity cert - something's wrong, give up.
  127. if (retList.Count > 1)
  128. return orig;
  129. for (int i = 0; i != retList.Count; i++)
  130. {
  131. issuer = retList[i].IssuerDN;
  132. for (int j = 0; j < certs.Count; j++)
  133. {
  134. X509Certificate c = certs[j];
  135. if (issuer.Equivalent(c.SubjectDN, true))
  136. {
  137. retList.Add(c);
  138. certs.RemoveAt(j);
  139. break;
  140. }
  141. }
  142. }
  143. // make sure all certificates are accounted for.
  144. if (certs.Count > 0)
  145. return orig;
  146. return retList;
  147. }
  148. /**
  149. * Creates a CertPath of the specified type.
  150. * This constructor is protected because most users should use
  151. * a CertificateFactory to create CertPaths.
  152. * @param type the standard name of the type of Certificatesin this path
  153. **/
  154. public PkixCertPath(IList<X509Certificate> certificates)
  155. {
  156. m_certificates = SortCerts(new List<X509Certificate>(certificates));
  157. }
  158. public PkixCertPath(Stream inStream)
  159. : this(inStream, "PkiPath")
  160. {
  161. }
  162. /**
  163. * Creates a CertPath of the specified type.
  164. * This constructor is protected because most users should use
  165. * a CertificateFactory to create CertPaths.
  166. *
  167. * @param type the standard name of the type of Certificatesin this path
  168. **/
  169. public PkixCertPath(Stream inStream, string encoding)
  170. {
  171. //string upper = Org.BouncyCastle.Utilities.Platform.ToUpperInvariant(encoding);
  172. IList<X509Certificate> certs;
  173. try
  174. {
  175. if (Org.BouncyCastle.Utilities.Platform.EqualsIgnoreCase("PkiPath", encoding))
  176. {
  177. Asn1InputStream derInStream = new Asn1InputStream(inStream);
  178. Asn1Object derObject = derInStream.ReadObject();
  179. if (!(derObject is Asn1Sequence))
  180. {
  181. throw new CertificateException(
  182. "input stream does not contain a ASN1 SEQUENCE while reading PkiPath encoded data to load CertPath");
  183. }
  184. certs = new List<X509Certificate>();
  185. foreach (Asn1Encodable ae in (Asn1Sequence)derObject)
  186. {
  187. byte[] derBytes = ae.GetEncoded(Asn1Encodable.Der);
  188. Stream certInStream = new MemoryStream(derBytes, false);
  189. // TODO Is inserting at the front important (list will be sorted later anyway)?
  190. certs.Insert(0, new X509CertificateParser().ReadCertificate(certInStream));
  191. }
  192. }
  193. else if (Org.BouncyCastle.Utilities.Platform.EqualsIgnoreCase("PEM", encoding) ||
  194. Org.BouncyCastle.Utilities.Platform.EqualsIgnoreCase("PKCS7", encoding))
  195. {
  196. certs = new X509CertificateParser().ReadCertificates(inStream);
  197. }
  198. else
  199. {
  200. throw new CertificateException("unsupported encoding: " + encoding);
  201. }
  202. }
  203. catch (IOException ex)
  204. {
  205. throw new CertificateException(
  206. "IOException throw while decoding CertPath:\n"
  207. + ex.ToString());
  208. }
  209. m_certificates = SortCerts(certs);
  210. }
  211. /**
  212. * Returns an iteration of the encodings supported by this
  213. * certification path, with the default encoding
  214. * first. Attempts to modify the returned Iterator via its
  215. * remove method result in an UnsupportedOperationException.
  216. *
  217. * @return an Iterator over the names of the supported encodings (as Strings)
  218. **/
  219. public virtual IEnumerable<string> Encodings
  220. {
  221. get { return CollectionUtilities.Proxy(m_encodings); }
  222. }
  223. /**
  224. * Compares this certification path for equality with the specified object.
  225. * Two CertPaths are equal if and only if their types are equal and their
  226. * certificate Lists (and by implication the Certificates in those Lists)
  227. * are equal. A CertPath is never equal to an object that is not a CertPath.<br />
  228. * <br />
  229. * This algorithm is implemented by this method. If it is overridden, the
  230. * behavior specified here must be maintained.
  231. *
  232. * @param other
  233. * the object to test for equality with this certification path
  234. *
  235. * @return true if the specified object is equal to this certification path,
  236. * false otherwise
  237. *
  238. * @see Object#hashCode() Object.hashCode()
  239. */
  240. public override bool Equals(object obj)
  241. {
  242. if (this == obj)
  243. return true;
  244. if (!(obj is PkixCertPath that))
  245. return false;
  246. var thisCerts = this.Certificates;
  247. var thatCerts = that.Certificates;
  248. if (thisCerts.Count != thatCerts.Count)
  249. return false;
  250. var e1 = thisCerts.GetEnumerator();
  251. var e2 = thatCerts.GetEnumerator();
  252. while (e1.MoveNext())
  253. {
  254. e2.MoveNext();
  255. if (!Equals(e1.Current, e2.Current))
  256. return false;
  257. }
  258. return true;
  259. }
  260. public override int GetHashCode()
  261. {
  262. return m_certificates.GetHashCode();
  263. }
  264. /**
  265. * Returns the encoded form of this certification path, using
  266. * the default encoding.
  267. *
  268. * @return the encoded bytes
  269. * @exception CertificateEncodingException if an encoding error occurs
  270. **/
  271. public virtual byte[] GetEncoded()
  272. {
  273. return GetEncoded(m_encodings[0]);
  274. }
  275. /**
  276. * Returns the encoded form of this certification path, using
  277. * the specified encoding.
  278. *
  279. * @param encoding the name of the encoding to use
  280. * @return the encoded bytes
  281. * @exception CertificateEncodingException if an encoding error
  282. * occurs or the encoding requested is not supported
  283. *
  284. */
  285. public virtual byte[] GetEncoded(string encoding)
  286. {
  287. if (Org.BouncyCastle.Utilities.Platform.EqualsIgnoreCase(encoding, "PkiPath"))
  288. {
  289. Asn1EncodableVector v = new Asn1EncodableVector(m_certificates.Count);
  290. for (int i = m_certificates.Count - 1; i >= 0; i--)
  291. {
  292. v.Add(ToAsn1Object(m_certificates[i]));
  293. }
  294. return ToDerEncoded(new DerSequence(v));
  295. }
  296. else if (Org.BouncyCastle.Utilities.Platform.EqualsIgnoreCase(encoding, "PKCS7"))
  297. {
  298. ContentInfo encInfo = new ContentInfo(PkcsObjectIdentifiers.Data, null);
  299. Asn1EncodableVector v = new Asn1EncodableVector(m_certificates.Count);
  300. foreach (var cert in m_certificates)
  301. {
  302. v.Add(ToAsn1Object(cert));
  303. }
  304. SignedData sd = new SignedData(
  305. new DerInteger(1),
  306. new DerSet(),
  307. encInfo,
  308. new DerSet(v),
  309. null,
  310. new DerSet());
  311. return ToDerEncoded(new ContentInfo(PkcsObjectIdentifiers.SignedData, sd));
  312. }
  313. else if (Org.BouncyCastle.Utilities.Platform.EqualsIgnoreCase(encoding, "PEM"))
  314. {
  315. MemoryStream bOut = new MemoryStream();
  316. try
  317. {
  318. using (var pWrt = new PemWriter(new StreamWriter(bOut)))
  319. {
  320. foreach (var cert in m_certificates)
  321. {
  322. pWrt.WriteObject(cert);
  323. }
  324. }
  325. }
  326. catch (Exception)
  327. {
  328. throw new CertificateEncodingException("can't encode certificate for PEM encoded path");
  329. }
  330. return bOut.ToArray();
  331. }
  332. else
  333. {
  334. throw new CertificateEncodingException("unsupported encoding: " + encoding);
  335. }
  336. }
  337. /// <summary>
  338. /// Returns the list of certificates in this certification
  339. /// path.
  340. /// </summary>
  341. public virtual IList<X509Certificate> Certificates
  342. {
  343. get { return CollectionUtilities.ReadOnly(m_certificates); }
  344. }
  345. /**
  346. * Return a DERObject containing the encoded certificate.
  347. *
  348. * @param cert the X509Certificate object to be encoded
  349. *
  350. * @return the DERObject
  351. **/
  352. private Asn1Object ToAsn1Object(X509Certificate cert)
  353. {
  354. try
  355. {
  356. return cert.CertificateStructure.ToAsn1Object();
  357. }
  358. catch (Exception e)
  359. {
  360. throw new CertificateEncodingException("Exception while encoding certificate", e);
  361. }
  362. }
  363. private byte[] ToDerEncoded(Asn1Encodable obj)
  364. {
  365. try
  366. {
  367. return obj.GetEncoded(Asn1Encodable.Der);
  368. }
  369. catch (IOException e)
  370. {
  371. throw new CertificateEncodingException("Exception thrown", e);
  372. }
  373. }
  374. }
  375. }
  376. #pragma warning restore
  377. #endif