2010-06-03 Jb Evain <jbevain@novell.com>
[mcs.git] / class / System.ServiceModel / System.ServiceModel.Security.Tokens / SslSecurityTokenProvider.cs
blob6664b46c58d9efdf84e7aa2b47ce33a204712786
1 //
2 // SslSecurityTokenProvider.cs
3 //
4 // Author:
5 // Atsushi Enomoto <atsushi@ximian.com>
6 //
7 // Copyright (C) 2006-2007 Novell, Inc. http://www.novell.com
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 //
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 //
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 using System;
29 using System.Collections.Generic;
30 using System.IO;
31 using System.Net.Security;
32 using System.IdentityModel.Selectors;
33 using System.IdentityModel.Tokens;
34 using System.Security.Cryptography;
35 using System.Security.Cryptography.X509Certificates;
36 using System.Security.Cryptography.Xml;
37 using System.ServiceModel;
38 using System.ServiceModel.Channels;
39 using System.ServiceModel.Description;
40 using System.ServiceModel.Security;
41 using System.ServiceModel.Security.Tokens;
42 using System.Xml;
44 using ReqType = System.ServiceModel.Security.Tokens.ServiceModelSecurityTokenRequirement;
46 namespace System.ServiceModel.Security.Tokens
48 class SslSecurityTokenProvider : CommunicationSecurityTokenProvider
50 SslCommunicationObject comm;
51 ClientCredentialsSecurityTokenManager manager;
53 public SslSecurityTokenProvider (ClientCredentialsSecurityTokenManager manager, bool mutual)
55 this.manager = manager;
56 comm = new SslCommunicationObject (this, mutual);
59 public override ProviderCommunicationObject Communication {
60 get { return comm; }
63 public ClientCredentialsSecurityTokenManager Manager {
64 get { return manager; }
67 public override SecurityToken GetOnlineToken (TimeSpan timeout)
69 return comm.GetToken (timeout);
73 class SslCommunicationObject : ProviderCommunicationObject
75 SslSecurityTokenProvider owner;
76 WSTrustSecurityTokenServiceProxy proxy;
77 X509Certificate2 client_certificate;
80 public SslCommunicationObject (SslSecurityTokenProvider owner, bool mutual)
82 if (mutual) {
83 client_certificate = owner.Manager.ClientCredentials.ClientCertificate.Certificate;
84 if (client_certificate == null)
85 throw new InvalidOperationException ("ClientCertificate is required for mutual SSL negotiation.");
87 this.owner = owner;
90 class TlsnegoClientSessionContext
92 XmlDocument doc = new XmlDocument ();
93 XmlDsigExcC14NTransform t = new XmlDsigExcC14NTransform ();
94 MemoryStream stream = new MemoryStream ();
96 public void StoreMessage (XmlReader reader)
98 doc.RemoveAll ();
99 doc.AppendChild (doc.ReadNode (reader));
100 t.LoadInput (doc);
101 MemoryStream s = (MemoryStream) t.GetOutput ();
102 byte [] bytes = s.ToArray ();
103 stream.Write (bytes, 0, bytes.Length);
106 public byte [] GetC14NResults ()
108 return stream.ToArray ();
112 public SecurityToken GetToken (TimeSpan timeout)
114 TlsnegoClientSessionContext tlsctx =
115 new TlsnegoClientSessionContext ();
116 TlsClientSession tls = new TlsClientSession (IssuerAddress.Uri.ToString (), client_certificate);
117 WstRequestSecurityToken rst =
118 new WstRequestSecurityToken ();
119 string contextId = rst.Context;
121 // send ClientHello
122 rst.BinaryExchange = new WstBinaryExchange (Constants.WstBinaryExchangeValueTls);
123 rst.BinaryExchange.Value = tls.ProcessClientHello ();
125 Message request = Message.CreateMessage (IssuerBinding.MessageVersion, Constants.WstIssueAction, rst);
126 request.Headers.MessageId = new UniqueId ();
127 request.Headers.ReplyTo = new EndpointAddress (Constants.WsaAnonymousUri);
128 request.Headers.To = TargetAddress.Uri;
129 MessageBuffer buffer = request.CreateBufferedCopy (0x10000);
130 tlsctx.StoreMessage (buffer.CreateMessage ().GetReaderAtBodyContents ());
131 Message response = proxy.Issue (buffer.CreateMessage ());
133 // FIXME: use correct limitation
134 buffer = response.CreateBufferedCopy (0x10000);
135 tlsctx.StoreMessage (buffer.CreateMessage ().GetReaderAtBodyContents ());
137 // receive ServerHello
138 WSTrustRequestSecurityTokenResponseReader reader =
139 new WSTrustRequestSecurityTokenResponseReader (Constants.WstTlsnegoProofTokenType, buffer.CreateMessage ().GetReaderAtBodyContents (), SecurityTokenSerializer, null);
140 reader.Read ();
141 if (reader.Value.RequestedSecurityToken != null)
142 return reader.Value.RequestedSecurityToken;
144 tls.ProcessServerHello (reader.Value.BinaryExchange.Value);
146 // send ClientKeyExchange
147 WstRequestSecurityTokenResponse rstr =
148 new WstRequestSecurityTokenResponse (SecurityTokenSerializer);
149 rstr.Context = reader.Value.Context;
150 rstr.BinaryExchange = new WstBinaryExchange (Constants.WstBinaryExchangeValueTls);
151 rstr.BinaryExchange.Value = tls.ProcessClientKeyExchange ();
153 request = Message.CreateMessage (IssuerBinding.MessageVersion, Constants.WstIssueReplyAction, rstr);
154 request.Headers.ReplyTo = new EndpointAddress (Constants.WsaAnonymousUri);
155 request.Headers.To = TargetAddress.Uri;
157 buffer = request.CreateBufferedCopy (0x10000);
158 tlsctx.StoreMessage (buffer.CreateMessage ().GetReaderAtBodyContents ());
159 //Console.WriteLine (System.Text.Encoding.UTF8.GetString (tlsctx.GetC14NResults ()));
161 // FIXME: regeneration of this instance is somehow required, but should not be.
162 proxy = new WSTrustSecurityTokenServiceProxy (
163 IssuerBinding, IssuerAddress);
164 response = proxy.IssueReply (buffer.CreateMessage ());
165 // FIXME: use correct limitation
166 buffer = response.CreateBufferedCopy (0x10000);
168 WstRequestSecurityTokenResponseCollection coll =
169 new WstRequestSecurityTokenResponseCollection ();
170 coll.Read (Constants.WstTlsnegoProofTokenType, buffer.CreateMessage ().GetReaderAtBodyContents (), SecurityTokenSerializer, null);
171 if (coll.Responses.Count != 2)
172 throw new SecurityNegotiationException (String.Format ("Expected response is RequestSecurityTokenResponseCollection which contains two RequestSecurityTokenResponse items, but it actually contains {0} items", coll.Responses.Count));
174 WstRequestSecurityTokenResponse r = coll.Responses [0];
175 tls.ProcessServerFinished (r.BinaryExchange.Value);
176 SecurityContextSecurityToken sctSrc =
177 r.RequestedSecurityToken;
179 #if false // FIXME: should this final RSTR included in RSTRC considered too?
180 XmlDocument doc = new XmlDocument ();
181 doc.PreserveWhitespace = true;
182 using (XmlDictionaryWriter dw = XmlDictionaryWriter.CreateDictionaryWriter (doc.CreateNavigator ().AppendChild ())) {
183 if (r == null) throw new Exception ("r");
184 if (dw == null) throw new Exception ("dw");
185 r.WriteBodyContents (dw);
187 tlsctx.StoreMessage (XmlDictionaryReader.CreateDictionaryReader (new XmlNodeReader (doc)));
188 #endif
190 // the RequestedProofToken is represented as 32 bytes
191 // of TLS ApplicationData.
192 // - According to WSE2 doc, it is *the* key, but not
193 // sure it also applies to WCF.
194 // - WSS4J also seems to store the encryped shared key.
195 // - (Important) It seems that without tls decryption,
196 // .NET fails to recover the key.
197 byte [] proof = tls.ProcessApplicationData (
198 (byte []) r.RequestedProofToken);
199 byte [] key = proof;
201 // Authenticate token.
203 byte [] actual = coll.Responses [1].Authenticator;
204 if (actual == null)
205 throw new SecurityNegotiationException ("Token authenticator is expected in the RequestSecurityTokenResponse but not found.");
207 if (coll.Responses [0].Context != contextId)
208 throw new SecurityNegotiationException ("The context Id does not match with that of the corresponding token authenticator.");
210 // H = sha1(exc14n(RST..RSTRs))
211 byte [] hash = SHA1.Create ().ComputeHash (tlsctx.GetC14NResults ());
212 byte [] referent = tls.CreateHash (key, hash, "AUTH-HASH");
213 Console.WriteLine (System.Text.Encoding.ASCII.GetString (tlsctx.GetC14NResults ()));
214 Console.Write ("Hash: ");
215 foreach (byte b in hash) Console.Write ("{0:X02} ", b); Console.WriteLine ();
216 Console.Write ("Referent: ");
217 foreach (byte b in referent) Console.Write ("{0:X02} ", b); Console.WriteLine ();
218 Console.Write ("Actual: ");
219 foreach (byte b in actual) Console.Write ("{0:X02} ", b); Console.WriteLine ();
220 Console.Write ("Proof: ");
221 foreach (byte b in proof) Console.Write ("{0:X02} ", b); Console.WriteLine ();
222 bool mismatch = referent.Length != actual.Length;
223 if (!mismatch)
224 for (int i = 0; i < referent.Length; i++)
225 if (referent [i] != actual [i])
226 mismatch = true;
227 // FIXME: enable verification
228 // if (mismatch)
229 // throw new SecurityNegotiationException ("The CombinedHash does not match the expected value.");
231 return sctSrc;
234 protected internal override TimeSpan DefaultCloseTimeout {
235 get { throw new NotImplementedException (); }
238 protected internal override TimeSpan DefaultOpenTimeout {
239 get { throw new NotImplementedException (); }
242 protected override void OnAbort ()
244 throw new NotImplementedException ();
247 protected override void OnOpen (TimeSpan timeout)
249 if (State == CommunicationState.Opened)
250 throw new InvalidOperationException ("Already opened.");
252 EnsureProperties ();
254 proxy = new WSTrustSecurityTokenServiceProxy (
255 IssuerBinding, IssuerAddress);
258 protected override IAsyncResult OnBeginOpen (TimeSpan timeout, AsyncCallback callback, object state)
260 throw new NotImplementedException ();
263 protected override void OnEndOpen (IAsyncResult result)
265 throw new NotImplementedException ();
268 protected override void OnClose (TimeSpan timeout)
270 if (proxy != null)
271 proxy.Close ();
274 protected override IAsyncResult OnBeginClose (TimeSpan timeout, AsyncCallback callback, object state)
276 throw new NotImplementedException ();
279 protected override void OnEndClose (IAsyncResult result)
281 throw new NotImplementedException ();