keycloak-aplcache

saml signatures

10/16/2014 10:14:04 AM

Details

diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SAML2PostBindingBuilder.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SAML2PostBindingBuilder.java
new file mode 100755
index 0000000..a1b105c
--- /dev/null
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SAML2PostBindingBuilder.java
@@ -0,0 +1,133 @@
+package org.keycloak.protocol.saml;
+
+import org.picketlink.common.constants.GeneralConstants;
+import org.picketlink.common.exceptions.ConfigurationException;
+import org.picketlink.common.exceptions.ProcessingException;
+import org.picketlink.common.util.DocumentUtil;
+import org.picketlink.identity.federation.web.util.PostBindingUtil;
+import org.w3c.dom.Document;
+
+import javax.ws.rs.core.CacheControl;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import java.io.IOException;
+import java.security.KeyPair;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.cert.X509Certificate;
+
+import static org.picketlink.common.util.StringUtil.isNotNull;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class SAML2PostBindingBuilder<T extends SAML2PostBindingBuilder> {
+    protected KeyPair signingKeyPair;
+    protected X509Certificate signingCertificate;
+    protected boolean signed;
+    protected String signatureDigestMethod;
+    protected String signatureMethod;
+    protected String relayState;
+    protected String destination;
+    protected String responseIssuer;
+
+    public T sign(KeyPair keyPair) {
+        this.signingKeyPair = keyPair;
+        this.signed = true;
+        return (T)this;
+    }
+
+    public T sign(PrivateKey privateKey, PublicKey publicKey) {
+        this.signingKeyPair = new KeyPair(publicKey, privateKey);
+        this.signed = true;
+        return (T)this;
+    }
+
+    public T sign(KeyPair keyPair, X509Certificate cert) {
+        this.signingKeyPair = keyPair;
+        this.signingCertificate = cert;
+        this.signed = true;
+        return (T)this;
+    }
+
+    public T signatureDigestMethod(String method) {
+        this.signatureDigestMethod = method;
+        return (T)this;
+    }
+
+    public T signatureMethod(String method) {
+        this.signatureMethod = method;
+        return (T)this;
+    }
+
+    public T sign(PrivateKey privateKey, PublicKey publicKey, X509Certificate cert) {
+        this.signingKeyPair = new KeyPair(publicKey, privateKey);
+        this.signingCertificate = cert;
+        this.signed = true;
+        return (T)this;
+    }
+
+    public T destination(String destination) {
+        this.destination = destination;
+        return (T)this;
+    }
+
+    public T responseIssuer(String issuer) {
+        this.responseIssuer = issuer;
+        return (T)this;
+    }
+
+    public T relayState(String relayState) {
+        this.relayState = relayState;
+        return (T)this;
+    }
+
+
+
+    protected void signDocument(Document samlDocument) throws ProcessingException {
+        SamlProtocolUtils.signDocument(samlDocument, signingKeyPair, signatureMethod, signatureDigestMethod, signingCertificate);
+    }
+
+    protected Response buildResponse(Document responseDoc) throws ProcessingException, ConfigurationException, IOException {
+        byte[] responseBytes = DocumentUtil.getDocumentAsString(responseDoc).getBytes("UTF-8");
+        String samlResponse = PostBindingUtil.base64Encode(new String(responseBytes));
+
+        if (destination == null) {
+            throw SALM2PostBindingLoginResponseBuilder.logger.nullValueError("Destination is null");
+        }
+
+        StringBuilder builder = new StringBuilder();
+
+        String key = GeneralConstants.SAML_RESPONSE_KEY;
+        builder.append("<HTML>");
+        builder.append("<HEAD>");
+
+        builder.append("<TITLE>HTTP Post Binding Response (Response)</TITLE>");
+        builder.append("</HEAD>");
+        builder.append("<BODY Onload=\"document.forms[0].submit()\">");
+
+        builder.append("<FORM METHOD=\"POST\" ACTION=\"" + destination + "\">");
+        builder.append("<INPUT TYPE=\"HIDDEN\" NAME=\"" + key + "\"" + " VALUE=\"" + samlResponse + "\"/>");
+
+        if (isNotNull(relayState)) {
+            builder.append("<INPUT TYPE=\"HIDDEN\" NAME=\"RelayState\" " + "VALUE=\"" + relayState + "\"/>");
+        }
+
+        builder.append("<NOSCRIPT>");
+        builder.append("<P>JavaScript is disabled. We strongly recommend to enable it. Click the button below to continue.</P>");
+        builder.append("<INPUT TYPE=\"SUBMIT\" VALUE=\"CONTINUE\" />");
+        builder.append("</NOSCRIPT>");
+
+        builder.append("</FORM></BODY></HTML>");
+
+        String str = builder.toString();
+
+        CacheControl cacheControl = new CacheControl();
+        cacheControl.setNoCache(true);
+        return Response.ok(str, MediaType.TEXT_HTML_TYPE)
+                       .header("Pragma", "no-cache")
+                       .header("Cache-Control", "no-cache, no-store").build();
+    }
+
+}
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SAML2PostBindingErrorResponseBuilder.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SAML2PostBindingErrorResponseBuilder.java
new file mode 100755
index 0000000..6d56e51
--- /dev/null
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SAML2PostBindingErrorResponseBuilder.java
@@ -0,0 +1,57 @@
+package org.keycloak.protocol.saml;
+
+import org.picketlink.common.constants.JBossSAMLURIConstants;
+import org.picketlink.common.exceptions.ConfigurationException;
+import org.picketlink.common.exceptions.ProcessingException;
+import org.picketlink.identity.federation.api.saml.v2.response.SAML2Response;
+import org.picketlink.identity.federation.core.saml.v2.common.IDGenerator;
+import org.picketlink.identity.federation.core.saml.v2.factories.JBossSAMLAuthnResponseFactory;
+import org.picketlink.identity.federation.core.saml.v2.holders.IDPInfoHolder;
+import org.picketlink.identity.federation.core.saml.v2.holders.IssuerInfoHolder;
+import org.picketlink.identity.federation.core.saml.v2.holders.SPInfoHolder;
+import org.picketlink.identity.federation.saml.v2.protocol.ResponseType;
+import org.w3c.dom.Document;
+
+import javax.ws.rs.core.Response;
+import java.io.IOException;
+import java.io.StringWriter;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class SAML2PostBindingErrorResponseBuilder extends SAML2PostBindingBuilder<SAML2PostBindingErrorResponseBuilder> {
+
+    public Document getErrorResponse(String status) throws ProcessingException {
+        Document samlResponse = null;
+        ResponseType responseType = null;
+
+        SAML2Response saml2Response = new SAML2Response();
+
+        // Create a response type
+        String id = IDGenerator.create("ID_");
+
+        IssuerInfoHolder issuerHolder = new IssuerInfoHolder(responseIssuer);
+        issuerHolder.setStatusCode(status);
+
+        IDPInfoHolder idp = new IDPInfoHolder();
+        idp.setNameIDFormatValue(null);
+        idp.setNameIDFormat(JBossSAMLURIConstants.NAMEID_FORMAT_PERSISTENT.get());
+
+        SPInfoHolder sp = new SPInfoHolder();
+        sp.setResponseDestinationURI(destination);
+
+        responseType = saml2Response.createResponseType(id);
+        responseType.setStatus(JBossSAMLAuthnResponseFactory.createStatusTypeForResponder(status));
+        responseType.setDestination(destination);
+
+       if (signed) {
+            signDocument(samlResponse);
+        }
+        return samlResponse;
+    }
+
+    public Response buildErrorResponse(String status)  throws ConfigurationException, ProcessingException, IOException {
+        Document doc = getErrorResponse(status);
+        return buildResponse(doc);
+    }}
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SAML2PostBindingLogoutResponseBuilder.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SAML2PostBindingLogoutResponseBuilder.java
new file mode 100755
index 0000000..70c1909
--- /dev/null
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SAML2PostBindingLogoutResponseBuilder.java
@@ -0,0 +1,69 @@
+package org.keycloak.protocol.saml;
+
+import org.picketlink.common.constants.JBossSAMLURIConstants;
+import org.picketlink.common.exceptions.ConfigurationException;
+import org.picketlink.common.exceptions.ParsingException;
+import org.picketlink.common.exceptions.ProcessingException;
+import org.picketlink.identity.federation.api.saml.v2.request.SAML2Request;
+import org.picketlink.identity.federation.api.saml.v2.response.SAML2Response;
+import org.picketlink.identity.federation.core.saml.v2.common.IDGenerator;
+import org.picketlink.identity.federation.core.saml.v2.factories.JBossSAMLAuthnResponseFactory;
+import org.picketlink.identity.federation.core.saml.v2.holders.IDPInfoHolder;
+import org.picketlink.identity.federation.core.saml.v2.holders.IssuerInfoHolder;
+import org.picketlink.identity.federation.core.saml.v2.holders.SPInfoHolder;
+import org.picketlink.identity.federation.core.saml.v2.util.DocumentUtil;
+import org.picketlink.identity.federation.core.saml.v2.util.XMLTimeUtil;
+import org.picketlink.identity.federation.core.sts.PicketLinkCoreSTS;
+import org.picketlink.identity.federation.saml.v2.assertion.NameIDType;
+import org.picketlink.identity.federation.saml.v2.protocol.LogoutRequestType;
+import org.picketlink.identity.federation.saml.v2.protocol.ResponseType;
+import org.picketlink.identity.federation.web.util.PostBindingUtil;
+import org.w3c.dom.Document;
+
+import javax.ws.rs.core.Response;
+import java.io.IOException;
+import java.net.URI;
+import java.security.KeyPair;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class SAML2PostBindingLogoutResponseBuilder extends SAML2PostBindingBuilder<SAML2PostBindingLogoutResponseBuilder> {
+    protected String userPrincipal;
+
+    public SAML2PostBindingLogoutResponseBuilder userPrincipal(String userPrincipal) {
+        this.userPrincipal = userPrincipal;
+        return this;
+    }
+
+    public String buildRequestString() {
+        try {
+            Document logoutRequestDocument = new SAML2Request().convert(createLogoutRequest());
+            if (signed) {
+                signDocument(logoutRequestDocument);
+            }
+            byte[] responseBytes = DocumentUtil.getDocumentAsString(logoutRequestDocument).getBytes("UTF-8");
+            return PostBindingUtil.base64Encode(new String(responseBytes));
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private LogoutRequestType createLogoutRequest() throws ConfigurationException {
+        LogoutRequestType lort = new SAML2Request().createLogoutRequest(responseIssuer);
+
+        NameIDType nameID = new NameIDType();
+        nameID.setValue(userPrincipal);
+        //Deal with NameID Format
+        String nameIDFormat = JBossSAMLURIConstants.NAMEID_FORMAT_PERSISTENT.get();
+        nameID.setFormat(URI.create(nameIDFormat));
+        lort.setNameID(nameID);
+
+        long assertionValidity = PicketLinkCoreSTS.instance().getConfiguration().getIssuedTokenTimeout();
+
+        lort.setNotOnOrAfter(XMLTimeUtil.add(lort.getIssueInstant(), assertionValidity));
+        lort.setDestination(URI.create(destination));
+        return lort;
+    }
+}
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocolUtils.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocolUtils.java
new file mode 100755
index 0000000..342f089
--- /dev/null
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocolUtils.java
@@ -0,0 +1,66 @@
+package org.keycloak.protocol.saml;
+
+import org.keycloak.VerificationException;
+import org.keycloak.models.ClientModel;
+import org.keycloak.util.PemUtils;
+import org.picketlink.common.exceptions.ProcessingException;
+import org.picketlink.identity.federation.api.saml.v2.sig.SAML2Signature;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+
+import java.security.KeyPair;
+import java.security.PublicKey;
+import java.security.cert.X509Certificate;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class SamlProtocolUtils {
+
+    public static void verifyPostBindingSignature(ClientModel client, Document document) throws VerificationException {
+        if (!"true".equals(client.getAttribute("samlClientSignature"))) {
+            return;
+        }
+        SAML2Signature saml2Signature = new SAML2Signature();
+        String publicKeyPem = client.getAttribute(ClientModel.PUBLIC_KEY);
+        if (publicKeyPem == null) throw new VerificationException("Client does not have a public key.");
+        PublicKey publicKey = null;
+        try {
+            publicKey = PemUtils.decodePublicKey(publicKeyPem);
+        } catch (Exception e) {
+            throw new VerificationException("Could not decode public key", e);
+        }
+        try {
+            if (!saml2Signature.validate(document, publicKey)) {
+                throw new VerificationException("Invalid signature on document");
+            }
+        } catch (ProcessingException e) {
+            throw new VerificationException("Error validating signature", e);
+        }
+    }
+
+    public static void signDocument(Document samlDocument, KeyPair signingKeyPair, String signatureMethod, String signatureDigestMethod, X509Certificate signingCertificate) throws ProcessingException {
+        SAML2Signature samlSignature = new SAML2Signature();
+
+        if (signatureMethod != null) {
+            samlSignature.setSignatureMethod(signatureMethod);
+        }
+
+        if (signatureDigestMethod != null) {
+            samlSignature.setDigestMethod(signatureDigestMethod);
+        }
+
+        Node nextSibling = samlSignature.getNextSiblingOfIssuer(samlDocument);
+
+        samlSignature.setNextSibling(nextSibling);
+
+        if (signingCertificate != null) {
+            samlSignature.setX509Certificate(signingCertificate);
+        }
+
+        samlSignature.signSAMLDocument(samlDocument, signingKeyPair);
+    }
+
+
+}
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SAMLRequestParser.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SAMLRequestParser.java
index 4d5b6d4..db19892 100755
--- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SAMLRequestParser.java
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SAMLRequestParser.java
@@ -43,6 +43,5 @@ public class SAMLRequestParser {
             logger.samlBase64DecodingError(e);
         }
         return null;
-
     }
 }
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlService.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlService.java
index 855b476..708010e 100755
--- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlService.java
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlService.java
@@ -1,13 +1,11 @@
 package org.keycloak.protocol.saml;
 
 import org.jboss.logging.Logger;
-import org.jboss.resteasy.annotations.cache.NoCache;
 import org.jboss.resteasy.specimpl.MultivaluedMapImpl;
 import org.jboss.resteasy.spi.HttpRequest;
 import org.jboss.resteasy.spi.HttpResponse;
 import org.keycloak.ClientConnection;
-import org.keycloak.OAuth2Constants;
-import org.keycloak.events.Details;
+import org.keycloak.VerificationException;
 import org.keycloak.events.Errors;
 import org.keycloak.events.EventBuilder;
 import org.keycloak.events.EventType;
@@ -27,15 +25,14 @@ import org.picketlink.identity.federation.core.saml.v2.common.SAMLDocumentHolder
 import org.picketlink.identity.federation.saml.v2.SAML2Object;
 import org.picketlink.identity.federation.saml.v2.protocol.AuthnRequestType;
 import org.picketlink.identity.federation.saml.v2.protocol.LogoutRequestType;
+import org.picketlink.identity.federation.saml.v2.protocol.RequestAbstractType;
+import org.w3c.dom.Document;
 
 import javax.ws.rs.Consumes;
 import javax.ws.rs.FormParam;
-import javax.ws.rs.GET;
 import javax.ws.rs.POST;
 import javax.ws.rs.Path;
-import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.Context;
-import javax.ws.rs.core.Cookie;
 import javax.ws.rs.core.HttpHeaders;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.MultivaluedMap;
@@ -45,8 +42,6 @@ import javax.ws.rs.core.UriBuilder;
 import javax.ws.rs.core.UriInfo;
 import javax.ws.rs.ext.Providers;
 import java.net.URI;
-import java.util.HashMap;
-import java.util.Map;
 
 /**
  * Resource class for the oauth/openid connect token service
@@ -137,24 +132,7 @@ public class SamlService {
 
         SAML2Object samlObject = documentHolder.getSamlObject();
 
-        if (samlObject instanceof AuthnRequestType) {
-            event.event(EventType.LOGIN);
-            // Get the SAML Request Message
-            AuthnRequestType requestAbstractType = (AuthnRequestType) samlObject;
-            return loginRequest(relayState, requestAbstractType);
-        } else if (samlObject instanceof LogoutRequestType) {
-            event.event(EventType.LOGOUT);
-            LogoutRequestType requestAbstractType = (LogoutRequestType) samlObject;
-            return logoutRequest(relayState, requestAbstractType);
-
-        } else {
-            event.event(EventType.LOGIN_ERROR);
-            event.error(Errors.INVALID_TOKEN);
-            return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid Request");
-        }
-    }
-
-    protected Response loginRequest(String relayState, AuthnRequestType requestAbstractType) {
+        RequestAbstractType requestAbstractType = (RequestAbstractType)samlObject;
         String issuer = requestAbstractType.getIssuer().getValue();
         ClientModel client = realm.findClient(issuer);
 
@@ -176,6 +154,32 @@ public class SamlService {
             return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "direct-grants-only clients are not allowed to initiate browser login");
         }
 
+        try {
+            SamlProtocolUtils.verifyPostBindingSignature(client, documentHolder.getSamlDocument());
+        } catch (VerificationException e) {
+            logger.error("request validation failed", e);
+            event.error(Errors.INVALID_CLIENT);
+            return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid requester.");
+        }
+        if (samlObject instanceof AuthnRequestType) {
+            event.event(EventType.LOGIN);
+            // Get the SAML Request Message
+            AuthnRequestType authn = (AuthnRequestType) samlObject;
+            return loginRequest(relayState, authn, client);
+        } else if (samlObject instanceof LogoutRequestType) {
+            event.event(EventType.LOGOUT);
+            LogoutRequestType logout = (LogoutRequestType) samlObject;
+            return logoutRequest(logout, client);
+
+        } else {
+            event.event(EventType.LOGIN_ERROR);
+            event.error(Errors.INVALID_TOKEN);
+            return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid Request");
+        }
+    }
+
+    protected Response loginRequest(String relayState, AuthnRequestType requestAbstractType, ClientModel client) {
+
         URI redirectUri = requestAbstractType.getAssertionConsumerServiceURL();
         String redirect = OpenIDConnectService.verifyRedirectUri(uriInfo, redirectUri.toString(), realm, client);
 
@@ -186,10 +190,10 @@ public class SamlService {
 
 
         ClientSessionModel clientSession = session.sessions().createClientSession(realm, client);
-        clientSession.setAuthMethod(SamlLogin.LOGIN_PROTOCOL);
+        clientSession.setAuthMethod(SalmProtocol.LOGIN_PROTOCOL);
         clientSession.setRedirectUri(redirect);
         clientSession.setAction(ClientSessionModel.Action.AUTHENTICATE);
-        clientSession.setNote(SamlLogin.SAML_BINDING, SamlLogin.SAML_POST_BINDING);
+        clientSession.setNote(SalmProtocol.SAML_BINDING, SalmProtocol.SAML_POST_BINDING);
         clientSession.setNote(GeneralConstants.RELAY_STATE, relayState);
         clientSession.setNote("REQUEST_ID", requestAbstractType.getID());
 
@@ -212,28 +216,7 @@ public class SamlService {
         return forms.createLogin();
     }
 
-    protected Response logoutRequest(String relayState, LogoutRequestType requestAbstractType) {
-        String issuer = requestAbstractType.getIssuer().getValue();
-        ClientModel client = realm.findClient(issuer);
-
-        if (client == null) {
-            event.error(Errors.CLIENT_NOT_FOUND);
-            return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Unknown login requester.");
-        }
-
-        if (!client.isEnabled()) {
-            event.error(Errors.CLIENT_DISABLED);
-            return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Login requester not enabled.");
-        }
-        if ((client instanceof ApplicationModel) && ((ApplicationModel)client).isBearerOnly()) {
-            event.error(Errors.NOT_ALLOWED);
-            return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Bearer-only applications are not allowed to initiate browser login");
-        }
-        if (client.isDirectGrantsOnly()) {
-            event.error(Errors.NOT_ALLOWED);
-            return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "direct-grants-only clients are not allowed to initiate browser login");
-        }
-
+    protected Response logoutRequest(LogoutRequestType requestAbstractType, ClientModel client) {
         // authenticate identity cookie, but ignore an access token timeout as we're logging out anyways.
         AuthenticationManager.AuthResult authResult = authManager.authenticateIdentityCookie(session, realm, uriInfo, clientConnection, headers, false);
         if (authResult != null) {
diff --git a/saml/saml-protocol/src/main/resources/META-INF/services/org.keycloak.protocol.LoginProtocolFactory b/saml/saml-protocol/src/main/resources/META-INF/services/org.keycloak.protocol.LoginProtocolFactory
index 632a1db..d0a2dd0 100755
--- a/saml/saml-protocol/src/main/resources/META-INF/services/org.keycloak.protocol.LoginProtocolFactory
+++ b/saml/saml-protocol/src/main/resources/META-INF/services/org.keycloak.protocol.LoginProtocolFactory
@@ -1 +1 @@
-org.keycloak.protocol.saml.SamlLoginFactory
\ No newline at end of file
+org.keycloak.protocol.saml.SamlProtocolFactory
\ No newline at end of file
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java
index 4cfaaa3..620f5e2 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java
@@ -157,11 +157,11 @@ public class AccountTest {
         });
     }
 
-//    @Test
-//    @Ignore
-//    public void runit() throws Exception {
-//        Thread.sleep(10000000);
-//    }
+    @Test
+    @Ignore
+    public void runit() throws Exception {
+        Thread.sleep(10000000);
+    }
 
     @Test
     public void returnToAppFromQueryParam() {
diff --git a/testsuite/integration/src/test/resources/testsaml.json b/testsuite/integration/src/test/resources/testsaml.json
index 198f170..0cd30ba 100755
--- a/testsuite/integration/src/test/resources/testsaml.json
+++ b/testsuite/integration/src/test/resources/testsaml.json
@@ -32,12 +32,31 @@
             "name": "http://localhost:8080/sales-post/",
             "enabled": true,
             "fullScopeAllowed": true,
+            "protocol": "saml",
             "baseUrl": "http://localhost:8080/sales-post",
             "adminUrl": "http://localhost:8080/sales-post",
             "redirectUris": [
                 "http://localhost:8080/sales-post/*"
             ]
-         }
+        },
+        {
+            "name": "http://localhost:8080/sales-post-sig/",
+            "enabled": true,
+            "protocol": "saml",
+            "fullScopeAllowed": true,
+            "baseUrl": "http://localhost:8080/sales-post-sig",
+            "adminUrl": "http://localhost:8080/sales-post-sig",
+            "redirectUris": [
+                "http://localhost:8080/sales-post-sig/*"
+            ],
+            "attributes": {
+                "samlServerSignature": "true",
+                "samlClientSignature": "true",
+                "privateKey": "MIICWwIBAAKBgQDVG8a7xGN6ZIkDbeecySygcDfsypjUMNPE4QJjis8B316CvsZQ0hcTTLUyiRpHlHZys2k3xEhHBHymFC1AONcvzZzpb40tAhLHO1qtAnut00khjAdjR3muLVdGkM/zMC7G5s9iIwBVhwOQhy+VsGnCH91EzkjZ4SVEr55KJoyQJQIDAQABAoGADaTtoG/+foOZUiLjRWKL/OmyavK9vjgyFtThNkZY4qHOh0h3og0RdSbgIxAsIpEa1FUwU2W5yvI6mNeJ3ibFgCgcxqPk6GkAC7DWfQfdQ8cS+dCuaFTs8ObIQEvU50YzeNPiiFxRA+MnauCUXaKm/PnDfjd4tPgru7XZvlGh0wECQQDsBbN2cKkBKpr/b5oJiBcBaSZtWiMNuYBDn9x8uORj+Gy/49BUIMHF2EWyxOWz6ocP5YiynNRkPe21Zus7PEr1AkEA5yWQOkxUTIg43s4pxNSeHtL+Ebqcg54lY2xOQK0yufxUVZI8ODctAKmVBMiCKpU3mZQquOaQicuGtocpgxlScQI/YM31zZ5nsxLGf/5GL6KhzPJT0IYn2nk7IoFu7bjn9BjwgcPurpLA52TNMYWQsTqAKwT6DEhG1NaRqNWNpb4VAkBehObAYBwMm5udyHIeEc+CzUalm0iLLa0eRdiN7AUVNpCJ2V2Uo0NcxPux1AgeP5xXydXafDXYkwhINWcNO9qRAkEA58ckAC5loUGwU5dLaugsGH/a2Q8Ac8bmPglwfCstYDpl8Gp/eimb1eKyvDEELOhyImAv4/uZV9wN85V0xZXWsw==",
+                "publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDVG8a7xGN6ZIkDbeecySygcDfsypjUMNPE4QJjis8B316CvsZQ0hcTTLUyiRpHlHZys2k3xEhHBHymFC1AONcvzZzpb40tAhLHO1qtAnut00khjAdjR3muLVdGkM/zMC7G5s9iIwBVhwOQhy+VsGnCH91EzkjZ4SVEr55KJoyQJQIDAQAB",
+                "X509Certificate": "MIIB1DCCAT0CBgFJGP5dZDANBgkqhkiG9w0BAQsFADAwMS4wLAYDVQQDEyVodHRwOi8vbG9jYWxob3N0OjgwODAvc2FsZXMtcG9zdC1zaWcvMB4XDTE0MTAxNjEyNDQyM1oXDTI0MTAxNjEyNDYwM1owMDEuMCwGA1UEAxMlaHR0cDovL2xvY2FsaG9zdDo4MDgwL3NhbGVzLXBvc3Qtc2lnLzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1RvGu8RjemSJA23nnMksoHA37MqY1DDTxOECY4rPAd9egr7GUNIXE0y1MokaR5R2crNpN8RIRwR8phQtQDjXL82c6W+NLQISxztarQJ7rdNJIYwHY0d5ri1XRpDP8zAuxubPYiMAVYcDkIcvlbBpwh/dRM5I2eElRK+eSiaMkCUCAwEAATANBgkqhkiG9w0BAQsFAAOBgQCLms6htnPaY69k1ntm9a5jgwSn/K61cdai8R8B0ccY7zvinn9AfRD7fiROQpFyY29wKn8WCLrJ86NBXfgFUGyR5nLNHVy3FghE36N2oHy53uichieMxffE6vhkKJ4P8ChfJMMOZlmCPsQPDvjoAghHt4mriFiQgRdPgIy/zDjSNw=="
+            }
+        }
     ],
     "roles" : {
         "realm" : [