2011-11-16 13 views
12

का निर्माण मेरा लक्ष्य एकल लॉग आउट प्रोटोकॉल को लागू करना है। कैसे standar काम करता है और सबसे पहले मैं समझ रहा हूँ कैसे मैं इसे अपने परिदृश्य में फिट कर सकते हैं: ADFS 2.0 IdP के रूप में, मेरे लिएएक हस्ताक्षरित SAML2 लॉगऑट अनुरोध

क्या मैं इस समय कर रहा हूँ एक "ब्लैक बॉक्स" की तरह है बगल में है :

  1. मेरी IdP

  2. IdP के लिए एक <AuthnRequest> भेजें मुझे साख के लिए पूछता है, मैं उन्हें प्रदान करते हैं और प्रवेश सफलतापूर्वक मिलता है।

  3. SessionIndex मूल्य के रूप में प्राप्त करें और निर्माण करती है एक <LogoutRequest>

<samlp:LogoutRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="_135ad2fd-b275-4428-b5d6-3ac3361c3a7f" Version="2.0" Destination="https://idphost/adfs/ls/" IssueInstant="2008-06-03T12:59:57Z"><saml:Issuer>myhost</saml:Issuer><NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress" NameQualifier="https://idphost/adfs/ls/">[email protected]</NameID<samlp:SessionIndex>_0628125f-7f95-42cc-ad8e-fde86ae90bbe</samlp:SessionIndex></samlp:LogoutRequest>

  1. ऊपर <LogoutRequest>

  2. contructs अगले स्ट्रिंग ले लो और में यह सांकेतिक शब्दों में बदलना Base64 : SAMLRequest=base64encodedRequest&SigAlg=http%3A%2F%2Fwww.w3.org%2F2000%2F09%2Fxmldsig%23rsa-sha1

  3. ऊपर स्ट्रिंग के साथ हस्ताक्षर

  4. एनकोड उत्पन्न बेस 64

  5. में हस्ताक्षर अनुरोध भेजें: https://"https://idphost/adfs/ls/?SAMLRequest=base64encodedRequest&SigAlg=http%3A%2F%2Fwww.w3.org%2F2000%2F09%2Fxmldsig%23rsa-sha1&Signature=base64EncodedSignature

लेकिन IdP मुझे उत्तर दे रहा है: के सत्यापन SAML संदेश हस्ताक्षर विफल रहा।

पर हस्ताक्षर मैं अपने निजी कुंजी (2048 बाइट्स) का उपयोग कर रहा के लिए, और यह पुष्टि करने के लिए माना जाता है कि IdP मेरी सार्वजनिक कुंजी (एक है कि मैं इसे भेजा जब मैं अपने मेजबान पंजीकृत)

कोड उपयोग कर रहा है हस्ताक्षर करने के लिए अनुरोध लगता है:

// Retrieve the private key 
KeyStore keyStore = KeyStore.getInstance("JKS", "SUN"); 
FileInputStream stream; 
stream = new FileInputStream("/path/to/my/keystore.jks"); 
keyStore.load(stream, "storepass".toCharArray()); 
PrivateKey key = (PrivateKey) keyStore.getKey("keyAlias","keyPass".toCharArray()); 

// Create the signature 
Signature signature = Signature.getInstance("SHA1withRSA"); 
signature.initSign(key); 
signature.update("SAMLRequest=jVJda8IwFH2e4H8ofW%2BbVmvboGWCDApusDn2sBdJm1sNtEmXmw7x1y92KDrY2Ov5uueEzJG1TUfXaqd68wIfPaBxDm0jkQ7Mwu21pIqhQCpZC0hNRTfLxzWNfEI7rYyqVONeWf52METQRijpOsVq4W7JoSzjJJnWAEAmwLMMpmRG0jCrYJICIcR13kCjdSxcG%2BA6K9tQSGYGZG9MhzQIGrUT0uPw6VegpV%2FtA8ZrDBq0ZxB7KCQaJo2NICT1yMwjk9cwonFG4%2BTdzceju%2FmpOx3EOu8qYThgGJ3j5sE1fZE%2F2X3FynlQumXm9%2BGhHw6I4F49SCm0TDRLzjWgrXiKee5ZI2oB%2Bj%2Bj8qYX6GvFtdj1cPRryzPJ4Xh%2F2%2Fe736VvRzf2nn24wmoP%2BZbMojSM4tpL6iz2plFVeYyn4NUc0hmDjJQlfCf9cI5HZ%2Fjm4%2BRf&RelayState=null&SigAlg=http%3A%2F%2Fwww.w3.org%2F2000%2F09%2Fxmldsig%23rsa-sha1".getBytes()); 

String signatureBase64encodedString = (new BASE64Encoder()).encodeBuffer(signature.sign()); 
+0

अब मुझे यह अपवाद मिल रहा है: ** बेस -64 चार सरणी ** के लिए अमान्य लंबाई **। असल में मैंने अनुरोध से नामक्यूलीफायर विशेषता हटा दी है और मैं पिछले एक के बजाय आरएसए-शाए 256 का उपयोग कर रहा हूं, चरण-दर-चरण ... – Gaucho

+0

अंतिम ADFS2 लॉग: ADFS2: ** SAML अनुरोध अपेक्षित हस्ताक्षर एल्गोरिदम के साथ हस्ताक्षरित नहीं है। \t के साथ साइन इन किया गया: http://www.w3.org/2001/04/xmldsig-more#rsa-sha256 \t अपेक्षित: http://www.w3.org/2000/09/xmldsig#rsa-sha1* * – Gaucho

+0

ठीक है, समस्या एल्गोरिदम है। मैं ** SHA1withRSA ** का उपयोग कर हस्ताक्षर उत्पन्न कर रहा हूं। मुझे ** http: //www.w3.org/2000/09/xmldsig#rsa-sha1 "का उपयोग करने की आवश्यकता होगी। [जावा एक्सएमएल एपीआई] का उपयोग करना (http://java.sun.com/developer/technicalArticles/ xml/dig_signature_api /) मैं एक SignedAuthnRequest xml उत्पन्न करने में सक्षम हूं, लेकिन मुझे HTTP-Redirect (SAMLRequest = value और SigAlg = value और Signature = value) लागू करने की आवश्यकता होगी ... – Gaucho

उत्तर

9

अंत में मैं सही नुस्खा मिल गया:

  1. SAMLRequest मूल्य उत्पन्न
  2. Base64 में SAMLRequest मूल्य सांकेतिक शब्दों में बदलना
  3. यूआरएल एन्कोड SAMLRequest मूल्य
  4. यूआरएल एन्कोड SigAlg मूल्य: http://www.w3.org/2000/09/xmldsig#rsa-sha1
  5. SAMLRequest = मूल्य & SigAlg = मूल्य के साथ फ़ीड एल्गोरिथ्म हस्ताक्षर (SHA1withRSA)
  6. यूआरएल-सांकेतिक शब्दों में बदलना उत्पन्न हस्ताक्षर

हम चरणों SAML 2.0 डीबगर (https://rnd.feide.no/simplesaml/module.php/saml2debug/debug.php) के साथ 2 और 3 प्रदर्शन कर सकते हैं।और के लिए यूआरएल-एन्कोडिंग क्लासिक w3schools (http://www.w3schools.com/tags/ref_urlencode.asp)

चेतावनी का उपयोग करें! सुनिश्चित करें कि ADFS2 में, आपकी भरोसेमंद पार्टी के लिए एल्गोरिदम SHA1 पर सेट है!

सादर,

लुइस

पुनश्च: अब मैं एक छोटा सा कोड करने के लिए है ...

पी पी एस: आप यहाँ कोड पा सकते हैं: https://github.com/cerndb/wls-cern-sso/tree/master/saml2slo

+2

हैश एल्गोरिदम के बारे में टिप के लिए धन्यवाद! वह मुझे मिल रहा था। – David

+0

यह मेरे लिए एनएएम के खिलाफ काम करता था, लेकिन केवल तभी जब मैंने इसे URLEncoding बंद कर दिया –

+0

एनएएम = नोवेल एक्सेस मैनेजर? – Gaucho

2

वहाँ a bug in the ADFS implementation जहां है यह त्रुटि संदेश देता है पीछे की ओर। जब यह कहता है:

SAML अनुरोध अपेक्षित हस्ताक्षर एल्गोरिदम के साथ हस्ताक्षरित नहीं है। SAML अनुरोध हस्ताक्षर एल्गोरिदम http://www.w3.org/2001/04/xmldsig-more#rsa-sha256 के साथ हस्ताक्षरित है। अपेक्षित हस्ताक्षर एल्गोरिदम http://www.w3.org/2000/09/xmldsig#rsa-sha1

वास्तव में इसका अर्थ है कि आप SHA1 का उपयोग कर रहे हैं और यह SHA256 की अपेक्षा कर रहा था।

+0

में संग्रहीत मेरी निजीकी का उपयोग करना है इसे साझा करने के लिए धन्यवाद! – Gaucho

+0

स्पष्टीकरण के लिए, इसका मतलब है कि आपका ऐप है sha1 और आपके AD FS का उपयोग करके लॉगआउट का अनुरोध SHA256 के लिए कॉन्फ़िगर किया गया है। – Dscoduc

0

चूंकि हमारे पास डोमिनोज 9.0.1 पर सफलतापूर्वक कार्यान्वित एसएलओ प्राप्त करने के लिए कई कदम उठाने के लिए, मैंने कोड लिखने का फैसला किया जो हमारे डोमिनोज सर्वर के साथ चलाने के लिए किसी भी (भविष्य) आईडीपी कॉन्फ़िगरेशन का उपयोग करने की अनुमति देगा। मैं निम्नलिखित रणनीति को लागू किया:

  • उपयोग जानकारी के जितना संभव हो सके
  • एक इनकमिंग SAML लॉगआउट अनुरोध से उपलब्ध IdP SLO प्रतिक्रिया के बारे में जानकारी प्राप्त करने के लिए इसी होने की idpcat.nsf में IdP विन्यास को पहचानें आईडीपी सेवा प्रदाता (SAML सर्वर)
  • SAML कॉन्फ़िगरेशन में परिवर्तन होने पर नई आवश्यकताओं को गतिशील अनुकूलन की अनुमति देने के लिए idpcat.nsf में संबंधित आईडीपी कॉन्फ़िगरेशन में SAML लॉगआउट प्रतिक्रिया को परिभाषित करें।

नतीजतन, कोड में एक पैरामीटर मानचित्र भेजे SAML लॉगआउट अनुरोध के सभी क्षेत्रों को पढ़ता है और डीकोड और क्वेरी स्ट्रिंग में पैरामीटर मानचित्र अनुरोध की एक्सएमएल पैरामीटर्स को निकालने के लिए अनावश्यक रूप से बढ़ा। चूंकि डोमिनोज सर्वर पर विभिन्न वेबसाइटों को एसएसओ कनेक्शन की अनुमति देने के लिए अलग-अलग आईडीपी सेवा प्रदाताओं के लिए कॉन्फ़िगर किया जा सकता है, इसलिए मैं संबंधित "होस्ट नाम" के साथ आईडीपी कॉन्फ़िगरेशन की पहचान करता हूं और उसी पैरामीटर मानचित्र में अपने सभी फ़ील्ड को पढ़ता हूं। एक लागू एक्सएमएल प्रतिक्रिया को परिभाषित करने के लिए मैंने सभी आवश्यक परिभाषाओं को आईडीपी कॉन्फ़िगरेशन की टिप्पणी में लिखने का निर्णय लिया, जो अलग-अलग आईडीपी प्रदाताओं के लिए एक ही कोड का उपयोग करने के लिए एकल आईडीपी कॉन्फ़िगरेशन को अनुकूलित करने की अनुमति देता है भले ही विभिन्न SAML संस्करणों का उपयोग करें। Idpcat.nsf में आईडीपी कॉन्फ़िगरेशन के टिप्पणी क्षेत्र में परिभाषाएं इस तरह दिखती हैं:

एसएलओ प्रतिक्रिया:/idp/एसएलओ।SAML2;

SLO प्रतिक्रिया एक्सएमएल: "<" कलश: LogoutResponse आईडी = "@ UUID" संस्करण = "# संस्करण" IssueInstant = "@ ACTUAL_TIME" गंतव्य = "SLO_Response" InResponseTo = "# आईडी" xmlns: कलश = "# xmlns : कलश ">" "<" urn1: जारीकर्ता xmlns: urn1 = "XML_Parameter1" ">" HTTP_HSP_LISTENERURI "<"/urn1: जारीकर्ता ">" "<" कलश: स्थिति ">" "<" कलश: StatusCode मूल्य = "XML_Parameter2"/">" "<"/कलश: स्थिति ">" "<"/कलश: LogoutResponse ">";

एक्सएमएल मान: #xmlns: कलश = प्रोटोकॉल -> अभिकथन & #xmlns: urn = प्रोटोकॉल -> स्थिति: सफलता;

प्रतिक्रिया पैरामीटर: रिलेस्टेट & सिगएल्ग और हस्ताक्षर;

हस्ताक्षर प्रकार: SHA256withRSA;

कीस्टोर प्रकार: पीकेसीएस 12;

कीस्टोर फ़ाइल: डी: \ saml_cert.pfx;

कीस्टोर पासवर्ड: **********;

प्रमाणपत्र: {} xxxxxxxxxx

इस परिभाषा में कुंजी के साथ मूल्यों से अलग होती है ":" और मूल्यों के अंत के साथ निर्दिष्ट किया जाता है "," (नई लाइन नहीं) यह एसएसओ कनेक्शन के लिए उपयोग किए गए संबंधित आईडीपी कॉन्फ़िगरेशन में आईडीपी सेवा प्रदाता से आवश्यक एसएएमएल प्रतिक्रिया का पूर्ण पैरामीटरकरण सेट करने की अनुमति देता है।

• SLO प्रतिक्रिया:: यह रिश्तेदार का पता है, जहां SLO रिस्पांस संबंधित IdP सर्वर पर करने के लिए भेजा जाना है इस प्रकार है परिभाषाएँ निर्दिष्ट हैं।

• एसएलओ प्रतिक्रिया एक्सएमएल: यह एक्सएमएल प्रारूप में संरचित एसएलओ प्रतिक्रिया को परिभाषित करने वाली टेक्स्ट स्ट्रिंग है ("<" और ">" बिना ") का उपयोग करें। पैरामीटर मानचित्र में पाए गए पैरामीटर की पहचान करने वाले स्ट्रिंग्स को उनके संबंधित मान में बदल दिया जाता है। सुनिश्चित करें कि समान पैरामीटर सही ढंग से पहचान कुकी पैरामीटर। इसके अतिरिक्त 2 सूत्रों प्रदान की जाती हैं एक अग्रणी "$" और अनुरोध क्वेरी एक अग्रणी "#" के एक्सएमएल मानकों है, जहां "@UUID" के साथ एक यादृच्छिक UUID की गणना करेगा कर रहे हैं बनाने के लिए XML प्रतिक्रिया के आईडी पैरामीटर के लिए सही प्रारूप और "@ACTUAL_TIME" XML प्रतिक्रिया के IssueInstant पैरामीटर के लिए तत्काल प्रारूप में सही टाइम स्टाम्प की गणना करेगा।

• XML मान: यह टेक्स्ट स्ट्रिंग अतिरिक्त पैरामीटर की पहचान करती है, जहां मूल रूप से एक ज्ञात पैरामीटर का उपयोग किया जाता है, लेकिन आवश्यक पाठ से मेल खाने के लिए पैरामीटर मान का एक हिस्सा आदान-प्रदान की आवश्यकता होती है। मापदंडों स्ट्रिंग "XML_Paramater" स्ट्रिंग SLO प्रतिक्रिया एक्सएमएल टेक्स्ट में "&" के साथ प्रत्येक मूल्य को अलग करने में स्थिति के बाद से पहचाने जाते हैं। एक्सएमएल वैल्यूज के लिए पाठ पैरामीटर पहचान के बाद "=" और टेक्स्ट को प्रतिस्थापित करने के बाद "->" और नया टेक्स्ट द्वारा संरचित किया जाता है।

• प्रतिक्रिया पैरामीटर: प्रतिक्रिया पैरामीटर "&" से अलग किए गए हैं और परिभाषित किए गए एसएलओ प्रतिक्रिया में जोड़े जाएंगे। एक हस्ताक्षर मानकों की आवश्यकता है तो SigAlg और हस्ताक्षर इस स्ट्रिंग में की जरूरत है और अंत में रखा जाना चाहिए।

• हस्ताक्षर प्रकार: तो हस्ताक्षर की आवश्यकता है एल्गोरिथ्म के प्रकार के हस्ताक्षर गणना के लिए इस्तेमाल यहाँ निर्दिष्ट किया जाता है।

• कीस्टोर प्रकार: यह प्रमाणपत्र के लिए उपयोग की जाने वाली कीस्टोर का प्रकार है।

• कीस्टोर फ़ाइल: यह वह फ़ाइल है जहां लोटस नोट्स सर्वर पर ड्राइव और पथ सहित कीस्टोर सहेजा गया है। हमने टेस्ट सर्वर पर डी: \ saml_cert.pfx का उपयोग किया।

• कीस्टोर पासवर्ड: यह कुंजीस्टोर फ़ाइल और उसमें संग्रहीत प्रमाणपत्र खोलने के लिए आवश्यक पासवर्ड है।

• प्रमाणपत्र: यह कुंजीस्टोर फ़ाइल में प्रमाणपत्र की पहचान प्रमाणपत्र का उपनाम है। यदि किसी स्थान पर कई प्रमाणपत्रों को गठबंधन करने के लिए किसी नई कुंजीस्टोर फ़ाइल में प्रमाणपत्र संग्रहीत किया जाता है, तो उपनाम हमेशा एक नए मान में बदल जाता है, जिसे यहां अनुकूलित किया जाना है।

कोड लागू किया गया कोड domcfg.nsf में "लॉगआउट" नाम के साथ एक जावा एजेंट है, लेकिन इसे मूल रूप से एसएसओ उपयोगकर्ताओं के लिए उपलब्ध किसी भी डेटाबेस में कार्यान्वित किया जा सकता है और यह आईडी के संरक्षण की अनुमति देने के लिए सर्वर के रूप में चलाता है उच्चतम सुरक्षा के साथ idpcat.nsf में विन्यास। आईडीपी सेवा प्रदाता पर आपको एसएमएल अनुरोध के बाद क्रमशः संबंधित वेबसाइट "https://WEBSITE/domcfg.nsf/Logout?Open&" के रूप में डोमिनोज सर्वर के लिए एसएलओ अनुरोध को कॉन्फ़िगर करना होगा। यदि आईडीपी सेवा प्रदाता द्वारा हस्ताक्षर का अनुरोध किया जाता है, तो आपको हस्ताक्षर करने के लिए आवश्यक निजीकी सहित सर्टिफिकेट के साथ एक कीस्टोर फ़ाइल को स्टोर करना होगा। कीस्टोर फ़ाइल को एमएमसी स्नैप-इन फ़ंक्शन का उपयोग करके प्रबंधित किया जा सकता है (https://msdn.microsoft.com/en-us/library/ms788967(v=vs.110).aspx देखें)। निर्यात फ़ंक्शन द्वारा एक फ़ाइल में कई प्रमाणपत्रों को गठबंधन करना संभव है, लेकिन आपको यह सुनिश्चित करना होगा कि आप निर्यात विज़ार्ड में संबंधित सेटिंग द्वारा फ़ाइल में निजी कुंजी निर्यात करें।

import lotus.domino.*; 
import java.io.*; 
import java.util.*; 
import java.text.*; 
import com.ibm.xml.crypto.util.Base64; 
import java.util.zip.*; 
import java.net.URLEncoder; 
import java.security.*; 

public class JavaAgent extends AgentBase { 
    public void NotesMain() { 
     try { 
      Session ASession = getSession(); 
      AgentContext AContext = ASession.getAgentContext(); 
      DateTime date = ASession.createDateTime("Today 06:00"); 
      int timezone = date.getTimeZone(); 

      Database DB = AContext.getCurrentDatabase(); 
      String DBName = DB.getFileName(); 
      DBName = DBName.replace("\\", "/").replace(" ", "+"); 

      //Load PrintWriter to printout values for checking (only to debug) 
      //PrintWriter pwdebug = getAgentOutput(); 
      //pwdebug.flush(); 

      //Load Data from Logout Request 
      Document Doc = AContext.getDocumentContext(); 
      Vector<?> items = Doc.getItems(); 
      Map<String, String> Params = new LinkedHashMap<String, String>(); 
      for (int j=0; j<items.size(); j++) { 
       Item item = (Item)items.elementAt(j); 
       if (!item.getValueString().isEmpty()) Params.put(item.getName(), item.getValueString()); 
      } 
      String ServerName = Params.get("HTTP_HSP_HTTPS_HOST"); 
      int pos = ServerName.indexOf(":"); 
      ServerName = pos > 0 ? ServerName.substring(0, ServerName.indexOf(":")) : ServerName; 
      Params.put("ServerName", ServerName); 
      Doc.recycle(); 
      DB.recycle(); 

      //Load Cookie Variables 
      Params = map(Params, Params.get("HTTP_COOKIE"), "$", "; ", "=", false, false); 
      //Load Query Variables 
      Params = map(Params, Params.get("QUERY_STRING_DECODED"), "", "&", "=", false, false); 
      //Decode and Infalte SAML Request 
      String RequestUnziped = decode_inflate(Params.get("SAMLRequest"), true); 
      //pwdebug.println("Request unziped: " + RequestUnziped); 
      //System.out.println("Request unziped: " + RequestUnziped); 
      String RequestXMLParams = RequestUnziped.substring(19, RequestUnziped.indexOf("\">")); 
      //Load XML Parameters from Request 
      Params = map(Params, RequestXMLParams, "#", "\" ", "=\"", false, false); 
      //for (Map.Entry<String, String> entry : Params.entrySet()) pwdebug.println(entry.getKey() + " value: " + entry.getValue()); 
      //for (Map.Entry<String, String> entry : Params.entrySet()) System.out.println(entry.getKey() + " value: " + entry.getValue()); 
      String Issuer = RequestUnziped.substring(RequestUnziped.indexOf(":Issuer"), RequestUnziped.indexOf("Issuer>")); 
      Issuer = Issuer.substring(Issuer.indexOf(">") + 1, Issuer.indexOf("<")); 
      Params.put("SLO_Issuer", Issuer); 

      //Load Parameters for the Response 
      DbDirectory Dir = ASession.getDbDirectory(null); 
      Database idpcat = Dir.openDatabase("idpcat.nsf"); 
      View idpView = idpcat.getView("($IdPConfigs)"); 
      Document idpDoc = idpView.getDocumentByKey(ServerName, false); 
      items = idpDoc.getItems(); 
      for (int j=0; j<items.size(); j++) { 
       Item item = (Item)items.elementAt(j); 
       if (!item.getValueString().isEmpty()) Params.put(item.getName(), item.getValueString()); 
      } 
      Params = map(Params, idpDoc.getItemValueString("Comments"), "", ";", ": ", false, false); 
      Params.put("SLO_Response", Issuer + Params.get("SLO Response")); 
      Params.put("@UUID", "_" + UUID.randomUUID().toString()); 
      Params.put("@ACTUAL_TIME", actualTime(Params.get("#IssueInstant"), Params.get("#NotOnOrAfter"), timezone)); 
      //for (Map.Entry<String, String> entry : Params.entrySet()) pwdebug.println(entry.getKey() + " value: " + entry.getValue()); 
      //for (Map.Entry<String, String> entry : Params.entrySet()) System.out.println(entry.getKey() + " value: " + entry.getValue()); 
      idpDoc.recycle(); 
      idpView.recycle(); 
      idpcat.recycle(); 
      Dir.recycle(); 

      //Setup XML Response as defined 
      String ResponseString = Params.get("SLO Response XML"); 
      for (Iterator<String> itRq = Params.keySet().iterator(); itRq.hasNext();) { 
       String Key = (String) itRq.next(); 
       ResponseString = ResponseString.replace(Key, Params.get(Key)); 
      } 
      //pwdebug.println("Response String replaced: " + ResponseString); 
      //System.out.println("Response String replaced: " + ResponseString); 
      //Load Values to be exchanged in the defined Response 
      Map<String, String> RsXMLValues = map(new LinkedHashMap<String, String>(), Params.get("XML Values"), "", "&", "=", true, false); 
      //for (Map.Entry<String, String> entry : RsXMLValues.entrySet()) pwdebug.println(entry.getKey() + " value: " + entry.getValue()); 
      //for (Map.Entry<String, String> entry : RsXMLValues.entrySet()) System.out.println(entry.getKey() + " value: " + entry.getValue()); 
      //Exchange defined Strings with Values from the Request 
      int itc = 0; 
      for (Iterator<String> itRXV = RsXMLValues.keySet().iterator(); itRXV.hasNext();) { 
       itc = itc + 1; 
       String Key = (String) itRXV.next(); 
       int lock = Key.indexOf(" -> "); 
       String KeyRq = lock > 0 ? Key.substring(0, lock) : Key; 
       int lockRq = KeyRq.indexOf(" "); 
       KeyRq = lockRq > 0 ? KeyRq.substring(0, lockRq) : KeyRq; 
       String Parameter = Params.get(KeyRq); 
       String Value = RsXMLValues.get(Key); 
       if (!Value.isEmpty()) { 
        int locv = Value.indexOf(" -> "); 
        String ValueS = locv > 0 ? Value.substring(0, locv) : Value; 
        String ValueR = locv > 0 && Value.length() > locv + 4 ? Value.substring(locv + 4) : ValueS; 
        Parameter = Parameter.replace(ValueS, ValueR); 
       } 
       ResponseString = ResponseString.replace(("XML_Parameter" + itc), Parameter); 
      } 
      //pwdebug.println("Final XML Response String: " + ResponseString); 
      //System.out.println("Final XML Response String: " + ResponseString); 
      //Deflate and Encode the XML Response 
      String ResponseZiped = deflate_encode(ResponseString, Deflater.DEFAULT_COMPRESSION, true); 
      //pwdebug.println("Response Ziped: " + ResponseZiped); 
      //System.out.println("Response Ziped: " + ResponseZiped); 
      //Setup Response URLQuery as defined 
      String ResponseEncoded = "SAMLResponse=" + URLEncoder.encode(ResponseZiped, "UTF-8"); 
      //pwdebug.println("Response to Sign: " + ResponseEncoded); 
      //System.out.println("Response to Sign: " + ResponseEncoded); 
      //Load Parameters to be added to the Response 
      Map<String, String> ResponseParams = map(new LinkedHashMap<String, String>(), Params.get("Response Parameters"), "", "&", "=", false, true); 
      //for (Map.Entry<String, String> entry : ResponseParams.entrySet()) pwdebug.println(entry.getKey() + " value: " + entry.getValue()); 
      //for (Map.Entry<String, String> entry : ResponseParams.entrySet()) System.out.println(entry.getKey() + " value: " + entry.getValue()); 
      //Add defined Parameters with Values from the Request 
      for (Iterator<String> itRP = ResponseParams.keySet().iterator(); itRP.hasNext();) { 
       String Key = (String) itRP.next(); 
       if (Key.contains("Signature")) { 
        //pwdebug.println("Response to Sign: " + ResponseEncoded); 
        //System.out.println("Response to Sign: " + ResponseEncoded); 
        Signature signature = Signature.getInstance(Params.get("Signature Type")); 
        //pwdebug.println("Signature: Initiated"); 
        //System.out.println("Signature: Initiated"); 
        KeyStore keyStore = KeyStore.getInstance(Params.get("KeyStore Type")); 
        //pwdebug.println("Key Store: Initiated"); 
        //System.out.println("Key Store: Initiated"); 
        keyStore.load(new FileInputStream(Params.get("KeyStore File")), Params.get("KeyStore Password").toCharArray()); 
        //pwdebug.println("Key Store: Loaded"); 
        //System.out.println("Key Store: Loaded"); 
        PrivateKey key = (PrivateKey) keyStore.getKey (Params.get("Certificate"), Params.get("KeyStore Password").toCharArray()); 
        //pwdebug.println("Key Store: Private Key Loaded"); 
        //System.out.println("Key Store: Private Key Loaded"); 
        signature.initSign(key); 
        //pwdebug.println("Signature: Private Key Initiated"); 
        //System.out.println("Signature: Private Key Initiated"); 
        signature.update(ResponseEncoded.getBytes("UTF-8")); 
        //pwdebug.println("Signature: Signed"); 
        //System.out.println("Signature: Signed"); 
        String ResponseSignature = URLEncoder.encode(Base64.encode(signature.sign()), "UTF-8"); 
        //pwdebug.println("Signature: Signed"); 
        //System.out.println("Signature: Signed"); 
        ResponseEncoded = ResponseEncoded.concat("&").concat(Key).concat("=").concat(ResponseSignature); 
       } 
       else ResponseEncoded = ResponseEncoded.concat("&").concat(Key).concat("=").concat(URLEncoder.encode(Params.get(Key), "UTF-8")); 
      } 
      String ResponseURL = Params.get("SLO_Response").concat("?").concat(ResponseEncoded); 
      //pwdebug.println("Final Response URL: " + ResponseURL); 
      //pwdebug.close(); 
      //System.out.println("Final Response URL: " + ResponseURL); 

      //Send Logout to Server and redirect to Response to defined Destination 
      PrintWriter pwsaml = getAgentOutput(); 
      pwsaml.flush(); 
      pwsaml.println("[" + Params.get("HTTP_HSP_LISTENERURI") + "/" + DBName + "?logout&redirectto=" + URLEncoder.encode(ResponseURL, "UTF-8") + "]"); 
      pwsaml.close(); 

      //Recycle Agent and Session 
      AContext.recycle(); 
      ASession.recycle(); 

     } catch(Exception e) { 
      PrintWriter pwerror = getAgentOutput(); 
      pwerror.flush(); 
      pwerror.println(e); 
      System.out.println(e); 
      pwerror.close(); 
     } 
    } 

    //Load Maps from Strings to identify Paramteres and Values 
    private static Map<String, String> map(Map<String, String> map, String input, String keys, String spliting, String pairing, Boolean keycount, Boolean empty) { 
     Map<String, String> output = map.isEmpty() ? new LinkedHashMap<String, String>() : map; 
     String[] Pairs = input.split(spliting); 
     int kc = 0; 
     for (String Pair : Pairs) { 
      kc = kc + 1; 
      int pos = Pair.indexOf(pairing); 
      String Key = pos > 0 ? Pair.substring(0, pos) : Pair; 
      if (keycount) Key = Key + " " + kc; 
      String Value = pos > 0 && Pair.length() > (pos + pairing.length()) ? Pair.substring(pos + pairing.length()) : ""; 
      if (!output.containsKey(Key) && (empty || !Value.trim().isEmpty())) output.put((keys + Key).trim(), Value.trim()); 
     } 
     return output; 
    } 

    //Decode and Inflate to XML 
    private static String decode_inflate(String input, Boolean infflag) throws IOException, DataFormatException { 
     byte[] inputDecoded = Base64.decode(input.getBytes("UTF-8")); 
     Inflater inflater = new Inflater(infflag); 
     inflater.setInput(inputDecoded); 
     byte[] outputBytes = new byte[1024]; 
     int infLength = inflater.inflate(outputBytes); 
     inflater.end(); 
     String output = new String(outputBytes, 0, infLength, "UTF-8"); 
     return output; 
    } 

    //Deflate and Encode XML 
    private static String deflate_encode(String input, int level , Boolean infflag) throws IOException { 
     byte[] inputBytes = input.getBytes("UTF-8"); 
     Deflater deflater = new Deflater(level, infflag); 
     deflater.setInput(inputBytes); 
     deflater.finish(); 
     byte[] outputBytes = new byte[1024]; 
     int defLength = deflater.deflate(outputBytes); 
     deflater.end(); 
     byte[] outputDeflated = new byte[defLength]; 
     System.arraycopy(outputBytes, 0, outputDeflated, 0, defLength); 
     String output = Base64.encode(outputDeflated); 
     return output; 
    } 

    //Define Date and Time Formats 
    private static SimpleDateFormat DateFormat = new SimpleDateFormat("yyyy-MM-dd"); 
    private static SimpleDateFormat TimeFormat = new SimpleDateFormat("HH:mm:ss.SSS"); 

    //Formated Actual Time 
    private static String actualTime(String minTime, String maxTime, int localZone) throws ParseException { 
     Date actualtime = new Date(); 
     long acttime = actualtime.getTime(); 
     long mintime = resetTime(minTime, localZone); 
     long maxtime = resetTime(maxTime, localZone); 
     acttime = (acttime > mintime) && (acttime < maxtime) ? acttime: mintime + 1000; 
     return formatTime(acttime); 
    } 

    //Reset timemillis from String as defined 
    private static long resetTime(String givenTime, int localZone) throws ParseException { 
     Date date = DateFormat.parse(givenTime.substring(0, givenTime.indexOf("T"))); 
     long days = date.getTime(); 
     Date time = TimeFormat.parse(givenTime.substring(givenTime.indexOf("T") + 1, givenTime.indexOf("Z"))); 
     long hours = time.getTime(); 
     long zonecorr = localZone * 3600000; 
     return days + hours - zonecorr; 
    } 

    //Format timemillis into a String as required 
    private static String formatTime(long totalmilliSeconds) { 
     long date = 86400000 * (totalmilliSeconds/86400000); 
     long time = totalmilliSeconds % 86400000; 
     String dateString = DateFormat.format(date).concat("T"); 
     String timeString = TimeFormat.format(time).concat("Z"); 
     return dateString.concat(timeString); 
    } 

    public static String noCRLF(String input) { 
     String lf = "%0D"; 
     String cr = "%0A"; 
     String find = lf; 
     int pos = input.indexOf(find); 
     StringBuffer output = new StringBuffer(); 
     while (pos != -1) { 
      output.append(input.substring(0, pos)); 
      input = input.substring(pos + 3, input.length()); 
      if (find.equals(lf)) find = cr; 
      else find = lf; 
      pos = input.indexOf(find); 
     } 
     if (output.toString().equals("")) return input; 
     else return output.toString(); 
    } 
} 

आप मान्यता प्राप्त हो सकता है के रूप में, कई:

यह "लॉगआउट" एजेंट के लिए कोड है, जो डोमिनो सर्वर से उपयोगकर्ता लॉग आउट और भेजता IdP सेवा प्रदाता के लिए SAML लॉगआउट प्रतिक्रिया है टिप्पणी की गई लाइनों का उपयोग एजेंट को डीबग करने के लिए किया जा सकता है, यदि परिभाषाएं सही नहीं हैं और परिणामस्वरूप सफल लॉगआउट नहीं होता है। आप उन पंक्तियों को शुरू करने "//" को हटाकर उन पंक्तियों को आसानी से बदल सकते हैं और उन पैरामीटर को प्रिंट कर सकते हैं जिन्हें आप अपनी स्क्रीन पर देखना चाहते हैं या उन्हें लॉग में भेजना चाहते हैं।

डोमिनोज सर्वर पर एसएलओ शुरू करने के लिए, मैंने एक ही अवधारणा का उपयोग करके एक और जावा एजेंट लिखा। एजेंट को स्टार्ट एसएलओ कहा जाता है और "लॉगआउट" एजेंट के समान डेटाबेस में स्थित होता है। इस एजेंट का उपयोग सापेक्ष यूआरएल "/domcfg.nsf/startSLO?Open" खोलने वाले बटन बनाकर आपके किसी भी एप्लिकेशन में आसानी से कार्यान्वित किया जा सकता है। "StartSLO" एजेंट में निम्न कोड है .:

import lotus.domino.*; 
import java.io.*; 

public class JavaAgent extends AgentBase { 
    public void NotesMain() { 
     try { 
      Session ASession = getSession(); 
      AgentContext AContext = ASession.getAgentContext(); 

      Database DB = AContext.getCurrentDatabase(); 
      String DBName = DB.getFileName(); 
      DBName = DBName.replace("\\", "/").replace(" ", "+"); 

      //Load Data from Logout Request 
      Document Doc = AContext.getDocumentContext(); 
      String ServerName = Doc.getItemValueString("HTTP_HSP_HTTPS_HOST"); 
      int pos = ServerName.indexOf(":"); 
      ServerName = pos > 0 ? ServerName.substring(0, ServerName.indexOf(":")) : ServerName; 
      String Query = Doc.getItemValueString("Query_String"); 
      pos = Query.indexOf("?Open&"); 
      Query = pos > 0 ? "?" + Query.substring(Query.indexOf("?Open") + 6) : ""; 
      Doc.recycle(); 
      DB.recycle(); 

      //Load Parameters for the Response 
      DbDirectory Dir = ASession.getDbDirectory(null); 
      Database idpcat = Dir.openDatabase("idpcat.nsf"); 
      View idpView = idpcat.getView("($IdPConfigs)"); 
      Document idpDoc = idpView.getDocumentByKey(ServerName, false); 
      String SAMLSLO = idpDoc.getItemValueString("SAMLSloUrl"); 
      idpDoc.recycle(); 
      idpView.recycle(); 
      idpcat.recycle(); 
      Dir.recycle(); 

      //Send Logout to Server and redirect to Response to defined Destination 
      PrintWriter pwsaml = getAgentOutput(); 
      pwsaml.flush(); 
      pwsaml.println("[" + SAMLSLO + Query + "]"); 
      pwsaml.close(); 

      //Recycle Agent and Session 
      AContext.recycle(); 
      ASession.recycle(); 

     } catch(Exception e) { 
      PrintWriter pwerror = getAgentOutput(); 
      pwerror.flush(); 
      pwerror.println(e); 
      System.out.println(e); 
      pwerror.close(); 
     } 
    } 
} 
संबंधित मुद्दे