Certificate.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. #if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
  2. #pragma warning disable
  3. using System;
  4. using System.Collections;
  5. using System.IO;
  6. using BestHTTP.SecureProtocol.Org.BouncyCastle.Tls.Crypto;
  7. using BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities;
  8. namespace BestHTTP.SecureProtocol.Org.BouncyCastle.Tls
  9. {
  10. /// <summary>Parsing and encoding of a <i>Certificate</i> struct from RFC 4346.</summary>
  11. /// <remarks>
  12. /// <pre>
  13. /// opaque ASN.1Cert&lt;2^24-1&gt;;
  14. /// struct {
  15. /// ASN.1Cert certificate_list&lt;0..2^24-1&gt;;
  16. /// } Certificate;
  17. /// </pre>
  18. /// </remarks>
  19. public sealed class Certificate
  20. {
  21. private static readonly TlsCertificate[] EmptyCerts = new TlsCertificate[0];
  22. private static readonly CertificateEntry[] EmptyCertEntries = new CertificateEntry[0];
  23. public static readonly Certificate EmptyChain = new Certificate(EmptyCerts);
  24. public static readonly Certificate EmptyChainTls13 = new Certificate(TlsUtilities.EmptyBytes, EmptyCertEntries);
  25. public sealed class ParseOptions
  26. {
  27. private int m_maxChainLength = int.MaxValue;
  28. public int MaxChainLength
  29. {
  30. get { return m_maxChainLength; }
  31. }
  32. public ParseOptions SetMaxChainLength(int maxChainLength)
  33. {
  34. this.m_maxChainLength = maxChainLength;
  35. return this;
  36. }
  37. }
  38. private static CertificateEntry[] Convert(TlsCertificate[] certificateList)
  39. {
  40. if (TlsUtilities.IsNullOrContainsNull(certificateList))
  41. throw new ArgumentException("cannot be null or contain any nulls", "certificateList");
  42. int count = certificateList.Length;
  43. CertificateEntry[] result = new CertificateEntry[count];
  44. for (int i = 0; i < count; ++i)
  45. {
  46. result[i] = new CertificateEntry(certificateList[i], null);
  47. }
  48. return result;
  49. }
  50. private readonly byte[] m_certificateRequestContext;
  51. private readonly CertificateEntry[] m_certificateEntryList;
  52. public Certificate(TlsCertificate[] certificateList)
  53. : this(null, Convert(certificateList))
  54. {
  55. }
  56. // TODO[tls13] Prefer to manage the certificateRequestContext internally only?
  57. public Certificate(byte[] certificateRequestContext, CertificateEntry[] certificateEntryList)
  58. {
  59. if (null != certificateRequestContext && !TlsUtilities.IsValidUint8(certificateRequestContext.Length))
  60. throw new ArgumentException("cannot be longer than 255", "certificateRequestContext");
  61. if (TlsUtilities.IsNullOrContainsNull(certificateEntryList))
  62. throw new ArgumentException("cannot be null or contain any nulls", "certificateEntryList");
  63. this.m_certificateRequestContext = TlsUtilities.Clone(certificateRequestContext);
  64. this.m_certificateEntryList = certificateEntryList;
  65. }
  66. public byte[] GetCertificateRequestContext()
  67. {
  68. return TlsUtilities.Clone(m_certificateRequestContext);
  69. }
  70. /// <returns>an array of <see cref="TlsCertificate"/> representing a certificate chain.</returns>
  71. public TlsCertificate[] GetCertificateList()
  72. {
  73. return CloneCertificateList();
  74. }
  75. public TlsCertificate GetCertificateAt(int index)
  76. {
  77. return m_certificateEntryList[index].Certificate;
  78. }
  79. public CertificateEntry GetCertificateEntryAt(int index)
  80. {
  81. return m_certificateEntryList[index];
  82. }
  83. public CertificateEntry[] GetCertificateEntryList()
  84. {
  85. return CloneCertificateEntryList();
  86. }
  87. public short CertificateType
  88. {
  89. get { return Tls.CertificateType.X509; }
  90. }
  91. public int Length
  92. {
  93. get { return m_certificateEntryList.Length; }
  94. }
  95. /// <returns><c>true</c> if this certificate chain contains no certificates, or <c>false</c> otherwise.
  96. /// </returns>
  97. public bool IsEmpty
  98. {
  99. get { return m_certificateEntryList.Length == 0; }
  100. }
  101. /// <summary>Encode this <see cref="Certificate"/> to a <see cref="Stream"/>, and optionally calculate the
  102. /// "end point hash" (per RFC 5929's tls-server-end-point binding).</summary>
  103. /// <param name="context">the <see cref="TlsContext"/> of the current connection.</param>
  104. /// <param name="messageOutput">the <see cref="Stream"/> to encode to.</param>
  105. /// <param name="endPointHashOutput">the <see cref="Stream"/> to write the "end point hash" to (or null).
  106. /// </param>
  107. /// <exception cref="IOException"/>
  108. public void Encode(TlsContext context, Stream messageOutput, Stream endPointHashOutput)
  109. {
  110. bool isTlsV13 = TlsUtilities.IsTlsV13(context);
  111. if ((null != m_certificateRequestContext) != isTlsV13)
  112. throw new InvalidOperationException();
  113. if (isTlsV13)
  114. {
  115. TlsUtilities.WriteOpaque8(m_certificateRequestContext, messageOutput);
  116. }
  117. int count = m_certificateEntryList.Length;
  118. IList certEncodings = BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities.Platform.CreateArrayList(count);
  119. IList extEncodings = isTlsV13 ? BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities.Platform.CreateArrayList(count) : null;
  120. long totalLength = 0;
  121. for (int i = 0; i < count; ++i)
  122. {
  123. CertificateEntry entry = m_certificateEntryList[i];
  124. TlsCertificate cert = entry.Certificate;
  125. byte[] derEncoding = cert.GetEncoded();
  126. if (i == 0 && endPointHashOutput != null)
  127. {
  128. CalculateEndPointHash(context, cert, derEncoding, endPointHashOutput);
  129. }
  130. certEncodings.Add(derEncoding);
  131. totalLength += derEncoding.Length;
  132. totalLength += 3;
  133. if (isTlsV13)
  134. {
  135. IDictionary extensions = entry.Extensions;
  136. byte[] extEncoding = (null == extensions)
  137. ? TlsUtilities.EmptyBytes
  138. : TlsProtocol.WriteExtensionsData(extensions);
  139. extEncodings.Add(extEncoding);
  140. totalLength += extEncoding.Length;
  141. totalLength += 2;
  142. }
  143. }
  144. TlsUtilities.CheckUint24(totalLength);
  145. TlsUtilities.WriteUint24((int)totalLength, messageOutput);
  146. for (int i = 0; i < count; ++i)
  147. {
  148. byte[] certEncoding = (byte[])certEncodings[i];
  149. TlsUtilities.WriteOpaque24(certEncoding, messageOutput);
  150. if (isTlsV13)
  151. {
  152. byte[] extEncoding = (byte[])extEncodings[i];
  153. TlsUtilities.WriteOpaque16(extEncoding, messageOutput);
  154. }
  155. }
  156. }
  157. /// <summary>Parse a <see cref="Certificate"/> from a <see cref="Stream"/>.</summary>
  158. /// <param name="options">the <see cref="ParseOptions"/> to apply during parsing.</param>
  159. /// <param name="context">the <see cref="TlsContext"/> of the current connection.</param>
  160. /// <param name="messageInput">the <see cref="Stream"/> to parse from.</param>
  161. /// <param name="endPointHashOutput">the <see cref="Stream"/> to write the "end point hash" to (or null).
  162. /// </param>
  163. /// <returns>a <see cref="Certificate"/> object.</returns>
  164. /// <exception cref="IOException"/>
  165. public static Certificate Parse(ParseOptions options, TlsContext context, Stream messageInput,
  166. Stream endPointHashOutput)
  167. {
  168. SecurityParameters securityParameters = context.SecurityParameters;
  169. bool isTlsV13 = TlsUtilities.IsTlsV13(securityParameters.NegotiatedVersion);
  170. byte[] certificateRequestContext = null;
  171. if (isTlsV13)
  172. {
  173. certificateRequestContext = TlsUtilities.ReadOpaque8(messageInput);
  174. }
  175. int totalLength = TlsUtilities.ReadUint24(messageInput);
  176. if (totalLength == 0)
  177. {
  178. return !isTlsV13 ? EmptyChain
  179. : certificateRequestContext.Length < 1 ? EmptyChainTls13
  180. : new Certificate(certificateRequestContext, EmptyCertEntries);
  181. }
  182. byte[] certListData = TlsUtilities.ReadFully(totalLength, messageInput);
  183. MemoryStream buf = new MemoryStream(certListData, false);
  184. TlsCrypto crypto = context.Crypto;
  185. int maxChainLength = System.Math.Max(1, options.MaxChainLength);
  186. IList certificate_list = BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities.Platform.CreateArrayList();
  187. while (buf.Position < buf.Length)
  188. {
  189. if (certificate_list.Count >= maxChainLength)
  190. {
  191. throw new TlsFatalAlert(AlertDescription.internal_error,
  192. "Certificate chain longer than maximum (" + maxChainLength + ")");
  193. }
  194. byte[] derEncoding = TlsUtilities.ReadOpaque24(buf, 1);
  195. TlsCertificate cert = crypto.CreateCertificate(derEncoding);
  196. if (certificate_list.Count < 1 && endPointHashOutput != null)
  197. {
  198. CalculateEndPointHash(context, cert, derEncoding, endPointHashOutput);
  199. }
  200. IDictionary extensions = null;
  201. if (isTlsV13)
  202. {
  203. byte[] extEncoding = TlsUtilities.ReadOpaque16(buf);
  204. extensions = TlsProtocol.ReadExtensionsData13(HandshakeType.certificate, extEncoding);
  205. }
  206. certificate_list.Add(new CertificateEntry(cert, extensions));
  207. }
  208. CertificateEntry[] certificateList = new CertificateEntry[certificate_list.Count];
  209. for (int i = 0; i < certificate_list.Count; i++)
  210. {
  211. certificateList[i] = (CertificateEntry)certificate_list[i];
  212. }
  213. return new Certificate(certificateRequestContext, certificateList);
  214. }
  215. private static void CalculateEndPointHash(TlsContext context, TlsCertificate cert, byte[] encoding,
  216. Stream output)
  217. {
  218. byte[] endPointHash = TlsUtilities.CalculateEndPointHash(context, cert, encoding);
  219. if (endPointHash != null && endPointHash.Length > 0)
  220. {
  221. output.Write(endPointHash, 0, endPointHash.Length);
  222. }
  223. }
  224. private TlsCertificate[] CloneCertificateList()
  225. {
  226. int count = m_certificateEntryList.Length;
  227. if (0 == count)
  228. return EmptyCerts;
  229. TlsCertificate[] result = new TlsCertificate[count];
  230. for (int i = 0; i < count; ++i)
  231. {
  232. result[i] = m_certificateEntryList[i].Certificate;
  233. }
  234. return result;
  235. }
  236. private CertificateEntry[] CloneCertificateEntryList()
  237. {
  238. int count = m_certificateEntryList.Length;
  239. if (0 == count)
  240. return EmptyCertEntries;
  241. CertificateEntry[] result = new CertificateEntry[count];
  242. Array.Copy(m_certificateEntryList, 0, result, 0, count);
  243. return result;
  244. }
  245. }
  246. }
  247. #pragma warning restore
  248. #endif