My aim is to implement the Single Log Out Protocol. First I am understanding how the standar works and how I can fit it in my scenario: ADFS 2.0 as IdP, for me is like a "black box"
What I am doing at the moment is the next:
Send an
<AuthnRequest>
to my IdPIdP asks me for credentials, I provide them and get succesfully login.
Get the SessionIndex value form the and constructs a
<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/">myemail@mydomain.com</NameID<samlp:SessionIndex>_0628125f-7f95-42cc-ad8e-fde86ae90bbe</samlp:SessionIndex></samlp:LogoutRequest>
Take the above
<LogoutRequest>
and encode it in Base64Contructs the next string:
SAMLRequest=base64encodedRequest&SigAlg=http%3A%2F%2Fwww.w3.org%2F2000%2F09%2Fxmldsig%23rsa-sha1
With the above string generates the signature
Encode the signature in base64
Send the request:
https://"https://idphost/adfs/ls/?SAMLRequest=base64encodedRequest&SigAlg=http%3A%2F%2Fwww.w3.org%2F2000%2F09%2Fxmldsig%23rsa-sha1&Signature=base64EncodedSignature
But the IdP is answering me: The verification of the SAML message signature failed.
For signing I am using my private key (2048 bytes), and for verifying it is supposed that the IdP is using my public key (the one that I sent it when I registered my host)
The code for signing the request looks like:
// 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());
As we had many steps to take to finally get SLO successfully implemented on Domino 9.0.1, I decided to write code which will allow using any (future) IdP configuration to run with our Domino servers. I implemented the following strategy:
As a result, the code reads all fields of the incoming SAML Logout Request into a Parameter Map and decodes and inflates the query string to extract the XML Parameters of the Request into the Parameter Map. As different websites on the domino server can be configured for different IdP service providers to allow SSO connection, I identify the IdP configuration with the corresponding "host name" and read all its fields in the same Parameter Map. For defining an applicable XML Response I decided to write all needed definitions into the Comment of the IdP configuration, which allows to adapt single IdP configurations to use the same code for different IdP providers even if the use different SAML versions. The definitions in the Comment field of the IdP configuration in the idpcat.nsf look like:
SLO Response: /idp/SLO.saml2;
SLO Response XML: "<"urn:LogoutResponse ID="@UUID" Version="#Version" IssueInstant="@ACTUAL_TIME" Destination="SLO_Response" InResponseTo="#ID" xmlns:urn="#xmlns:urn">" "<"urn1:Issuer xmlns:urn1="XML_Parameter1"">"HTTP_HSP_LISTENERURI"<"/urn1:Issuer">" "<"urn:Status">" "<"urn:StatusCode Value="XML_Parameter2"/">" "<"/urn:Status">" "<"/urn:LogoutResponse">";
XML Values: #xmlns:urn=protocol -> assertion&#xmlns:urn=protocol -> status:Success;
Response Parameters: RelayState&SigAlg&Signature;
Signature Type: SHA256withRSA;
KeyStore Type: PKCS12;
KeyStore File: D:\saml_cert.pfx;
KeyStore Password: **********;
Certificate: {xxxxxxxxxx}
The Keys in this definitions are separated from the Values with ": " and the end of the Values is specified with ";" (not the new line) This allows to setup a full parameterization of the SAML response as required from the IdP Service provider in the respective IdP configuration used for the SSO connection. The definitions are specified as follows:
• SLO Response: This is the relative address, where the SLO Response has to be sent to on the respective IdP Server.
• SLO Response XML: This is the text string defining the SLO Response structured in XML format (Use "<" and ">" without "). Strings identifying parameters found in the Parameter Map are exchanged to their respective Value. To make sure that similar parameters are identified correctly the Cookie parameters have a leading "$" and the XML parameters of the Request Query a leading "#". Additionally 2 formulas are provided, where "@UUID" will calculate a random UUID with the correct format for the ID parameter of the XML Response and "@ACTUAL_TIME" will calculate the correct Time Stamp in the Instant format for the IssueInstant parameter of the XML Response.
• XML Values: This text string identifies additional parameters, where basically a known parameter is used, but a part of the parameter value needs to be exchanged to match the required text. The parameters are identified by the string "XML_Paramater" followed by the positon in the string separating each value with "&" in the SLO Response XML text. The text for the XML Values is structured by having the parameter identification followed by "=" and the text to be replaced followed by " -> " and the new text.
• Response Parameters: The response parameters are separated with "&" and will be added to the SLO Response as defined. If a signature is required the parameters SigAlg and Signature are needed in this string and should be placed at the end.
• Signature Type: If Signature is required the type of algorithm used to calculate the signature is specified here.
• KeyStore Type: This is the type of the KeyStore used for the certificate.
• KeyStore File: This is the File where the KeyStore has been saved including the drive and path on the Lotus Notes Server. We used D:\saml_cert.pfx on the Test Server.
• KeyStore Password: This is the password required to open the KeyStore File and the certificates stored therein.
• Certificate: This is the Alias of the Certificate identifying the Certificate in the KeyStore File. If a Certificate is stored in a new KeyStore File to combine several Certificates in one location, the Alias is always changed to a new value, which has to be adapted here.
The code I implemented is a Java Agent with the name "Logout" in the domcfg.nsf, but it could basically be implemented in any database available for the SSO Users and it runs as the server to allow protection of the IdP configurations in the idpcat.nsf with highest security. On the IdP service provider you have to configure the SLO Request for the Domino Server respectively the corresponding website as "https://WEBSITE/domcfg.nsf/Logout?Open&" followed by the SAML Request. If signature is requested by the IdP service provider, you have to store a KeyStore File with the Certificate including the PrivateKey required to sign. The KeyStore File can be managed by using the MMC Snap-In function (see https://msdn.microsoft.com/en-us/library/ms788967(v=vs.110).aspx). It is possible to combine several certificates into one File by the export function, but you have to make sure that you export the private keys into the file by the respective setting in the export wizard.
This is the code for the "Logout" agent, which logs out the user from the domino server and sends the SAML Logout Response to the IdP service provider:
As you might have recognized, several commented lines can be used for debugging the agent, if the definitions are not correct and do not result in a successful logout. You can easily change those lines by deleting the "//" starting those lines and print out the parameters you would like to see on your screen or send them to the logs.
To initiate SLO on the domino server, I wrote another Java Agent using the same concept. The agent is called startSLO and is located in the same database as the "Logout" agent. The use of this agent can be easily implemented in any of your application by creating buttons opening the relative URL "/domcfg.nsf/startSLO?Open". The "startSLO" agent has the following code.:
Finally I got the right recipe:
We can perform the steps 2 and 3 with the SAML 2.0 Debugger (https://rnd.feide.no/simplesaml/module.php/saml2debug/debug.php). And for the URL-encoding use the classic w3schools (http://www.w3schools.com/tags/ref_urlencode.asp)
Warning! Ensure that the algorithm for your relying party, in the ADFS2, is setup to SHA1!
Best regards,
Luis
ps: now I have to code a little bit...
pps: You can find the code here: https://github.com/cerndb/wls-cern-sso/tree/master/saml2slo
There's a bug in the ADFS implementation where the error message it gives is backwards. When it says:
it actually means that you're using SHA1 and it was expecting SHA256.