2010-03-17 62 views
1

我一直有這個問題一段時間,這讓我瘋狂。我正在嘗試創建一個客戶端(使用C#.NET 2.0),它將使用SAML 1.1登錄到WebLogic 10.0服務器(即使用瀏覽器/郵件配置文件的單一登錄方案)。客戶端位於WinXP機器上,WebLogic服務器位於RHEL 5機器上。Weblogic 10.0:SAMLSignedObject.verify()未能驗證簽名值

我基於我的客戶端很大程度上在這裏的示例代碼:http://www.codeproject.com/KB/aspnet/DotNetSamlPost.aspx(源代碼有一個SAML 1.1部分)。

我建立基於WebLogic上從這裏SAML目標站點的說明:http://www.oracle.com/technology/pub/articles/dev2arch/2006/12/sso-with-saml4.html

我使用隨VS 2005

makecert -r -pe -n "CN=whatever" -b 01/01/2010 -e 01/01/2011 -sky exchange whatever.cer -sv whatever.pvk 
pvk2pfx.exe -pvk whatever.pvk -spc whatever.cer -pfx whatever.pfx 

來到makecert創建的證書然後我安裝的.pfx我個人證書目錄,並將.cer安裝到WebLogic SAML標識聲明器V2中。

我在另一個網站上看到,格式化響應的可讀性(即,添加空白)到簽名後的響應會導致此問題,所以我嘗試了各種組合打開/關閉.Indent XMLWriterSettings和打開/關閉.PreserveWhiteSpace加載XML文檔時,沒有任何區別。在消息編碼/發送之前以及到達/解碼之後,我已經打印了SignatureValue,它們是相同的。

因此,要清楚:響應似乎已經形成,編碼,發送和解碼正常(我在WebLogic日誌中看到完整的響應)。 WebLogic找到我希望它使用的證書,驗證是否提供了密鑰,獲取簽名信息,然後驗證簽名失敗。

代碼:

public string createResponse(Dictionary<string, string> attributes){ 
    ResponseType response = new ResponseType(); 
    // Create Response 
    response.ResponseID = "_" + Guid.NewGuid().ToString(); 

    response.MajorVersion = "1"; 
    response.MinorVersion = "1"; 
    response.IssueInstant = System.DateTime.UtcNow; 
    response.Recipient = "http://theWLServer/samlacs/acs"; 

    StatusType status = new StatusType(); 

    status.StatusCode = new StatusCodeType(); 
    status.StatusCode.Value = new XmlQualifiedName("Success", "urn:oasis:names:tc:SAML:1.0:protocol"); 

    response.Status = status; 

    // Create Assertion 
    AssertionType assertionType = CreateSaml11Assertion(attributes); 

    response.Assertion = new AssertionType[] {assertionType}; 

    //Serialize 
    XmlSerializerNamespaces ns = new XmlSerializerNamespaces(); 
    ns.Add("samlp", "urn:oasis:names:tc:SAML:1.0:protocol"); 
    ns.Add("saml", "urn:oasis:names:tc:SAML:1.0:assertion"); 
    XmlSerializer responseSerializer = 
      new XmlSerializer(response.GetType()); 
    StringWriter stringWriter = new StringWriter(); 
    XmlWriterSettings settings = new XmlWriterSettings(); 
    settings.OmitXmlDeclaration = true; 
    settings.Indent = false;//I've tried both ways, for the fun of it 
    settings.Encoding = Encoding.UTF8; 

    XmlWriter responseWriter = XmlTextWriter.Create(stringWriter, settings); 

    responseSerializer.Serialize(responseWriter, response, ns); 
    responseWriter.Close(); 

    string samlString = stringWriter.ToString(); 
    stringWriter.Close(); 
    // Sign the document 
    XmlDocument doc = new XmlDocument(); 
    doc.PreserveWhiteSpace = true; //also tried this both ways to no avail 
    doc.LoadXml(samlString); 
    X509Certificate2 cert = null; 

    X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser); 
    store.Open(OpenFlags.ReadOnly); 
    X509Certificate2Collection coll = store.Certificates.Find(X509FindType.FindBySubjectDistinguishedName, "distName", true); 
    if (coll.Count < 1) { 
     throw new ArgumentException("Unable to locate certificate"); 
    } 
    cert = coll[0]; 
    store.Close(); 

    //this special SignDoc just overrides a function in SignedXml so 
    //it knows to look for ResponseID rather than ID 
    XmlElement signature = SamlHelper.SignDoc(
      doc, cert, "ResponseID", response.ResponseID); 

    doc.DocumentElement.InsertBefore(signature, 
      doc.DocumentElement.ChildNodes[0]); 

    // Base64Encode and URL Encode 
    byte[] base64EncodedBytes = 
      Encoding.UTF8.GetBytes(doc.OuterXml); 

    string returnValue = System.Convert.ToBase64String(
      base64EncodedBytes); 

    return returnValue; 
} 

private AssertionType CreateSaml11Assertion(Dictionary<string, string> attributes){ 
    AssertionType assertion = new AssertionType(); 
     assertion.AssertionID = "_" + Guid.NewGuid().ToString(); 
     assertion.Issuer = "madeUpValue"; 
     assertion.MajorVersion = "1"; 
     assertion.MinorVersion = "1"; 
     assertion.IssueInstant = System.DateTime.UtcNow; 

     //Not before, not after conditions 
     ConditionsType conditions = new ConditionsType(); 
     conditions.NotBefore = DateTime.UtcNow; 
     conditions.NotBeforeSpecified = true; 
     conditions.NotOnOrAfter = DateTime.UtcNow.AddMinutes(10); 
     conditions.NotOnOrAfterSpecified = true; 
     //Name Identifier to be used in Saml Subject 
     NameIdentifierType nameIdentifier = new NameIdentifierType(); 
     nameIdentifier.NameQualifier = domain.Trim(); 
     nameIdentifier.Value = subject.Trim(); 

     SubjectConfirmationType subjectConfirmation = new SubjectConfirmationType(); 
     subjectConfirmation.ConfirmationMethod = new string[] { "urn:oasis:names:tc:SAML:1.0:cm:bearer" }; 
     // 
     // Create some SAML subject. 
     SubjectType samlSubject = new SubjectType(); 

     AttributeStatementType attrStatement = new AttributeStatementType(); 
     AuthenticationStatementType authStatement = new AuthenticationStatementType(); 
     authStatement.AuthenticationMethod = "urn:oasis:names:tc:SAML:1.0:am:password"; 
     authStatement.AuthenticationInstant = System.DateTime.UtcNow; 

     samlSubject.Items = new object[] { nameIdentifier, subjectConfirmation}; 

     attrStatement.Subject = samlSubject; 
     authStatement.Subject = samlSubject; 

     IPHostEntry ipEntry = 
      Dns.GetHostEntry(System.Environment.MachineName); 

     SubjectLocalityType subjectLocality = new SubjectLocalityType(); 
     subjectLocality.IPAddress = ipEntry.AddressList[0].ToString(); 

     authStatement.SubjectLocality = subjectLocality; 

     attrStatement.Attribute = new AttributeType[attributes.Count]; 
     int i=0; 
     // Create SAML attributes. 
     foreach (KeyValuePair<string, string> attribute in attributes) { 
      AttributeType attr = new AttributeType(); 
      attr.AttributeName = attribute.Key; 
      attr.AttributeNamespace= domain; 
      attr.AttributeValue = new object[] {attribute.Value}; 
      attrStatement.Attribute[i] = attr; 
      i++; 
     } 
     assertion.Conditions = conditions; 

     assertion.Items = new StatementAbstractType[] {authStatement, attrStatement}; 

     return assertion; 
} 

private static XmlElement SignDoc(XmlDocument doc, X509Certificate2 cert2, string referenceId, string referenceValue) { 
     // Use our own implementation of SignedXml 
     SamlSignedXml sig = new SamlSignedXml(doc, referenceId); 
     // Add the key to the SignedXml xmlDocument. 
     sig.SigningKey = cert2.PrivateKey; 

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

     reference.Uri= String.Empty; 
     reference.Uri = "#" + referenceValue; 

     // Add an enveloped transformation to the reference. 
     XmlDsigEnvelopedSignatureTransform env = new  
      XmlDsigEnvelopedSignatureTransform(); 
     reference.AddTransform(env); 

     // Add the reference to the SignedXml object. 
     sig.AddReference(reference); 

     // Add an RSAKeyValue KeyInfo (optional; helps recipient find key to validate). 
     KeyInfo keyInfo = new KeyInfo(); 
     keyInfo.AddClause(new KeyInfoX509Data(cert2)); 
     sig.KeyInfo = keyInfo; 

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

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

     return xmlDigitalSignature; 

    } 

要在我的客戶端應用程序打開網頁,

string postData = String.Format("SAMLResponse={0}&APID=ap_00001&TARGET={1}", System.Web.HttpUtility.UrlEncode(builder.buildResponse("http://theWLServer/samlacs/acs",attributes)), "http://desiredURL"); 
webBrowser.Navigate("http://theWLServer/samlacs/acs", "_self", Encoding.UTF8.GetBytes(postData), "Content-Type: application/x-www-form-urlencoded"); 

回答

1

哇靠我終於找到了。好吧,無論如何,它的一部分。在SignDoc()函數,我不得不添加另一個轉變到參考:

reference.AddTransform(new XmlDsigExcC14NTransform()); 

其中,據我可以告訴改變規範化方法的專屬。我認爲已經完成了(因爲CanonicalizationMethod元素已經出現在響應中),但顯然不是。

現在我遇到了一個不同的錯誤,告訴我「持票人」主體確認方法無效。我以爲自己在某個地方讀了那個使用方法是用於瀏覽器/ POST的方法,但在這一點上,我很高興能夠通過我幾乎不在乎的第一個錯誤。

+0

無人問題是我的錯;錯過了「釐米」之前的「承載」在甕。 *捂臉* – joshea 2010-03-24 19:31:53