2 // SslSecurityTokenAuthenticator.cs
5 // Atsushi Enomoto <atsushi@ximian.com>
7 // Copyright (C) 2007 Novell, Inc. http://www.novell.com
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:
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
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.
29 using System
.Collections
.Generic
;
30 using System
.Collections
.ObjectModel
;
32 using System
.Net
.Security
;
33 using System
.IdentityModel
.Policy
;
34 using System
.IdentityModel
.Selectors
;
35 using System
.IdentityModel
.Tokens
;
36 using System
.Security
.Cryptography
;
37 using System
.Security
.Cryptography
.X509Certificates
;
38 using System
.Security
.Cryptography
.Xml
;
39 using System
.ServiceModel
.Channels
;
40 using System
.ServiceModel
.Description
;
41 using System
.ServiceModel
.Security
;
42 using System
.ServiceModel
.Security
.Tokens
;
45 using ReqType
= System
.ServiceModel
.Security
.Tokens
.ServiceModelSecurityTokenRequirement
;
47 namespace System
.ServiceModel
.Security
.Tokens
49 // FIXME: implement all
50 class SslSecurityTokenAuthenticator
: CommunicationSecurityTokenAuthenticator
52 ServiceCredentialsSecurityTokenManager manager
;
53 SslAuthenticatorCommunicationObject comm
;
56 public SslSecurityTokenAuthenticator (
57 ServiceCredentialsSecurityTokenManager manager
,
58 SecurityTokenRequirement r
)
60 this.manager
= manager
;
61 mutual
= (r
.TokenType
== ServiceModelSecurityTokenTypes
.MutualSslnego
);
62 comm
= new SslAuthenticatorCommunicationObject (this);
65 public bool IsMutual
{
66 get { return mutual; }
69 public ServiceCredentialsSecurityTokenManager Manager
{
70 get { return manager; }
73 public override AuthenticatorCommunicationObject Communication
{
77 protected override bool CanValidateTokenCore (SecurityToken token
)
79 throw new NotImplementedException ();
82 protected override ReadOnlyCollection
<IAuthorizationPolicy
>
83 ValidateTokenCore (SecurityToken token
)
85 throw new NotImplementedException ();
89 class SslAuthenticatorCommunicationObject
: AuthenticatorCommunicationObject
91 SslSecurityTokenAuthenticator owner
;
93 public SslAuthenticatorCommunicationObject (SslSecurityTokenAuthenticator owner
)
98 WSTrustSecurityTokenServiceProxy proxy
;
100 protected internal override TimeSpan DefaultCloseTimeout
{
101 get { throw new NotImplementedException (); }
104 protected internal override TimeSpan DefaultOpenTimeout
{
105 get { throw new NotImplementedException (); }
108 public override Message
ProcessNegotiation (Message request
)
110 if (request
.Headers
.Action
== Constants
.WstIssueAction
)
111 return ProcessClientHello (request
);
113 return ProcessClientKeyExchange (request
);
116 class TlsServerSessionInfo
118 public TlsServerSessionInfo (string context
, TlsServerSession tls
)
124 public string ContextId
;
125 public TlsServerSession Tls
;
126 public MemoryStream Messages
= new MemoryStream ();
129 Dictionary
<string,TlsServerSessionInfo
> sessions
=
130 new Dictionary
<string,TlsServerSessionInfo
> ();
132 void AppendNegotiationMessageXml (XmlReader reader
, TlsServerSessionInfo tlsInfo
)
134 XmlDsigExcC14NTransform t
= new XmlDsigExcC14NTransform ();
135 XmlDocument doc
= new XmlDocument ();
136 doc
.PreserveWhitespace
= true;
137 reader
.MoveToContent ();
138 doc
.AppendChild (doc
.ReadNode (reader
));
140 MemoryStream stream
= (MemoryStream
) t
.GetOutput ();
141 byte [] bytes
= stream
.ToArray ();
142 tlsInfo
.Messages
.Write (bytes
, 0, bytes
.Length
);
145 Message
ProcessClientHello (Message request
)
147 // FIXME: use correct buffer size
148 MessageBuffer buffer
= request
.CreateBufferedCopy (0x10000);
149 WSTrustRequestSecurityTokenReader reader
=
150 new WSTrustRequestSecurityTokenReader (buffer
.CreateMessage ().GetReaderAtBodyContents (), SecurityTokenSerializer
);
153 if (sessions
.ContainsKey (reader
.Value
.Context
))
154 throw new SecurityNegotiationException (String
.Format ("The context '{0}' already exists in this SSL negotiation manager", reader
.Value
.Context
));
156 TlsServerSession tls
= new TlsServerSession (owner
.Manager
.ServiceCredentials
.ServiceCertificate
.Certificate
, owner
.IsMutual
);
157 TlsServerSessionInfo tlsInfo
= new TlsServerSessionInfo (
158 reader
.Value
.Context
, tls
);
160 AppendNegotiationMessageXml (buffer
.CreateMessage ().GetReaderAtBodyContents (), tlsInfo
);
162 tls
.ProcessClientHello (reader
.Value
.BinaryExchange
.Value
);
163 WstRequestSecurityTokenResponse rstr
=
164 new WstRequestSecurityTokenResponse (SecurityTokenSerializer
);
165 rstr
.Context
= reader
.Value
.Context
;
166 rstr
.BinaryExchange
= new WstBinaryExchange (Constants
.WstBinaryExchangeValueTls
);
167 rstr
.BinaryExchange
.Value
= tls
.ProcessServerHello ();
169 Message reply
= Message
.CreateMessage (request
.Version
, Constants
.WstIssueReplyAction
, rstr
);
170 reply
.Headers
.RelatesTo
= request
.Headers
.MessageId
;
172 // FIXME: use correct buffer size
173 buffer
= reply
.CreateBufferedCopy (0x10000);
174 AppendNegotiationMessageXml (buffer
.CreateMessage ().GetReaderAtBodyContents (), tlsInfo
);
176 sessions
[reader
.Value
.Context
] = tlsInfo
;
178 return buffer
.CreateMessage ();
181 Message
ProcessClientKeyExchange (Message request
)
183 // FIXME: use correct buffer size
184 MessageBuffer buffer
= request
.CreateBufferedCopy (0x10000);
185 WSTrustRequestSecurityTokenResponseReader reader
=
186 new WSTrustRequestSecurityTokenResponseReader (Constants
.WstTlsnegoProofTokenType
, buffer
.CreateMessage ().GetReaderAtBodyContents (), SecurityTokenSerializer
, null);
189 TlsServerSessionInfo tlsInfo
;
190 if (!sessions
.TryGetValue (reader
.Value
.Context
, out tlsInfo
))
191 throw new SecurityNegotiationException (String
.Format ("The context '{0}' does not exist in this SSL negotiation manager", reader
.Value
.Context
));
192 TlsServerSession tls
= tlsInfo
.Tls
;
194 AppendNegotiationMessageXml (buffer
.CreateMessage ().GetReaderAtBodyContents (), tlsInfo
);
195 //Console.WriteLine (System.Text.Encoding.UTF8.GetString (tlsInfo.Messages.ToArray ()));
197 tls
.ProcessClientKeyExchange (reader
.Value
.BinaryExchange
.Value
);
199 byte [] serverFinished
= tls
.ProcessServerFinished ();
201 // The shared key is computed as recommended in WS-Trust:
202 // P_SHA1(encrypted_key,SHA1(exc14n(RST..RSTRs))+"CK-HASH")
203 byte [] hash
= SHA1
.Create ().ComputeHash (tlsInfo
.Messages
.ToArray ());
204 byte [] key
= tls
.CreateHash (tls
.MasterSecret
, hash
, "CK-HASH");
205 byte [] keyTlsApplied
= tls
.ProcessApplicationData (key
);
206 foreach (byte b
in hash
) Console
.Write ("{0:X02} ", b
); Console
.WriteLine ();
207 foreach (byte b
in key
) Console
.Write ("{0:X02} ", b
); Console
.WriteLine ();
209 WstRequestSecurityTokenResponseCollection col
=
210 new WstRequestSecurityTokenResponseCollection ();
211 WstRequestSecurityTokenResponse rstr
=
212 new WstRequestSecurityTokenResponse (SecurityTokenSerializer
);
213 rstr
.Context
= reader
.Value
.Context
;
214 rstr
.TokenType
= Constants
.WsscContextToken
;
215 DateTime
from = DateTime
.Now
;
216 // FIXME: not sure if arbitrary key is used here.
217 SecurityContextSecurityToken sct
= SecurityContextSecurityToken
.CreateCookieSecurityContextToken (
218 // Create a new context.
219 // (do not use sslnego context here.)
221 "uuid-" + Guid
.NewGuid (),
224 // FIXME: use LocalServiceSecuritySettings.NegotiationTimeout
227 owner
.Manager
.ServiceCredentials
.SecureConversationAuthentication
.SecurityStateEncoder
);
228 rstr
.RequestedSecurityToken
= sct
;
229 // without this ProcessApplicationData(), .NET seems
230 // to fail recovering the key.
231 rstr
.RequestedProofToken
= keyTlsApplied
;
232 rstr
.RequestedAttachedReference
= new LocalIdKeyIdentifierClause (sct
.Id
);
233 rstr
.RequestedUnattachedReference
= new SecurityContextKeyIdentifierClause (sct
.ContextId
, null);
234 WstLifetime lt
= new WstLifetime ();
236 lt
.Expires
= from.Add (SecurityBindingElement
.LocalServiceSettings
.IssuedCookieLifetime
);
238 rstr
.BinaryExchange
= new WstBinaryExchange (Constants
.WstBinaryExchangeValueTls
);
239 rstr
.BinaryExchange
.Value
= serverFinished
;
241 col
.Responses
.Add (rstr
);
243 // Authenticator is mandatory for MS sslnego.
244 rstr
= new WstRequestSecurityTokenResponse (SecurityTokenSerializer
);
245 rstr
.Context
= reader
.Value
.Context
;
246 rstr
.Authenticator
= tls
.CreateHash (key
, hash
, "AUTH-HASH");
247 col
.Responses
.Add (rstr
);
249 sessions
.Remove (reader
.Value
.Context
);
251 // FIXME: get correct tokenRequestor address (probably identity authorized?)
252 if (owner
.IssuedSecurityTokenHandler
!= null)
253 owner
.IssuedSecurityTokenHandler (sct
, request
.Headers
.ReplyTo
);
255 return Message
.CreateMessage (request
.Version
, Constants
.WstIssueReplyAction
, col
);
258 protected override void OnAbort ()
260 throw new NotImplementedException ();
263 protected override void OnOpen (TimeSpan timeout
)
265 if (State
== CommunicationState
.Opened
)
266 throw new InvalidOperationException ("Already opened.");
270 proxy
= new WSTrustSecurityTokenServiceProxy (
271 IssuerBinding
, IssuerAddress
);
274 protected override IAsyncResult
OnBeginOpen (TimeSpan timeout
, AsyncCallback callback
, object state
)
276 throw new NotImplementedException ();
279 protected override void OnEndOpen (IAsyncResult result
)
281 throw new NotImplementedException ();
284 protected override void OnClose (TimeSpan timeout
)
290 protected override IAsyncResult
OnBeginClose (TimeSpan timeout
, AsyncCallback callback
, object state
)
292 throw new NotImplementedException ();
295 protected override void OnEndClose (IAsyncResult result
)
297 throw new NotImplementedException ();