gpt4 book ai didi

c# - 如何在 WCF soap 响应中实现安全 token ?

转载 作者:行者123 更新时间:2023-11-30 15:04:30 25 4
gpt4 key购买 nike

我正在实现银行的 API,他们需要提供安全 token 。在每个 soap 消息的标题中都有如下内容:

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

根据他们的文档,我需要在每条消息的正文中生成一个 8 字节的 MAC 值。 MAC 由 CBC-MAC 算法和 DES 作为分组密码生成。每条消息的soapenv:Body标签的内容作为MAC计算的数据。

所以我的问题是如何让 WCF 执行此操作?我已将以下代码放在一起以创建 MAC 值,但不确定如何将其放入每条消息的 header 中。

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(位于 header 中)如下所示:

 [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";
}

最佳答案

我最近不得不做这样的事情,我最终做的是创建一个实现 IClientMessageInspector 的行为,并使用 BeforeSendRequest 方法为我的 header 创建数据,然后将其填充到 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 是我在互联网某个地方找到的一个类(遗憾的是现在似乎找不到),它刚刚起作用。这是相关代码:

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);
}
}

然后分 3 步通过配置文件将其连接到 WCF 客户端端点(全部在 system.serviceModel 节点下:

注册扩展

<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>

希望对你有帮助。

关于c# - 如何在 WCF soap 响应中实现安全 token ?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10234219/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com