2010-03-17 6 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

Я создал сертификат с помощью MakeCert, поставляемого с 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 

Затем я установил .pfx на мой каталог личных сертификатов, и установил .cer в WebLogic SAML Identity Asserter V2.

Я прочитал на другом сайте, что форматирование ответа на чтение (то есть добавление пробелов) в ответ после подписания вызовет эту проблему, поэтому я попытался использовать различные комбинации включения/выключения. Идентификатор 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 появился в Response), но, по-видимому, нет.

Теперь меня поразила другая ошибка, сообщив мне, что метод подтверждения субъекта «несущий» недействителен. Мне показалось, что я где-то читал, что метод-носитель - тот, который используется для браузера/POST, но на данный момент я так счастлив преодолеть первую ошибку, о которой я даже не забочусь.

+0

Проблема с носителем была опечаткой с моей стороны; пропустил «см» прямо перед «носителем» в урне. * Facepalm * – joshea