431 lines
14 KiB
C#
431 lines
14 KiB
C#
|
//
|
||
|
// WSTrustMessageConverters.cs
|
||
|
//
|
||
|
// Author:
|
||
|
// Atsushi Enomoto <atsushi@ximian.com>
|
||
|
//
|
||
|
// Copyright (C) 2006-2007 Novell, Inc.
|
||
|
//
|
||
|
// Permission is hereby granted, free of charge, to any person obtaining
|
||
|
// a copy of this software and associated documentation files (the
|
||
|
// "Software"), to deal in the Software without restriction, including
|
||
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
||
|
// permit persons to whom the Software is furnished to do so, subject to
|
||
|
// the following conditions:
|
||
|
//
|
||
|
// The above copyright notice and this permission notice shall be
|
||
|
// included in all copies or substantial portions of the Software.
|
||
|
//
|
||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||
|
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||
|
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||
|
//
|
||
|
|
||
|
using System;
|
||
|
using System.Reflection;
|
||
|
using System.Security.Cryptography;
|
||
|
using System.IdentityModel.Selectors;
|
||
|
using System.IdentityModel.Tokens;
|
||
|
using System.Security.Cryptography.Xml;
|
||
|
using System.ServiceModel;
|
||
|
using System.ServiceModel.Channels;
|
||
|
using System.ServiceModel.Description;
|
||
|
using System.ServiceModel.Security;
|
||
|
using System.ServiceModel.Security.Tokens;
|
||
|
using System.Xml;
|
||
|
using System.Xml.Serialization;
|
||
|
|
||
|
namespace System.ServiceModel.Description
|
||
|
{
|
||
|
class WSTrustRequestSecurityTokenReader : IDisposable
|
||
|
{
|
||
|
WstRequestSecurityToken req = new WstRequestSecurityToken ();
|
||
|
XmlDictionaryReader reader;
|
||
|
SecurityTokenSerializer serializer;
|
||
|
|
||
|
public WSTrustRequestSecurityTokenReader (XmlDictionaryReader reader, SecurityTokenSerializer serializer)
|
||
|
{
|
||
|
this.reader = reader;
|
||
|
this.serializer = serializer;
|
||
|
}
|
||
|
|
||
|
public WstRequestSecurityToken Value {
|
||
|
get { return req; }
|
||
|
}
|
||
|
|
||
|
string LineInfo ()
|
||
|
{
|
||
|
IXmlLineInfo li = reader as IXmlLineInfo;
|
||
|
return li != null && li.HasLineInfo () ?
|
||
|
String.Format ("({0},{1})", li.LineNumber, li.LinePosition) : String.Empty;
|
||
|
}
|
||
|
|
||
|
public void Dispose ()
|
||
|
{
|
||
|
reader.Close ();
|
||
|
}
|
||
|
|
||
|
public WstRequestSecurityToken Read ()
|
||
|
{
|
||
|
reader.MoveToContent ();
|
||
|
req.Context = reader.GetAttribute ("Context");
|
||
|
reader.ReadStartElement ("RequestSecurityToken", Constants.WstNamespace);
|
||
|
do {
|
||
|
reader.MoveToContent ();
|
||
|
switch (reader.NodeType) {
|
||
|
case XmlNodeType.EndElement:
|
||
|
reader.Read (); // consume RequestSecurityToken end element.
|
||
|
return req;
|
||
|
case XmlNodeType.Element:
|
||
|
ReadTokenContent ();
|
||
|
break;
|
||
|
default:
|
||
|
throw new XmlException (String.Format ("Unexpected request XML {0} node, name {1}{2}", reader.NodeType, reader.Name, LineInfo ()));
|
||
|
}
|
||
|
} while (true);
|
||
|
}
|
||
|
|
||
|
void ReadTokenContent ()
|
||
|
{
|
||
|
switch (reader.NamespaceURI) {
|
||
|
case Constants.WstNamespace:
|
||
|
switch (reader.LocalName) {
|
||
|
case "RequestType":
|
||
|
req.RequestType = reader.ReadElementContentAsString ();
|
||
|
return;
|
||
|
case "Entropy":
|
||
|
ReadEntropy ();
|
||
|
return;
|
||
|
case "KeySize":
|
||
|
req.KeySize = reader.ReadElementContentAsInt ();
|
||
|
return;
|
||
|
case "KeyType":
|
||
|
req.KeyType = reader.ReadElementContentAsString ();
|
||
|
return;
|
||
|
case "TokenType":
|
||
|
string tokenType = reader.ReadElementContentAsString ();
|
||
|
if (tokenType != Constants.WsscContextToken)
|
||
|
throw new SecurityTokenException (String.Format ("Unexpected TokenType: {0}", tokenType));
|
||
|
return;
|
||
|
case "ComputedKeyAlgorithm":
|
||
|
req.ComputedKeyAlgorithm = reader.ReadElementContentAsString ();
|
||
|
return;
|
||
|
case "BinaryExchange":
|
||
|
ReadBinaryExchange ();
|
||
|
return;
|
||
|
}
|
||
|
break;
|
||
|
case Constants.WspNamespace:
|
||
|
switch (reader.LocalName) {
|
||
|
case "AppliesTo":
|
||
|
ReadAppliesTo ();
|
||
|
return;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
throw new XmlException (String.Format ("Unexpected RequestSecurityToken content element. Name is {0} and namespace URI is {1}{2}", reader.Name, reader.NamespaceURI, LineInfo ()));
|
||
|
}
|
||
|
|
||
|
protected void ReadBinaryExchange ()
|
||
|
{
|
||
|
if (reader.IsEmptyElement)
|
||
|
throw new XmlException (String.Format ("Binary content is expected in 'BinaryExchange' element.{0}", LineInfo ()));
|
||
|
WstBinaryExchange b = new WstBinaryExchange (reader.GetAttribute ("ValueType"));
|
||
|
b.EncodingType = reader.GetAttribute ("EncodingType");
|
||
|
b.Value = Convert.FromBase64String (reader.ReadElementContentAsString ());
|
||
|
req.BinaryExchange = b;
|
||
|
}
|
||
|
|
||
|
void ReadEntropy ()
|
||
|
{
|
||
|
if (reader.IsEmptyElement)
|
||
|
throw new XmlException (String.Format ("WS-Trust Entropy element is empty.{2}", LineInfo ()));
|
||
|
reader.ReadStartElement ("Entropy", Constants.WstNamespace);
|
||
|
reader.MoveToContent ();
|
||
|
|
||
|
req.Entropy = serializer.ReadToken (reader, null);
|
||
|
// after reading a token, </Entropy> should follow.
|
||
|
reader.MoveToContent ();
|
||
|
reader.ReadEndElement ();
|
||
|
}
|
||
|
|
||
|
void ReadAppliesTo ()
|
||
|
{
|
||
|
WspAppliesTo aTo = new WspAppliesTo ();
|
||
|
|
||
|
if (reader.IsEmptyElement)
|
||
|
throw new XmlException (String.Format ("WS-Policy AppliesTo element is empty.{2}", LineInfo ()));
|
||
|
reader.ReadStartElement ();
|
||
|
reader.MoveToContent ();
|
||
|
reader.ReadStartElement ("EndpointReference", Constants.WsaNamespace);
|
||
|
reader.MoveToContent ();
|
||
|
WsaEndpointReference er = new WsaEndpointReference ();
|
||
|
er.Address = reader.ReadElementContentAsString ("Address", Constants.WsaNamespace);
|
||
|
reader.MoveToContent ();
|
||
|
reader.ReadEndElement (); // </EndpointReference>
|
||
|
aTo.EndpointReference = er;
|
||
|
reader.MoveToContent ();
|
||
|
reader.ReadEndElement (); // </wsp:AppliesTo>
|
||
|
req.AppliesTo = aTo;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// FIXME: it might be extraneous - currently used only for IssuedToken
|
||
|
class WstRequestSecurityTokenWriter : BodyWriter
|
||
|
{
|
||
|
WstRequestSecurityToken value;
|
||
|
SecurityTokenSerializer serializer;
|
||
|
|
||
|
public WstRequestSecurityTokenWriter (WstRequestSecurityToken value, SecurityTokenSerializer serializer)
|
||
|
: base (true)
|
||
|
{
|
||
|
this.value = value;
|
||
|
this.serializer = serializer;
|
||
|
}
|
||
|
|
||
|
protected override void OnWriteBodyContents (XmlDictionaryWriter w)
|
||
|
{
|
||
|
w.WriteStartElement ("RequestSecurityToken", Constants.WstNamespace);
|
||
|
w.WriteEndElement ();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
class WSTrustRequestSecurityTokenResponseReader : IDisposable
|
||
|
{
|
||
|
string negotiation_type;
|
||
|
WstRequestSecurityTokenResponse res;
|
||
|
XmlDictionaryReader reader;
|
||
|
SecurityTokenSerializer serializer;
|
||
|
SecurityTokenResolver resolver;
|
||
|
|
||
|
public WSTrustRequestSecurityTokenResponseReader (
|
||
|
string negotiationType,
|
||
|
XmlDictionaryReader reader,
|
||
|
SecurityTokenSerializer serializer,
|
||
|
SecurityTokenResolver resolver)
|
||
|
{
|
||
|
this.negotiation_type = negotiationType;
|
||
|
this.reader = reader;
|
||
|
this.serializer = serializer;
|
||
|
this.resolver = resolver;
|
||
|
res = new WstRequestSecurityTokenResponse (serializer);
|
||
|
}
|
||
|
|
||
|
public WstRequestSecurityTokenResponse Value {
|
||
|
get { return res; }
|
||
|
}
|
||
|
|
||
|
string LineInfo ()
|
||
|
{
|
||
|
IXmlLineInfo li = reader as IXmlLineInfo;
|
||
|
return li != null && li.HasLineInfo () ?
|
||
|
String.Format ("({0},{1})", li.LineNumber, li.LinePosition) : String.Empty;
|
||
|
}
|
||
|
|
||
|
public void Dispose ()
|
||
|
{
|
||
|
reader.Close ();
|
||
|
}
|
||
|
|
||
|
public WstRequestSecurityTokenResponse Read ()
|
||
|
{
|
||
|
reader.MoveToContent ();
|
||
|
res.Context = reader.GetAttribute ("Context");
|
||
|
reader.ReadStartElement ("RequestSecurityTokenResponse", Constants.WstNamespace);
|
||
|
do {
|
||
|
reader.MoveToContent ();
|
||
|
switch (reader.NodeType) {
|
||
|
case XmlNodeType.EndElement:
|
||
|
reader.Read (); // consume RequestSecurityTokenResponse end element.
|
||
|
return res;
|
||
|
case XmlNodeType.Element:
|
||
|
ReadTokenContent ();
|
||
|
break;
|
||
|
default:
|
||
|
throw new XmlException (String.Format ("Unexpected request XML {0} node, name {1}{2}", reader.NodeType, reader.Name, LineInfo ()));
|
||
|
}
|
||
|
} while (true);
|
||
|
}
|
||
|
|
||
|
void ReadTokenContent ()
|
||
|
{
|
||
|
switch (reader.NamespaceURI) {
|
||
|
case Constants.WstNamespace:
|
||
|
switch (reader.LocalName) {
|
||
|
case "RequestedSecurityToken":
|
||
|
res.RequestedSecurityToken = (SecurityContextSecurityToken) ReadToken ();
|
||
|
return;
|
||
|
case "RequestedProofToken":
|
||
|
#if true // FIXME: we can't handle it right now
|
||
|
string ens = EncryptedXml.XmlEncNamespaceUrl;
|
||
|
reader.Read ();
|
||
|
reader.ReadStartElement ("EncryptedKey", ens);
|
||
|
string alg = reader.GetAttribute ("Algorithm");
|
||
|
bool isEmpty = reader.IsEmptyElement;
|
||
|
reader.ReadStartElement ("EncryptionMethod", ens);
|
||
|
if (alg != negotiation_type)
|
||
|
throw new XmlException (String.Format ("EncryptionMethod '{0}' is not supported in RequestedProofToken.", alg));
|
||
|
if (!isEmpty)
|
||
|
reader.ReadEndElement ();
|
||
|
reader.ReadStartElement ("CipherData", ens);
|
||
|
reader.ReadStartElement ("CipherValue", ens);
|
||
|
byte [] pt = reader.ReadContentAsBase64 ();
|
||
|
res.RequestedProofToken = pt;
|
||
|
reader.ReadEndElement ();
|
||
|
reader.ReadEndElement ();
|
||
|
reader.ReadEndElement ();// EncryptedKey
|
||
|
reader.ReadEndElement ();// RPT
|
||
|
#else
|
||
|
reader.Read ();
|
||
|
reader.MoveToContent ();
|
||
|
if (serializer.CanReadToken (reader))
|
||
|
res.RequestedProofToken = serializer.ReadToken (reader, resolver);
|
||
|
else
|
||
|
res.RequestedProofToken = serializer.ReadKeyIdentifierClause (reader);
|
||
|
reader.ReadEndElement ();
|
||
|
#endif
|
||
|
return;
|
||
|
case "BinaryExchange":
|
||
|
ReadBinaryExchange ();
|
||
|
return;
|
||
|
case "TokenType":
|
||
|
res.TokenType = reader.ReadElementContentAsString ();
|
||
|
return;
|
||
|
case "Lifetime":
|
||
|
ReadLifetime ();
|
||
|
return;
|
||
|
case "KeySize":
|
||
|
res.KeySize = reader.ReadElementContentAsInt ();
|
||
|
return;
|
||
|
case "RequestedAttachedReference":
|
||
|
res.RequestedAttachedReference = ReadTokenReference ();
|
||
|
return;
|
||
|
case "RequestedUnattachedReference":
|
||
|
res.RequestedUnattachedReference = ReadTokenReference ();
|
||
|
return;
|
||
|
case "Authenticator":
|
||
|
ReadAuthenticator ();
|
||
|
return;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
throw new XmlException (String.Format ("Unexpected RequestSecurityTokenResponse content element. Name is {0} and namespace URI is {1} {2}", reader.Name, reader.NamespaceURI, LineInfo ()));
|
||
|
}
|
||
|
|
||
|
void ReadAuthenticator ()
|
||
|
{
|
||
|
if (reader.IsEmptyElement)
|
||
|
throw new XmlException (String.Format ("WS-Trust 'Authenticator' element is expected to have contents. {0}", LineInfo ()));
|
||
|
reader.Read ();
|
||
|
reader.MoveToContent ();
|
||
|
res.Authenticator = Convert.FromBase64String (reader.ReadElementContentAsString ("CombinedHash", Constants.WstNamespace));
|
||
|
reader.ReadEndElement ();
|
||
|
}
|
||
|
|
||
|
void ReadLifetime ()
|
||
|
{
|
||
|
WstLifetime lt = new WstLifetime ();
|
||
|
res.Lifetime = lt;
|
||
|
if (reader.IsEmptyElement)
|
||
|
throw new XmlException (String.Format ("WS-Trust 'Lifetime' element is expected to have contents. {0}", LineInfo ()));
|
||
|
reader.Read ();
|
||
|
while (true) {
|
||
|
reader.MoveToContent ();
|
||
|
if (reader.NodeType != XmlNodeType.Element)
|
||
|
break;
|
||
|
if (reader.NamespaceURI == Constants.WsuNamespace) {
|
||
|
switch (reader.LocalName) {
|
||
|
case "Created":
|
||
|
lt.Created = XmlConvert.ToDateTime (reader.ReadElementContentAsString (), XmlDateTimeSerializationMode.RoundtripKind);
|
||
|
continue;
|
||
|
case "Expires":
|
||
|
lt.Expires = XmlConvert.ToDateTime (reader.ReadElementContentAsString (), XmlDateTimeSerializationMode.RoundtripKind);
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
throw new XmlException (String.Format ("Unexpected Lifetime content. Name is {0} and namespace URI is {1} {2}", reader.Name, reader.NamespaceURI, LineInfo ()));
|
||
|
}
|
||
|
reader.ReadEndElement ();
|
||
|
}
|
||
|
|
||
|
SecurityToken ReadToken ()
|
||
|
{
|
||
|
if (reader.IsEmptyElement)
|
||
|
throw new XmlException (String.Format ("Security token body is expected in '{0}' element. {1}", reader.LocalName, LineInfo ()));
|
||
|
reader.Read ();
|
||
|
reader.MoveToContent ();
|
||
|
SecurityToken token = serializer.ReadToken (reader, resolver);
|
||
|
reader.ReadEndElement ();
|
||
|
return token;
|
||
|
}
|
||
|
|
||
|
SecurityKeyIdentifierClause ReadTokenReference ()
|
||
|
{
|
||
|
if (reader.IsEmptyElement)
|
||
|
throw new XmlException (String.Format ("Content is expected in 'RequestedAttachedReference' element. {0}", LineInfo ()));
|
||
|
reader.Read ();
|
||
|
SecurityKeyIdentifierClause ret = serializer.ReadKeyIdentifierClause (reader);
|
||
|
reader.ReadEndElement ();
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
void ReadBinaryExchange ()
|
||
|
{
|
||
|
if (reader.IsEmptyElement)
|
||
|
throw new XmlException (String.Format ("Binary content is expected in 'BinaryExchange' element.{0}", LineInfo ()));
|
||
|
WstBinaryExchange b = new WstBinaryExchange (reader.GetAttribute ("ValueType"));
|
||
|
b.EncodingType = reader.GetAttribute ("EncodingType");
|
||
|
b.Value = Convert.FromBase64String (reader.ReadElementContentAsString ());
|
||
|
res.BinaryExchange = b;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// FIXME: it might be extraneous - currently unused
|
||
|
internal class WstRequestSecurityTokenResponseWriter : BodyWriter
|
||
|
{
|
||
|
WstRequestSecurityTokenResponse res;
|
||
|
SecurityTokenSerializer serializer;
|
||
|
|
||
|
public WstRequestSecurityTokenResponseWriter (WstRequestSecurityTokenResponse res, SecurityTokenSerializer serializer)
|
||
|
: base (true)
|
||
|
{
|
||
|
this.res = res;
|
||
|
this.serializer = serializer;
|
||
|
}
|
||
|
|
||
|
protected override void OnWriteBodyContents (XmlDictionaryWriter writer)
|
||
|
{
|
||
|
try {
|
||
|
writer.WriteStartElement ("RequestSecurityTokenResponse", Constants.WstNamespace);
|
||
|
|
||
|
// RequestedSecurityToken
|
||
|
writer.WriteStartElement ("RequestedSecurityToken", Constants.WstNamespace);
|
||
|
|
||
|
serializer.WriteToken (writer, res.RequestedSecurityToken);
|
||
|
|
||
|
writer.WriteEndElement ();
|
||
|
|
||
|
/*
|
||
|
// Entropy
|
||
|
writer.WriteStartElement ("Entropy", Constants.WstNamespace);
|
||
|
// FIXME: keep generated key
|
||
|
serializer.WriteToken (writer,
|
||
|
new BinarySecretSecurityToken (Rijndael.Create ().Key));
|
||
|
writer.WriteEndElement ();
|
||
|
*/
|
||
|
|
||
|
writer.WriteEndElement ();
|
||
|
} catch (Exception ex) {
|
||
|
Console.WriteLine (ex);
|
||
|
throw;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|