2011-08-23 192 views
1

我设计了一个向供应商发送SOAP请求的WCF.net客户端。为了满足供应商的WS安全要求,我必须创建一个自定义SOAP头,并将自定义头的请求发送到供应商方的Web服务。所以,我通过实现从的MessageHeader derieved一个新的类创建一个自定义标题(见下文)签名失败核心验证错误

public class SignOnlyMessageHeader : MessageHeader 
{ 
    private const string PREFIX_CP = "wsse"; 

    public string m_Username { get; set; } 
    public string m_Envelope { get; set; } 

    public SignOnlyMessageHeader(string Username, string Envelope) 
    { 
     m_Username = Username; 
     m_Envelope = Envelope; 
    } 

    public override string Name 
    { 
     get { return "wsse:Security"; } 
    } 

    public override string Namespace 
    { 
     get { return null; } 
    } 

    public override bool MustUnderstand 
    { 
     get 
     { 
      return false; 
     } 
    } 

    protected override void OnWriteStartHeader(XmlDictionaryWriter writer, MessageVersion messageVersion) 
    { 
     base.OnWriteStartHeader(writer, messageVersion); 
     writer.WriteXmlnsAttribute(PREFIX_CP, "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"); 
    } 

    protected override void OnWriteHeaderContents(XmlDictionaryWriter writer, MessageVersion messageVersion) 
    { 
     writer.WriteStartElement(PREFIX_CP, "UsernameToken", null); 
     writer.WriteAttributeString("wsu:Id", "UsernameToken-20"); 
     writer.WriteXmlnsAttribute("wsu", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"); 
     writer.WriteElementString(PREFIX_CP, "Username", null, m_Username); 
     writer.WriteEndElement(); 
     SignXmlFile(writer); 
    } 

    public void SignXmlFile(XmlDictionaryWriter writer) 
    { 
     string certificatePath = "C:\\Users\\22428-cert.p12"; 
     System.Security.Cryptography.X509Certificates.X509Certificate2 cert = new X509Certificate2(certificatePath, "changeit"); 

     // Create a new XML document. 
     XmlDocument doc = new XmlDocument(); 

     // Format the document to ignore white spaces. 
     doc.PreserveWhitespace = false; 
     doc.LoadXml(m_Envelope); 

     // Create a SignedXml object. 
     SignedXml signedXml = new SignedXml(doc); 

     // Add the key to the SignedXml document. 
     //signedXml.SigningKey = Key; 
     signedXml.SigningKey = cert.PrivateKey; 

     // Create a new KeyInfo object. 
     KeyInfo keyInfo = new KeyInfo(); 
     keyInfo.Id = ""; 

     // Load the certificate into a KeyInfoX509Data object 
     // and add it to the KeyInfo object. 
     KeyInfoX509Data keyInfoData = new KeyInfoX509Data(); 
     keyInfoData.AddCertificate(cert); 
     keyInfo.AddClause(keyInfoData); 
     // Add the KeyInfo object to the SignedXml object. 
     signedXml.KeyInfo = keyInfo; 

     signedXml.SignedInfo.CanonicalizationMethod = "http://www.w3.org/2001/10/xml-exc-c14n#"; 

     // Create a reference to be signed. 
     Reference reference = new Reference(); 
     reference.Uri = ""; 

     // Add an enveloped transformation to the reference. 
     XmlDsigEnvelopedSignatureTransform env = new XmlDsigEnvelopedSignatureTransform(); 
     reference.AddTransform(env); 
     reference.DigestMethod = "http://www.w3.org/2001/04/xmlenc#sha256"; 

     // Add the reference to the SignedXml object. 
     signedXml.AddReference(reference); 
     signedXml.Signature.Id = ""; 

     // Compute the signature. 
     signedXml.ComputeSignature(); 

     // Get the XML representation of the signature and save 
     // it to an XmlElement object. 
     XmlElement xmlDigitalSignature = signedXml.GetXml(); 

     // Check the signature and return the result. 
     if (!signedXml.CheckSignature(new X509Certificate2(certificatePath, "changeit"), true)) 
     { 
      Console.WriteLine("invalid signature"); 
     } 

     xmlDigitalSignature.WriteTo(writer); 
    } 

所以创建自定义标题下课后,我推翻了IClientMessageInspector.BeforeSendRequest方法来拦截传出请求,并添加我的自定义标题肥皂请求。请参阅下面的代码,

object IClientMessageInspector.BeforeSendRequest(ref System.ServiceModel.Channels.Message request, IClientChannel channel) 
    { 
     request.Headers.RemoveAt(0); 
     SignOnlyMessageHeader header = new SignOnlyMessageHeader("x509user", env); 
     request.Headers.Add(header); 
     return null; 
    } 

最终结果是我拦截SOAP请求并正确使用自定义标头替换当前标头。在发送请求之前,我检查了更新后的SOAP请求(放置了一个断点),该结构完全匹配供应商请求的内容。但是,在供应商端处理请求后,我收到一个错误。它只说“签名失败的核心验证”。我认为我正确地在“SignXmlFile”方法中签署了整个信封。我甚至检查了方法中的有效性(if(!signedXml.CheckSignature(new X509Certificate2(certificatePath,“changeit”),true))),该语句返回一个假,表示签名有效。

我在做什么错?

+0

你可以使用Fiddler或Wireshark来查看*完全*你的应用程序发送的线路是什么? – Amy

+0

你确定你必须忽略空白吗?有或没有空格的 –

+0

,我得到相同的错误。 – wcfvemi

回答

0

嗯,我尝试过,尝试了一些方法,我拦截标题,然后我用Signature注入标题..验证失败。作为解决办法,我从我的.net客户端中分离出整个标题。我将我的请求通过soap绑定到XML网关,我们将网关配置为拦截请求并添加必要的头部init,并将请求转发给外部供应商。有效。