1 // Transport Security Layer (TLS)
2 // Copyright (c) 2003-2004 Carlos Guzman Alvarez
3 // Copyright (C) 2006 Novell, Inc (http://www.novell.com)
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:
13 // The above copyright notice and this permission notice shall be
14 // included in all copies or substantial portions of the Software.
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.
26 using System
.Collections
;
27 using SSCX
= System
.Security
.Cryptography
.X509Certificates
;
28 using Mono
.Security
.X509
;
29 using Mono
.Security
.X509
.Extensions
;
31 namespace Mono
.Security
.Protocol
.Tls
.Handshake
.Server
33 internal class TlsClientCertificate
: HandshakeMessage
37 private X509CertificateCollection clientCertificates
;
43 public TlsClientCertificate(Context context
, byte[] buffer
)
44 : base(context
, HandshakeType
.Certificate
, buffer
)
52 public override void Update()
54 foreach (X509Certificate certificate
in clientCertificates
) {
55 this.Context
.ClientSettings
.Certificates
.Add (new SSCX
.X509Certificate (certificate
.RawData
));
61 #region Protected Methods
63 protected override void ProcessAsSsl3()
68 protected override void ProcessAsTls1()
71 int length
= this.ReadInt24 ();
72 this.clientCertificates
= new X509CertificateCollection ();
73 while (length
> bytesRead
) {
74 int certLength
= this.ReadInt24 ();
75 bytesRead
+= certLength
+ 3;
76 byte[] cert
= this.ReadBytes (certLength
);
77 this.clientCertificates
.Add (new X509Certificate (cert
));
80 if (this.clientCertificates
.Count
> 0)
82 this.validateCertificates (this.clientCertificates
);
84 else if ((this.Context
as ServerContext
).ClientCertificateRequired
)
86 throw new TlsException (AlertDescription
.NoCertificate
);
92 #region Private Methods
94 private bool checkCertificateUsage (X509Certificate cert
)
96 ServerContext context
= (ServerContext
)this.Context
;
98 // certificate extensions are required for this
99 // we "must" accept older certificates without proofs
100 if (cert
.Version
< 3)
103 KeyUsages ku
= KeyUsages
.none
;
104 switch (context
.Negotiating
.Cipher
.ExchangeAlgorithmType
)
106 case ExchangeAlgorithmType
.RsaSign
:
107 case ExchangeAlgorithmType
.RsaKeyX
:
108 ku
= KeyUsages
.digitalSignature
;
110 case ExchangeAlgorithmType
.DiffieHellman
:
111 ku
= KeyUsages
.keyAgreement
;
113 case ExchangeAlgorithmType
.Fortezza
:
114 return false; // unsupported certificate type
117 KeyUsageExtension kux
= null;
118 ExtendedKeyUsageExtension eku
= null;
120 X509Extension xtn
= cert
.Extensions
["2.5.29.15"];
122 kux
= new KeyUsageExtension (xtn
);
124 xtn
= cert
.Extensions
["2.5.29.37"];
126 eku
= new ExtendedKeyUsageExtension (xtn
);
128 if ((kux
!= null) && (eku
!= null))
130 // RFC3280 states that when both KeyUsageExtension and
131 // ExtendedKeyUsageExtension are present then BOTH should
133 return (kux
.Support (ku
) &&
134 eku
.KeyPurpose
.Contains ("1.3.6.1.5.5.7.3.2"));
136 else if (kux
!= null)
138 return kux
.Support (ku
);
140 else if (eku
!= null)
142 // Client Authentication (1.3.6.1.5.5.7.3.2)
143 return eku
.KeyPurpose
.Contains ("1.3.6.1.5.5.7.3.2");
146 // last chance - try with older (deprecated) Netscape extensions
147 xtn
= cert
.Extensions
["2.16.840.1.113730.1.1"];
150 NetscapeCertTypeExtension ct
= new NetscapeCertTypeExtension (xtn
);
151 return ct
.Support (NetscapeCertTypeExtension
.CertTypes
.SslClient
);
154 // certificate isn't valid for SSL server usage
158 private void validateCertificates (X509CertificateCollection certificates
)
160 ServerContext context
= (ServerContext
)this.Context
;
161 AlertDescription description
= AlertDescription
.BadCertificate
;
162 SSCX
.X509Certificate client
= null;
163 int[] certificateErrors
= null;
165 // note: certificate may be null is no certificate is sent
166 // (e.g. optional mutual authentication)
167 if (certificates
.Count
> 0) {
168 X509Certificate leaf
= certificates
[0];
170 ArrayList errors
= new ArrayList ();
172 // SSL specific check - not all certificates can be
173 // used to server-side SSL some rules applies after
175 if (!checkCertificateUsage (leaf
))
177 // WinError.h CERT_E_PURPOSE 0x800B0106
178 errors
.Add ((int)-2146762490);
182 // was a chain supplied ? if so use it, if not
183 if (certificates
.Count
> 1) {
184 // if so use it (and don't build our own)
185 X509CertificateCollection chain
= new X509CertificateCollection (certificates
);
187 verify
= new X509Chain (chain
);
189 // if not, then let's build our own (based on what's available in the stores)
190 verify
= new X509Chain ();
197 result
= verify
.Build (leaf
);
206 switch (verify
.Status
)
208 case X509ChainStatusFlags
.InvalidBasicConstraints
:
209 // WinError.h TRUST_E_BASIC_CONSTRAINTS 0x80096019
210 errors
.Add ((int)-2146869223);
213 case X509ChainStatusFlags
.NotSignatureValid
:
214 // WinError.h TRUST_E_BAD_DIGEST 0x80096010
215 errors
.Add ((int)-2146869232);
218 case X509ChainStatusFlags
.NotTimeNested
:
219 // WinError.h CERT_E_VALIDITYPERIODNESTING 0x800B0102
220 errors
.Add ((int)-2146762494);
223 case X509ChainStatusFlags
.NotTimeValid
:
224 // WinError.h CERT_E_EXPIRED 0x800B0101
225 description
= AlertDescription
.CertificateExpired
;
226 errors
.Add ((int)-2146762495);
229 case X509ChainStatusFlags
.PartialChain
:
230 // WinError.h CERT_E_CHAINING 0x800B010A
231 description
= AlertDescription
.UnknownCA
;
232 errors
.Add ((int)-2146762486);
235 case X509ChainStatusFlags
.UntrustedRoot
:
236 // WinError.h CERT_E_UNTRUSTEDROOT 0x800B0109
237 description
= AlertDescription
.UnknownCA
;
238 errors
.Add ((int)-2146762487);
243 description
= AlertDescription
.CertificateUnknown
;
244 errors
.Add ((int)verify
.Status
);
248 client
= new SSCX
.X509Certificate (leaf
.RawData
);
249 certificateErrors
= (int[])errors
.ToArray (typeof (int));
253 certificateErrors
= new int[0];
256 SSCX
.X509CertificateCollection certCollection
= new SSCX
.X509CertificateCollection ();
257 foreach (X509Certificate certificate
in certificates
) {
258 certCollection
.Add (new SSCX
.X509Certificate (certificate
.RawData
));
260 if (!context
.SslStream
.RaiseClientCertificateValidation(client
, certificateErrors
))
262 throw new TlsException (
264 "Invalid certificate received from client.");
267 this.Context
.ClientSettings
.ClientCertificate
= client
;