2010-03-17 18 views
1

J'ai eu ce problème pendant un moment et cela me rend fou. J'essaie de créer un client (en C# .NET 2.0) qui utilisera SAML 1.1 pour se connecter à un serveur WebLogic 10.0 (c'est-à-dire, un scénario de connexion unique, en utilisant un profil de navigateur/post). Le client est sur une machine WinXP et le serveur WebLogic est sur une boîte RHEL 5.Weblogic 10.0: SAMLSignedObject.verify() n'a pas réussi à valider la valeur de signature

J'ai principalement basé mon client sur du code dans l'exemple ici: http://www.codeproject.com/KB/aspnet/DotNetSamlPost.aspx (la source a une section pour SAML 1.1).

I mis en place WebLogic basé sur des instructions pour SAML site de destination d'ici: http://www.oracle.com/technology/pub/articles/dev2arch/2006/12/sso-with-saml4.html

J'ai créé un certificat à l'aide makecert fourni avec 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 

Puis j'ai installé le .pfx à mon répertoire de certificat personnel et installé le fichier .cer dans WebLogic SAML Identity Asserter V2. J'ai lu sur un autre site que le formatage de la réponse pour être lisible (c'est-à-dire, en ajoutant des espaces) à la réponse après la signature causerait ce problème, donc j'ai essayé diverses combinaisons d'activation/désactivation des paramètres XMLWriter et d'activation/désactivation .PreserveWhiteSpace lors du chargement du document XML, et rien de cela ne fait aucune différence. J'ai imprimé le SignatureValue à la fois avant que le message est codé/envoyé et après qu'il arrive/est décodé, et ils sont identiques. Donc, pour être clair: la réponse semble être formée, codée, envoyée et décodée correctement (je vois la réponse complète dans les journaux de WebLogic). WebLogic trouve le certificat que je veux qu'il utilise, vérifie qu'une clé a été fournie, obtient les informations signées et ne parvient pas à valider la signature.

code:

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; 

    } 

Pour ouvrir la page dans mon application client,

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

Répondre

1

Bordel je l'ai enfin trouvé. Eh bien, une partie, de toute façon. Dans la fonction SignDoc(), je devais ajouter une autre transformation à la référence:

reference.AddTransform(new XmlDsigExcC14NTransform()); 

qui, autant que je peux dire changer la méthode de canonicalisation à l'exclusif. Je pensais que c'était déjà fait (puisque l'élément CanonicalizationMethod apparaissait dans la réponse), mais apparemment pas.

Maintenant, je suis frappé par une erreur différente, en me disant que la méthode de confirmation du sujet «porteur» n'est pas valide. Je pensais avoir lu quelque part que la méthode du porteur était celle à utiliser pour Browser/POST, mais à ce stade, je suis tellement heureux de passer la première erreur que je m'en soucie à peine.

+0

Le problème du porteur était une faute de frappe de ma part; raté le "cm" juste avant le "porteur" dans l'urne. * Facepalm * – joshea