TlsClientCertificate.cs 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. // Transport Security Layer (TLS)
  2. // Copyright (c) 2003-2004 Carlos Guzman Alvarez
  3. // Copyright (C) 2006 Novell, Inc (http://www.novell.com)
  4. //
  5. // Permission is hereby granted, free of charge, to any person obtaining
  6. // a copy of this software and associated documentation files (the
  7. // "Software"), to deal in the Software without restriction, including
  8. // without limitation the rights to use, copy, modify, merge, publish,
  9. // distribute, sublicense, and/or sell copies of the Software, and to
  10. // permit persons to whom the Software is furnished to do so, subject to
  11. // the following conditions:
  12. //
  13. // The above copyright notice and this permission notice shall be
  14. // included in all copies or substantial portions of the Software.
  15. //
  16. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  17. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  18. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  19. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  20. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  21. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  22. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  23. //
  24. using System;
  25. using System.Collections;
  26. using SSCX = System.Security.Cryptography.X509Certificates;
  27. using Mono.Security.X509;
  28. using Mono.Security.X509.Extensions;
  29. namespace Mono.Security.Protocol.Tls.Handshake.Server
  30. {
  31. internal class TlsClientCertificate : HandshakeMessage
  32. {
  33. #region Fields
  34. private X509CertificateCollection clientCertificates;
  35. #endregion
  36. #region Constructors
  37. public TlsClientCertificate(Context context, byte[] buffer)
  38. : base(context, HandshakeType.Certificate, buffer)
  39. {
  40. }
  41. #endregion
  42. #region Methods
  43. public override void Update()
  44. {
  45. foreach (X509Certificate certificate in clientCertificates) {
  46. this.Context.ClientSettings.Certificates.Add (new SSCX.X509Certificate (certificate.RawData));
  47. }
  48. }
  49. #endregion
  50. #region Protected Methods
  51. protected override void ProcessAsSsl3()
  52. {
  53. this.ProcessAsTls1();
  54. }
  55. protected override void ProcessAsTls1()
  56. {
  57. int bytesRead = 0;
  58. int length = this.ReadInt24 ();
  59. this.clientCertificates = new X509CertificateCollection ();
  60. while (length > bytesRead) {
  61. int certLength = this.ReadInt24 ();
  62. bytesRead += certLength + 3;
  63. byte[] cert = this.ReadBytes (certLength);
  64. this.clientCertificates.Add (new X509Certificate (cert));
  65. }
  66. if (this.clientCertificates.Count > 0)
  67. {
  68. this.validateCertificates (this.clientCertificates);
  69. }
  70. else if ((this.Context as ServerContext).ClientCertificateRequired)
  71. {
  72. throw new TlsException (AlertDescription.NoCertificate);
  73. }
  74. }
  75. #endregion
  76. #region Private Methods
  77. private bool checkCertificateUsage (X509Certificate cert)
  78. {
  79. ServerContext context = (ServerContext)this.Context;
  80. // certificate extensions are required for this
  81. // we "must" accept older certificates without proofs
  82. if (cert.Version < 3)
  83. return true;
  84. KeyUsages ku = KeyUsages.none;
  85. switch (context.Negotiating.Cipher.ExchangeAlgorithmType)
  86. {
  87. case ExchangeAlgorithmType.RsaSign:
  88. case ExchangeAlgorithmType.RsaKeyX:
  89. ku = KeyUsages.digitalSignature;
  90. break;
  91. case ExchangeAlgorithmType.DiffieHellman:
  92. ku = KeyUsages.keyAgreement;
  93. break;
  94. case ExchangeAlgorithmType.Fortezza:
  95. return false; // unsupported certificate type
  96. }
  97. KeyUsageExtension kux = null;
  98. ExtendedKeyUsageExtension eku = null;
  99. X509Extension xtn = cert.Extensions["2.5.29.15"];
  100. if (xtn != null)
  101. kux = new KeyUsageExtension (xtn);
  102. xtn = cert.Extensions["2.5.29.37"];
  103. if (xtn != null)
  104. eku = new ExtendedKeyUsageExtension (xtn);
  105. if ((kux != null) && (eku != null))
  106. {
  107. // RFC3280 states that when both KeyUsageExtension and
  108. // ExtendedKeyUsageExtension are present then BOTH should
  109. // be valid
  110. return (kux.Support (ku) &&
  111. eku.KeyPurpose.Contains ("1.3.6.1.5.5.7.3.2"));
  112. }
  113. else if (kux != null)
  114. {
  115. return kux.Support (ku);
  116. }
  117. else if (eku != null)
  118. {
  119. // Client Authentication (1.3.6.1.5.5.7.3.2)
  120. return eku.KeyPurpose.Contains ("1.3.6.1.5.5.7.3.2");
  121. }
  122. // last chance - try with older (deprecated) Netscape extensions
  123. xtn = cert.Extensions["2.16.840.1.113730.1.1"];
  124. if (xtn != null)
  125. {
  126. NetscapeCertTypeExtension ct = new NetscapeCertTypeExtension (xtn);
  127. return ct.Support (NetscapeCertTypeExtension.CertTypes.SslClient);
  128. }
  129. // certificate isn't valid for SSL server usage
  130. return false;
  131. }
  132. private void validateCertificates (X509CertificateCollection certificates)
  133. {
  134. ServerContext context = (ServerContext)this.Context;
  135. AlertDescription description = AlertDescription.BadCertificate;
  136. SSCX.X509Certificate client = null;
  137. int[] certificateErrors = null;
  138. // note: certificate may be null is no certificate is sent
  139. // (e.g. optional mutual authentication)
  140. if (certificates.Count > 0) {
  141. X509Certificate leaf = certificates[0];
  142. ArrayList errors = new ArrayList ();
  143. // SSL specific check - not all certificates can be
  144. // used to server-side SSL some rules applies after
  145. // all ;-)
  146. if (!checkCertificateUsage (leaf))
  147. {
  148. // WinError.h CERT_E_PURPOSE 0x800B0106
  149. errors.Add ((int)-2146762490);
  150. }
  151. X509Chain verify;
  152. // was a chain supplied ? if so use it, if not
  153. if (certificates.Count > 1) {
  154. // if so use it (and don't build our own)
  155. X509CertificateCollection chain = new X509CertificateCollection (certificates);
  156. chain.Remove (leaf);
  157. verify = new X509Chain (chain);
  158. } else {
  159. // if not, then let's build our own (based on what's available in the stores)
  160. verify = new X509Chain ();
  161. }
  162. bool result = false;
  163. try
  164. {
  165. result = verify.Build (leaf);
  166. }
  167. catch (Exception)
  168. {
  169. result = false;
  170. }
  171. if (!result)
  172. {
  173. switch (verify.Status)
  174. {
  175. case X509ChainStatusFlags.InvalidBasicConstraints:
  176. // WinError.h TRUST_E_BASIC_CONSTRAINTS 0x80096019
  177. errors.Add ((int)-2146869223);
  178. break;
  179. case X509ChainStatusFlags.NotSignatureValid:
  180. // WinError.h TRUST_E_BAD_DIGEST 0x80096010
  181. errors.Add ((int)-2146869232);
  182. break;
  183. case X509ChainStatusFlags.NotTimeNested:
  184. // WinError.h CERT_E_VALIDITYPERIODNESTING 0x800B0102
  185. errors.Add ((int)-2146762494);
  186. break;
  187. case X509ChainStatusFlags.NotTimeValid:
  188. // WinError.h CERT_E_EXPIRED 0x800B0101
  189. description = AlertDescription.CertificateExpired;
  190. errors.Add ((int)-2146762495);
  191. break;
  192. case X509ChainStatusFlags.PartialChain:
  193. // WinError.h CERT_E_CHAINING 0x800B010A
  194. description = AlertDescription.UnknownCA;
  195. errors.Add ((int)-2146762486);
  196. break;
  197. case X509ChainStatusFlags.UntrustedRoot:
  198. // WinError.h CERT_E_UNTRUSTEDROOT 0x800B0109
  199. description = AlertDescription.UnknownCA;
  200. errors.Add ((int)-2146762487);
  201. break;
  202. default:
  203. // unknown error
  204. description = AlertDescription.CertificateUnknown;
  205. errors.Add ((int)verify.Status);
  206. break;
  207. }
  208. }
  209. client = new SSCX.X509Certificate (leaf.RawData);
  210. certificateErrors = (int[])errors.ToArray (typeof (int));
  211. }
  212. else
  213. {
  214. certificateErrors = new int[0];
  215. }
  216. SSCX.X509CertificateCollection certCollection = new SSCX.X509CertificateCollection ();
  217. foreach (X509Certificate certificate in certificates) {
  218. certCollection.Add (new SSCX.X509Certificate (certificate.RawData));
  219. }
  220. if (!context.SslStream.RaiseClientCertificateValidation(client, certificateErrors))
  221. {
  222. throw new TlsException (
  223. description,
  224. "Invalid certificate received from client.");
  225. }
  226. this.Context.ClientSettings.ClientCertificate = client;
  227. }
  228. #endregion
  229. }
  230. }