2012-04-19 14 views
3

私は銀行からAPIを実装しており、セキュリティトークンを提供する必要があります。WCF石鹸応答でセキュリティトークンを実装する方法は?

<soapenv:Header> 
    <tpw:BinarySecurityToken ValueType="MAC" Id="DesMacToken" EncodingType="Base64" Value="**xvz**"/> 
</soapenv:Header> 

私は、各メッセージのボディに8バイトのMAC値を生成する必要が彼らのドキュメントによると:各SOAPメッセージのヘッダには、以下のように見えるものがあります。 MACは、ブロック暗号としてCBC-MACアルゴリズムおよびDESによって生成される。各メッセージのsoapenv:Bodyタグの内容は、MAC計算のデータとして使用されます。

私の質問はどのようにWCFにこれをさせるのですか?次のコードをまとめてMAC値を作成しましたが、これをどのメッセージのヘッダーに入れるのかは不明です。

private string GenerateMAC(string SoapXML) 
     { 
      ASCIIEncoding encoding = new ASCIIEncoding(); 

      //Convert from Hex to Bin 
      byte[] Key = StringToByteArray(HexKey); 
      //Convert String to Bytes 
      byte[] XML = encoding.GetBytes(SoapXML); 

      //Perform the Mac goodies 
      MACTripleDES DesMac = new MACTripleDES(Key); 
      byte[] Mac = DesMac.ComputeHash(XML); 

      //Base64 the Mac 
      string Base64Mac = Convert.ToBase64String(Mac); 

      return Base64Mac; 
     } 

     public static byte[] StringToByteArray(string Hex) 
     { 
      if (Hex.Length % 2 != 0) 
      { 
       throw new ArgumentException(); 
      } 

      byte[] HexAsBin = new byte[Hex.Length/2]; 
      for (int index = 0; index < HexAsBin.Length; index++) 
      { 
       string bytevalue = Hex.Substring(index * 2, 2); 
       HexAsBin[index] = Convert.ToByte(bytevalue, 16); 
      } 

      return HexAsBin; 
     } 

ご協力いただきますようお願い申し上げます。

詳細情報: 私はサービスリファレンスとして使用しているWSDLを提供しています。

[System.Diagnostics.DebuggerStepThroughAttribute()] 
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")] 
[System.ServiceModel.MessageContractAttribute(WrapperName="LogonRequest", WrapperNamespace="http://webservice.com", IsWrapped=true)] 
public partial class LogonRequest { 

    [System.ServiceModel.MessageHeaderAttribute(Namespace="http://webservice.com")] 
    public DataAccess.BankService.BinarySecurityToken BinarySecurityToken; 

のBinarySecurityToken(つまり、ヘッダーに行く)は次のようになります:

[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "4.0.30319.233")] 
    [System.SerializableAttribute()] 
    [System.Diagnostics.DebuggerStepThroughAttribute()] 
    [System.ComponentModel.DesignerCategoryAttribute("code")] 
    [System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="http://webservice.com")] 
    public partial class BinarySecurityToken : object, System.ComponentModel.INotifyPropertyChanged { 

     private string valueTypeField; 

     private string idField; 

     private string encodingTypeField; 

     private string valueField; 

     public BinarySecurityToken() { 
      this.valueTypeField = "MAC"; 
      this.idField = "DesMacToken"; 
      this.encodingTypeField = "Base64"; 
     } 

答えて

5

私は最近、このような何かをしなければならなかったと私はやってしまっ送られる応答の例IClientMessageInspectorを実装し、BeforeSendRequestメソッドを使用して、私のヘッダーのデータを作成し、それをSOAP要求に取り込むという動作を作成していました。

public class SoapHeaderBehaviour : BehaviorExtensionElement, IClientMessageInspector 
{ 
    public void AfterReceiveReply(ref Message reply, object correlationState) { } 
    public object BeforeSendRequest(ref Message request, IClientChannel channel) 
    { 
     var security = new Security(); // details irrelevant 
     var messageHeader = MessageHeader.CreateHeader("Security", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd", security, new ConcreteXmlObjectSerializer(typeof(Security)), true); 
     request.Headers.Add(messageHeader); 

     return null; 
    } 

    protected override object CreateBehavior() { return new SoapHeaderBehaviour(); } 
    public override Type BehaviorType { get { return GetType(); } } 
} 

ConcreteXmlObjectSerializerだけで働いていた私はどこか、インターネットで見つけクラス(残念ながら今はそれを見つけるように見えることはできません)です。これは、すべてのsystem.serviceModelノードの下に(3段階で設定ファイルを経由してWCFのクライアントエンドポイントにフックされ

public class ConcreteXmlObjectSerializer : XmlObjectSerializer 
{ 
    readonly Type objectType; 
    XmlSerializer serializer; 

    public ConcreteXmlObjectSerializer(Type objectType) 
     : this(objectType, null, null) 
    { 
    } 

    public ConcreteXmlObjectSerializer(Type objectType, string wrapperName, string wrapperNamespace) 
    { 
     if (objectType == null) 
      throw new ArgumentNullException("objectType"); 
     if ((wrapperName == null) != (wrapperNamespace == null)) 
      throw new ArgumentException("wrapperName and wrapperNamespace must be either both null or both non-null."); 
     if (wrapperName == string.Empty) 
      throw new ArgumentException("Cannot be the empty string.", "wrapperName"); 

     this.objectType = objectType; 
     if (wrapperName != null) 
     { 
      XmlRootAttribute root = new XmlRootAttribute(wrapperName); 
      root.Namespace = wrapperNamespace; 
      this.serializer = new XmlSerializer(objectType, root); 
     } 
     else 
      this.serializer = new XmlSerializer(objectType); 
    } 

    public override bool IsStartObject(XmlDictionaryReader reader) 
    { 
     throw new NotImplementedException(); 
    } 

    public override object ReadObject(XmlDictionaryReader reader, bool verifyObjectName) 
    { 
     Debug.Assert(serializer != null); 
     if (reader == null) throw new ArgumentNullException("reader"); 
     if (!verifyObjectName) 
      throw new NotSupportedException(); 

     return serializer.Deserialize(reader); 
    } 

    public override void WriteStartObject(XmlDictionaryWriter writer, object graph) 
    { 
     throw new NotImplementedException(); 
    } 

    public override void WriteObjectContent(XmlDictionaryWriter writer, object graph) 
    { 
     if (writer == null) throw new ArgumentNullException("writer"); 
     if (writer.WriteState != WriteState.Element) 
      throw new SerializationException(string.Format("WriteState '{0}' not valid. Caller must write start element before serializing in contentOnly mode.", 
       writer.WriteState)); 
     using (MemoryStream memoryStream = new MemoryStream()) 
     { 
      using (XmlDictionaryWriter bufferWriter = XmlDictionaryWriter.CreateTextWriter(memoryStream, Encoding.UTF8)) 
      { 
       serializer.Serialize(bufferWriter, graph); 
       bufferWriter.Flush(); 
       memoryStream.Position = 0; 
       using (XmlReader reader = new XmlTextReader(memoryStream)) 
       { 
        reader.MoveToContent(); 
        writer.WriteAttributes(reader, false); 
        if (reader.Read()) // move off start node (we want to skip it) 
        { 
         while (reader.NodeType != XmlNodeType.EndElement) // also skip end node. 
          writer.WriteNode(reader, false); // this will take us to the start of the next child node, or the end node. 
         reader.ReadEndElement(); // not necessary, but clean 
        } 
       } 
      } 
     } 
    } 

    public override void WriteEndObject(XmlDictionaryWriter writer) 
    { 
     throw new NotImplementedException(); 
    } 

    public override void WriteObject(XmlDictionaryWriter writer, object graph) 
    { 
     Debug.Assert(serializer != null); 
     if (writer == null) throw new ArgumentNullException("writer"); 
     serializer.Serialize(writer, graph); 
    } 
} 

:ここではそのためのコードである

拡張子を登録するには、

<extensions> 
    <behaviorExtensions> 
    <add name="ClientSoapHeaderAdderBehaviour" 
     type="MyNamespace.SoapHeaderBehaviour, MyAssembly, Version=My.Version, Culture=neutral, PublicKeyToken=null" /> 
    </behaviorExtensions> 
</extensions> 

エンドポイント動作を使用してそれを作成する

<behaviors> 
    <endpointBehaviors> 
    <behavior name="MyEndpointBehaviours"> 
     <ClientSoapHeaderAdderBehaviour /> 
    </behavior> 
    </endpointBehaviors> 
</behaviors> 

エンドポイントの動作をクライアントのエンドポイントに送信する

<client> 
    <endpoint address="blah" binding="basicHttpBinding" 
    bindingConfiguration="blah" contract="blah" 
    name="blah" 
    behaviorConfiguration="MyEndpointBehaviours"/> 
</client> 

+0

ありがとうございました!いくつかの調整をすると、それは完全に機能しました! – Dylan

0

DavidJonesは、正しい答えを持っていたが、誰にも似た何かをする必要がある場合には、私は私のクラスを投稿したかった:

public class SoapHeaderBehaviour : BehaviorExtensionElement, IClientMessageInspector, IEndpointBehavior 
    { 
     public void AfterReceiveReply(ref Message reply, object correlationState) 
     { 
     } 

     public object BeforeSendRequest(ref Message request, IClientChannel channel) 
     { 
      try 
      { 
       // Get the request into an XDocument 
       var memoryStream = new MemoryStream(); 

       var writer = XmlDictionaryWriter.CreateTextWriter(memoryStream); 
       request.WriteMessage(writer); 
       writer.Flush(); 
       memoryStream.Position = 0; 
       var xmlDoc = new XmlDocument(); 
       xmlDoc.Load(memoryStream); 

       // get the body tag 
       XmlNode bodyNode = FindNode("body", xmlDoc); 
       Debug.Assert(bodyNode != null, "Unable to find the BODY in the SOAP message"); 
       if (bodyNode != null) 
       { 
        string MAC = GenerateMAC(bodyNode.InnerXml); 

        // replace the relevant item in the header 
        XmlNode tokenNode = FindNode("binarysecuritytoken", xmlDoc); 
        Debug.Assert(tokenNode != null, "Unable to find the BinarySecurityToken in the SOAP message"); 

        if (tokenNode != null) 
        { 
         tokenNode.Attributes["Value"].Value = MAC; 

         // recreate the request 
         memoryStream = new MemoryStream(); 
         writer = XmlDictionaryWriter.CreateTextWriter(memoryStream); 
         xmlDoc.WriteTo(writer); 
         writer.Flush(); 
         memoryStream.Position = 0; 
         var reader = XmlDictionaryReader.CreateTextReader(memoryStream, XmlDictionaryReaderQuotas.Max); 
         var newRequest = Message.CreateMessage(reader, int.MaxValue, request.Version); 
         request = newRequest; 
        } 
       } 
      } 
      catch (Exception ex) 
      { 
      } 

      return null; 
     } 

     private XmlNode FindNode(string name, XmlDocument xmlDoc) 
     { 
      XmlNode node = null; 
      for (int i = 0; i < xmlDoc.ChildNodes.Count; i++) 
      { 
       node = FindNode(name, xmlDoc.ChildNodes[i]); 
       if (node != null) 
        break; 
      } 

      return node; 
     } 

     private XmlNode FindNode(string name, XmlNode parentNode) 
     { 
      if (parentNode != null && parentNode.Name.ToLower().Contains(name)) 
      { 
       return parentNode; 
      } 

      XmlNode childNode = null; 
      for (int i = 0; i < parentNode.ChildNodes.Count; i++) 
      { 
       childNode = FindNode(name, parentNode.ChildNodes[i]); 
       if (childNode != null) 
        break; 
      } 

      return childNode; 
     } 

     private string GenerateMAC(string soapXML) 
     { 
      // get the key from the web.config file 
      var key = ConfigurationManager.AppSettings["Key"]; 

      ASCIIEncoding encoding = new ASCIIEncoding(); 

      //Convert from Hex to Bin 
      byte[] keyBytes = StringToByteArray(key); 
      //Convert String to Bytes 
      byte[] xmlBytes = encoding.GetBytes(soapXML); 

      //Perform the Mac goodies 
      MACTripleDES desMac = new MACTripleDES(keyBytes); 
      byte[] macBytes = desMac.ComputeHash(xmlBytes); 

      //Base64 the Mac 
      string base64Mac = Convert.ToBase64String(macBytes); 
      return base64Mac; 
     } 

     private static byte[] StringToByteArray(string hex) 
     { 
      if (hex.Length % 2 != 0) 
      { 
       throw new ArgumentException(); 
      } 

      byte[] hexBytes = new byte[hex.Length/2]; 
      for (int index = 0; index < hexBytes.Length; index++) 
      { 
       string bytevalue = hex.Substring(index * 2, 2); 
       hexBytes[index] = Convert.ToByte(bytevalue, 16); 
      } 

      return hexBytes; 
     } 

     protected override object CreateBehavior() 
     { 
      return new SoapHeaderBehaviour(); 
     } 

     public override Type BehaviorType 
     { 
      get 
      { 
       return GetType(); 
      } 
     } 

     public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) 
     { 
     } 

     public void ApplyClientBehavior(ServiceEndpoint serviceEndpoint, ClientRuntime behavior) 
     { 
      behavior.MessageInspectors.Add(this); 
      // behavior.MessageInspectors.Add(new FaultMessageInspector()); 
     } 

     public void ApplyDispatchBehavior(ServiceEndpoint serviceEndpoint, EndpointDispatcher endpointDispatcher) 
     { 
     } 

     public void Validate(ServiceEndpoint serviceEndpoint) 
     { 
     } 
    } 
関連する問題