Question

J'ai ce problème depuis 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 seul scénario d'authentification, en utilisant le navigateur / profil de post). Le client est sur une machine WinXP et le serveur WebLogic est sur une boîte RHEL 5.

J'ai basé mon client en grande partie sur le code dans l'exemple ici: http://www.codeproject.com/kb/aspnet/dotnetsamlpost.aspx (La source a une section pour SAML 1.1).

J'ai configuré WebLogic en fonction des instructions pour le site de destination SAML à partir d'ici:http://www.oracle.com/technology/pub/articles/dev2arch/2006/12/sso-with-saml4.html

J'ai créé un certificat à l'aide de MakeCert fournis 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

Ensuite, j'ai installé le .pfx sur mon répertoire de certificat personnel et j'ai installé le .Cer dans l'affirmeur d'identité SAML WebLogic V2.

J'ai lu sur un autre site qui format la réponse à être lisible (c'est-à-dire, l'ajout d'espace) à la réponse après la signature causerait ce problème, j'ai donc essayé diverses combinaisons d'allumage / désactiver. Le document XML, et rien de tout cela n'a fait de différence. J'ai imprimé la valeur de signature à la fois avant que le message ne soit encodé / envoyé et après son arrivée / se fait décoder, et ils sont les mêmes.

Donc, pour être clair: la réponse semble être formée, codée, envoyée et décodée très bien (je vois la réponse complète dans les journaux WebLogic). WebLogic trouve le certificat que je veux qu'il utilise, vérifie qu'une clé a été fournie, obtient les informations signées, puis ne valide pas 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");
Était-ce utile?

La solution

CRAP SAINT Je l'ai finalement trouvé. Eh bien, en partie, de toute façon. Dans la fonction SignDoc (), j'ai dû ajouter une autre transformation à la référence:

reference.AddTransform(new XmlDsigExcC14NTransform());

qui, pour autant que je sache, change la méthode canonicalisation à l'exclusivité. Je pensais que cela était déjà fait (puisque l'élément canonicalisation de la méthode appartenait dans la réponse), mais apparemment pas.

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

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top