WCF を使用して従来の Web サービスを使用する際に問題が発生しています。Web サービスが異なる署名キーと暗号化キーを使用していたという事実を扱っていた 1 つの問題を解決しました。しかし、Web サービス呼び出しを行うと、バージョン ミスマッチの応答が返されます。WSE で生成されたエンベロープを見ると、SOAP のバージョンが WCF で生成されたものと異なることに気付きました。
私の WSE エンベロープ ヘッダーは次のようになります。
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
一方、WCF エンベロープ ヘッダーは
<s:Envelope xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:s="http://www.w3.org/2003/05/soap-envelope">
soap エンベロープの名前空間とプレフィックスを変更するためのコードをいくつか追加しました。
EndpointAddress serviceEndpoint =
new EndpointAddress(new Uri("http://mylegacywebservice.com"));
CustomBinding binding = new CustomBinding();
AsymmetricSecurityBindingElement securityBE =
SecurityBindingElement.CreateMutualCertificateDuplexBindingElement(
MessageSecurityVersion.WSSecurity10WSTrust13WSSecureConversation13WSSecurityPolicy12BasicSecurityProfile10);
// Add a custom IdentityVerifier because the service uses two certificates
// (one for signing and one for encryption) and an endpoint identity that
// contains a single identity claim.
securityBE.LocalClientSettings.IdentityVerifier = new MyIdentityVerifier();
binding.Elements.Add(securityBE);
CompositeDuplexBindingElement compositeDuplex =
new CompositeDuplexBindingElement();
compositeDuplex.ClientBaseAddress = new Uri("http://mylegacywebservice.com");
binding.Elements.Add(compositeDuplex);
binding.Elements.Add(new OneWayBindingElement());
binding.Elements.Add(new HttpTransportBindingElement());
Dictionary<string, string> namespaceToPrefixMapping = new Dictionary<string, string>
{
{ "http://schemas.xmlsoap.org/soap/envelope/", "soap" },
{ "http://schemas.xmlsoap.org/ws/2004/08/addressing", "wsa" },
};
binding = ReplacePrefixMessageEncodingBindingElement.ReplaceEncodingBindingElement(
new WSHttpBinding(SecurityMode.None),
namespaceToPrefixMapping);
以下は、prefixreplace.cs です。
public class PrefixReplacer
{
const string XmlnsNamespace = "http://www.w3.org/2000/xmlns/";
Dictionary<string, string> namespaceToNewPrefixMapping = new Dictionary<string, string>();
public void AddNamespace(string namespaceUri, string newPrefix)
{
this.namespaceToNewPrefixMapping.Add(namespaceUri, newPrefix);
}
public void ChangePrefixes(XmlDocument doc)
{
XmlElement element = doc.DocumentElement;
XmlElement newElement = ChangePrefixes(doc, element);
doc.LoadXml(newElement.OuterXml);
}
private XmlElement ChangePrefixes(XmlDocument doc, XmlElement element)
{
string newPrefix;
if (this.namespaceToNewPrefixMapping.TryGetValue(element.NamespaceURI, out newPrefix))
{
XmlElement newElement = doc.CreateElement(newPrefix, element.LocalName, element.NamespaceURI);
List<XmlNode> children = new List<XmlNode>(element.ChildNodes.Cast<XmlNode>());
List<XmlAttribute> attributes = new List<XmlAttribute>(element.Attributes.Cast<XmlAttribute>());
foreach (XmlNode child in children)
{
newElement.AppendChild(child);
}
foreach (XmlAttribute attr in attributes)
{
newElement.Attributes.Append(attr);
}
element = newElement;
}
List<XmlAttribute> newAttributes = new List<XmlAttribute>();
bool modified = false;
for (int i = 0; i < element.Attributes.Count; i++)
{
XmlAttribute attr = element.Attributes[i];
if (this.namespaceToNewPrefixMapping.TryGetValue(attr.NamespaceURI, out newPrefix))
{
XmlAttribute newAttr = doc.CreateAttribute(newPrefix, attr.LocalName, attr.NamespaceURI);
newAttr.Value = attr.Value;
newAttributes.Add(newAttr);
modified = true;
}
else if (attr.NamespaceURI == XmlnsNamespace && this.namespaceToNewPrefixMapping.TryGetValue(attr.Value, out newPrefix))
{
XmlAttribute newAttr;
if (newPrefix != "")
{
newAttr = doc.CreateAttribute("xmlns", newPrefix, XmlnsNamespace);
}
else
{
newAttr = doc.CreateAttribute("xmlns");
}
newAttr.Value = attr.Value;
newAttributes.Add(newAttr);
modified = true;
}
else
{
newAttributes.Add(attr);
}
}
if (modified)
{
element.Attributes.RemoveAll();
foreach (var attr in newAttributes)
{
element.Attributes.Append(attr);
}
}
List<KeyValuePair<XmlNode, XmlNode>> toReplace = new List<KeyValuePair<XmlNode, XmlNode>>();
foreach (XmlNode child in element.ChildNodes)
{
XmlElement childElement = child as XmlElement;
if (childElement != null)
{
XmlElement newChildElement = ChangePrefixes(doc, childElement);
if (newChildElement != childElement)
{
toReplace.Add(new KeyValuePair<XmlNode, XmlNode>(childElement, newChildElement));
}
}
}
if (toReplace.Count > 0)
{
for (int i = 0; i < toReplace.Count; i++)
{
element.InsertAfter(toReplace[i].Value, toReplace[i].Key);
element.RemoveChild(toReplace[i].Key);
}
}
return element;
}
}
以下は、ReplacePrefixMessageEncodingBindingElement です。
public class ReplacePrefixMessageEncodingBindingElement : MessageEncodingBindingElement
{
MessageEncodingBindingElement inner;
Dictionary<string, string> namespaceToPrefixMapping = new Dictionary<string, string>();
public ReplacePrefixMessageEncodingBindingElement(MessageEncodingBindingElement inner)
{
this.inner = inner;
}
private ReplacePrefixMessageEncodingBindingElement(ReplacePrefixMessageEncodingBindingElement other)
{
this.inner = other.inner;
this.namespaceToPrefixMapping = new Dictionary<string, string>(other.namespaceToPrefixMapping);
}
public void AddNamespaceMapping(string namespaceUri, string newPrefix)
{
this.namespaceToPrefixMapping.Add(namespaceUri, newPrefix);
}
public override MessageEncoderFactory CreateMessageEncoderFactory()
{
return new ReplacePrefixMessageEncoderFactory(this.inner.CreateMessageEncoderFactory(), this.namespaceToPrefixMapping);
}
public override MessageVersion MessageVersion
{
get { return this.inner.MessageVersion; }
set { this.inner.MessageVersion = value; }
}
public override BindingElement Clone()
{
return new ReplacePrefixMessageEncodingBindingElement(this);
}
public override IChannelListener<TChannel> BuildChannelListener<TChannel>(BindingContext context)
{
context.BindingParameters.Add(this);
return context.BuildInnerChannelListener<TChannel>();
}
public override bool CanBuildChannelListener<TChannel>(BindingContext context)
{
return context.CanBuildInnerChannelListener<TChannel>();
}
public static CustomBinding ReplaceEncodingBindingElement(Binding originalBinding, Dictionary<string, string> namespaceToPrefixMapping)
{
CustomBinding custom = originalBinding as CustomBinding;
if (custom == null)
{
custom = new CustomBinding(originalBinding);
}
for (int i = 0; i < custom.Elements.Count; i++)
{
if (custom.Elements[i] is MessageEncodingBindingElement)
{
ReplacePrefixMessageEncodingBindingElement element = new ReplacePrefixMessageEncodingBindingElement((MessageEncodingBindingElement)custom.Elements[i]);
foreach (var mapping in namespaceToPrefixMapping)
{
element.AddNamespaceMapping(mapping.Key, mapping.Value);
}
custom.Elements[i] = element;
break;
}
}
return custom;
}