2 // ClientCredentialsSecurityTokenManager.cs
5 // Atsushi Enomoto <atsushi@ximian.com>
7 // Copyright (C) 2006 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.Net.Security;
30 using System.IdentityModel.Selectors;
31 using System.IdentityModel.Tokens;
32 using System.Security.Cryptography.X509Certificates;
33 using System.Security.Principal;
34 using System.ServiceModel.Channels;
35 using System.ServiceModel.Description;
36 using System.ServiceModel.Security;
37 using System.ServiceModel.Security.Tokens;
39 using ReqType = System.ServiceModel.Security.Tokens.ServiceModelSecurityTokenRequirement;
41 namespace System.ServiceModel
44 public class ClientCredentialsSecurityTokenManager : SecurityTokenManager
46 ClientCredentials credentials;
48 public ClientCredentialsSecurityTokenManager (ClientCredentials credentials)
50 if (credentials == null)
51 throw new ArgumentNullException ("credentials");
52 this.credentials = credentials;
55 public ClientCredentials ClientCredentials {
56 get { return credentials; }
60 public override SecurityTokenAuthenticator CreateSecurityTokenAuthenticator (
61 SecurityTokenRequirement requirement,
62 out SecurityTokenResolver outOfBandTokenResolver)
64 outOfBandTokenResolver = null;
65 if (requirement == null)
66 throw new ArgumentNullException ("requirement");
67 if (requirement.TokenType == SecurityTokenTypes.UserName) {
70 else if (requirement.TokenType == SecurityTokenTypes.Rsa)
71 return new RsaSecurityTokenAuthenticator ();
72 else if (requirement.TokenType == SecurityTokenTypes.X509Certificate)
73 return CreateX509Authenticator (requirement);
74 else if (requirement.TokenType == ServiceModelSecurityTokenTypes.Spnego)
75 return new SspiClientSecurityTokenAuthenticator (this, requirement);
77 throw new NotImplementedException ();
79 throw new NotSupportedException (String.Format ("Security token requirement '{0}' is not supported to create SecurityTokenAuthenticator.", requirement));
83 X509SecurityTokenAuthenticator CreateX509Authenticator (SecurityTokenRequirement requirement)
85 X509CertificateRecipientClientCredential c = ClientCredentials.ServiceCertificate;
86 switch (c.Authentication.CertificateValidationMode) {
87 case X509CertificateValidationMode.Custom:
88 if (c.Authentication.CustomCertificateValidator == null)
89 throw new InvalidOperationException ("For Custom certificate validation mode, CustomCertificateValidator is required to create a token authenticator for X509 certificate.");
90 return new X509SecurityTokenAuthenticator (c.Authentication.CustomCertificateValidator);
91 case X509CertificateValidationMode.None:
92 return new X509SecurityTokenAuthenticator (X509CertificateValidator.None);
93 case X509CertificateValidationMode.PeerOrChainTrust:
94 return new X509SecurityTokenAuthenticator (X509CertificateValidator.PeerOrChainTrust);
95 case X509CertificateValidationMode.ChainTrust:
96 return new X509SecurityTokenAuthenticator (X509CertificateValidator.ChainTrust);
98 return new X509SecurityTokenAuthenticator (X509CertificateValidator.PeerTrust);
102 #region CreateSecurityTokenProvider()
105 public override SecurityTokenProvider CreateSecurityTokenProvider (SecurityTokenRequirement requirement)
107 if (IsIssuedSecurityTokenRequirement (requirement))
108 return CreateIssuedTokenProvider (requirement);
112 // huh, they are not constants but properties.
113 if (requirement.TokenType == SecurityTokenTypes.X509Certificate)
114 return CreateX509SecurityTokenProvider (requirement);
115 else if (requirement.TokenType == ServiceModelSecurityTokenTypes.SecureConversation)
116 return CreateSecureConversationProvider (requirement);
117 else if (requirement.TokenType == ServiceModelSecurityTokenTypes.AnonymousSslnego) {
118 if (requirement.TryGetProperty<bool> (ReqType.IsInitiatorProperty, out isInitiator) && isInitiator)
119 return CreateSslnegoProvider (requirement);
120 } else if (requirement.TokenType == ServiceModelSecurityTokenTypes.MutualSslnego) {
121 if (requirement.TryGetProperty<bool> (ReqType.IsInitiatorProperty, out isInitiator) && isInitiator)
122 return CreateSslnegoProvider (requirement);
123 } else if (requirement.TokenType == ServiceModelSecurityTokenTypes.SecurityContext) {
125 } else if (requirement.TokenType == ServiceModelSecurityTokenTypes.Spnego) {
126 return CreateSpnegoProvider (requirement);
127 } else if (requirement.TokenType == ServiceModelSecurityTokenTypes.SspiCredential) {
129 } else if (requirement.TokenType == SecurityTokenTypes.Rsa) {
131 } else if (requirement.TokenType == SecurityTokenTypes.Saml) {
133 } else if (requirement.TokenType == SecurityTokenTypes.UserName)
134 return CreateUserNameProvider (requirement);
135 else if (requirement.TokenType == SecurityTokenTypes.Kerberos) {
136 return CreateKerberosProvider (requirement);
138 throw new NotSupportedException (String.Format ("Token type '{0}' is not supported", requirement.TokenType));
141 UserNameSecurityTokenProvider CreateUserNameProvider (
142 SecurityTokenRequirement requirement)
144 UserNamePasswordClientCredential c =
145 credentials.UserName;
146 if (c.UserName == null)
147 throw new InvalidOperationException ("User name is not specified in ClientCredentials.");
148 UserNameSecurityTokenProvider p =
149 new UserNameSecurityTokenProvider (c.UserName, c.Password);
153 KerberosSecurityTokenProvider CreateKerberosProvider (SecurityTokenRequirement requirement)
155 // FIXME: how to get SPN?
156 return new KerberosSecurityTokenProvider (
157 "", credentials.Windows.AllowedImpersonationLevel, credentials.Windows.ClientCredential);
160 X509SecurityTokenProvider CreateX509SecurityTokenProvider (SecurityTokenRequirement requirement)
162 // - When the request is as an initiator, then
163 // - if the purpose is key exchange, then
164 // the initiator wants the service certificate
165 // to encrypt the message with its public key.
166 // - otherwise, the initiator wants the client
167 // certificate to sign the message with the
170 // - if the purpose is key exchange, then
171 // the recipient wants the client certificate
172 // to encrypt the message with its public key.
173 // - otherwise, the recipient wants the service
174 // certificate to sign the message with the
177 if (!requirement.TryGetProperty<bool> (ReqType.IsInitiatorProperty, out isInitiator))
179 X509Certificate2 cert;
182 isClient = requirement.KeyUsage == SecurityKeyUsage.Signature;
184 if (!requirement.Properties.ContainsKey (SecurityTokenRequirement.KeyUsageProperty))
185 throw new NotSupportedException (String.Format ("Cannot create a security token provider from this requirement '{0}'", requirement));
186 isClient = requirement.KeyUsage == SecurityKeyUsage.Exchange;
189 cert = credentials.ClientCertificate.Certificate;
191 cert = GetServiceCertificate (requirement);
195 throw new InvalidOperationException ("Client certificate is not provided in ClientCredentials.");
197 throw new InvalidOperationException ("Service certificate is not provided.");
199 X509SecurityTokenProvider p =
200 new X509SecurityTokenProvider (cert);
204 X509Certificate2 GetServiceCertificate (SecurityTokenRequirement requirement)
206 // try X509CertificateEndpointIdentity,
207 // ServiceCertificate.ScopedCertificate and
208 // ServiceCertificate.DefaultCertificate.
210 X509Certificate2 cert = null;
211 EndpointAddress address = null;
212 requirement.TryGetProperty (ReqType.TargetAddressProperty, out address);
214 if (address != null) {
215 X509CertificateEndpointIdentity ident = address.Identity as X509CertificateEndpointIdentity;
216 if (ident != null && ident.Certificates.Count > 0)
217 cert = ident.Certificates [0];
219 credentials.ServiceCertificate.ScopedCertificates.TryGetValue (address.Uri, out cert);
222 cert = credentials.ServiceCertificate.DefaultCertificate;
226 void InitializeProviderCommunicationObject (ProviderCommunicationObject p, SecurityTokenRequirement r)
228 p.TargetAddress = r.GetProperty<EndpointAddress> (ReqType.TargetAddressProperty);
230 // FIXME: use it somewhere, probably to build
231 // IssuerBinding. However, there is also IssuerBinding
232 // property. SecureConversationSecurityBindingElement
234 SecurityBindingElement sbe =
235 r.GetProperty<SecurityBindingElement> (ReqType.SecurityBindingElementProperty);
237 // I doubt the binding is acquired this way ...
239 if (!r.TryGetProperty<Binding> (ReqType.IssuerBindingProperty, out binding))
240 binding = new CustomBinding (
241 new TextMessageEncodingBindingElement (),
242 new HttpTransportBindingElement ());
243 p.IssuerBinding = binding;
245 // not sure if it is used only for this purpose though ...
246 BindingContext ctx = r.GetProperty<BindingContext> (ReqType.IssuerBindingContextProperty);
247 foreach (IEndpointBehavior b in ctx.BindingParameters.FindAll<IEndpointBehavior> ())
248 p.IssuerChannelBehaviors.Add (b);
250 SecurityTokenVersion ver =
251 r.GetProperty<SecurityTokenVersion> (ReqType.MessageSecurityVersionProperty);
252 p.SecurityTokenSerializer =
253 CreateSecurityTokenSerializer (ver);
255 // seems like they are optional here ... (but possibly
257 EndpointAddress address;
258 if (!r.TryGetProperty<EndpointAddress> (ReqType.IssuerAddressProperty, out address))
259 address = p.TargetAddress;
260 p.IssuerAddress = address;
262 // It is somehow not checked as mandatory ...
263 SecurityAlgorithmSuite suite = null;
264 r.TryGetProperty<SecurityAlgorithmSuite> (ReqType.SecurityAlgorithmSuiteProperty, out suite);
265 p.SecurityAlgorithmSuite = suite;
268 // FIXME: it is far from done.
269 SecurityTokenProvider CreateSecureConversationProvider (SecurityTokenRequirement r)
271 IssuedSecurityTokenProvider p =
272 new IssuedSecurityTokenProvider ();
273 InitializeProviderCommunicationObject (p.Communication, r);
275 // FIXME: use it somewhere.
276 int keySize = r.KeySize;
281 SecurityTokenProvider CreateSslnegoProvider (SecurityTokenRequirement r)
283 SslSecurityTokenProvider p = new SslSecurityTokenProvider (this, r.TokenType == ServiceModelSecurityTokenTypes.MutualSslnego);
284 InitializeProviderCommunicationObject (p.Communication, r);
289 SecurityTokenProvider CreateSpnegoProvider (SecurityTokenRequirement r)
291 SpnegoSecurityTokenProvider p = new SpnegoSecurityTokenProvider (this, r);
292 InitializeProviderCommunicationObject (p.Communication, r);
297 IssuedSecurityTokenProvider CreateIssuedTokenProvider (SecurityTokenRequirement requirement)
299 IssuedSecurityTokenProvider p =
300 new IssuedSecurityTokenProvider ();
301 // FIXME: fill properties
302 EndpointAddress address;
303 if (requirement.TryGetProperty<EndpointAddress> (ReqType.IssuerAddressProperty, out address))
304 p.IssuerAddress = address;
305 if (requirement.TryGetProperty<EndpointAddress> (ReqType.TargetAddressProperty, out address))
306 p.TargetAddress = address;
308 if (requirement.TryGetProperty<Binding> (ReqType.IssuerBindingProperty, out binding))
309 p.IssuerBinding = binding;
310 MessageSecurityVersion ver;
311 if (requirement.TryGetProperty<MessageSecurityVersion> (ReqType.MessageSecurityVersionProperty, out ver))
312 p.SecurityTokenSerializer = CreateSecurityTokenSerializer (ver.SecurityVersion);
313 SecurityAlgorithmSuite suite;
314 if (requirement.TryGetProperty<SecurityAlgorithmSuite> (ReqType.SecurityAlgorithmSuiteProperty, out suite))
315 p.SecurityAlgorithmSuite = suite;
321 public override SecurityTokenSerializer CreateSecurityTokenSerializer (SecurityTokenVersion version)
323 bool bsp = version.GetSecuritySpecifications ().Contains (Constants.WSBasicSecurityProfileCore1);
324 SecurityVersion ver =
325 version.GetSecuritySpecifications ().Contains (Constants.Wss11Namespace) ?
326 SecurityVersion.WSSecurity11 :
327 SecurityVersion.WSSecurity10;
328 return new WSSecurityTokenSerializer (ver, bsp);
331 protected SecurityTokenSerializer CreateSecurityTokenSerializer (SecurityVersion version)
333 return new WSSecurityTokenSerializer (version);
336 protected internal bool IsIssuedSecurityTokenRequirement (
337 SecurityTokenRequirement requirement)
339 SecurityTokenParameters ret;
340 if (!requirement.TryGetProperty<SecurityTokenParameters> (ServiceModelSecurityTokenRequirement.IssuedSecurityTokenParametersProperty, out ret))
342 return ret is IssuedSecurityTokenParameters;