Certificate.cs 12 KB

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