2011-08-23 185 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,並將請求轉發給外部供應商。有效。