keycloak-aplcache

Details

diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js
index 0b599bf..61eefd9 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js
@@ -570,6 +570,13 @@ module.controller('ClientDetailCtrl', function($scope, realm, client, $route, se
         "persistent"
     ];
 
+    $scope.canonicalization = [
+        {name: "EXCLUSIVE", value:  "http://www.w3.org/2001/10/xml-exc-c14n#"  },
+        {name: "EXCLUSIVE_WITH_COMMENTS", value: "http://www.w3.org/2001/10/xml-exc-c14n#WithComments"},
+        {name: "INCLUSIVE", value: "http://www.w3.org/TR/2001/REC-xml-c14n-20010315" },
+        {name: "INCLUSIVE_WITH_COMMENTS", value: "http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments"}
+    ];
+
     $scope.realm = realm;
     $scope.create = !client.clientId;
     $scope.samlAuthnStatement = false;
@@ -614,9 +621,9 @@ module.controller('ClientDetailCtrl', function($scope, realm, client, $route, se
         } else if (client.attributes['saml_name_id_format'] == 'persistent') {
             $scope.nameIdFormat = $scope.nameIdFormats[3];
         }
-
     } else {
         $scope.client = { enabled: true, attributes: {}};
+        $scope.client.attributes['saml_signature_canonicalization_method'] = $scope.canonicalization[0].value;
         $scope.client.redirectUris = [];
         $scope.accessType = $scope.accessTypes[0];
         $scope.protocol = $scope.protocols[0];
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-detail.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-detail.html
index 727c66d..06c939b 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-detail.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-detail.html
@@ -113,6 +113,18 @@
                 </div>
                 <kc-tooltip>The signature algorithm to use to sign documents.</kc-tooltip>
             </div>
+            <div class="form-group" data-ng-show="(samlAssertionSignature || samlServerSignature) && protocol == 'saml'">
+                <label class="col-md-2 control-label" for="canonicalization">Canonicalization Method</label>
+                <div class="col-sm-6">
+                    <div>
+                        <select class="form-control" id="canonicalization"
+                                ng-model="client.attributes['saml_signature_canonicalization_method']"
+                                ng-options="canon.value as canon.name for canon in canonicalization">
+                        </select>
+                    </div>
+                </div>
+                <kc-tooltip>Canonicalization Method for XML signatures.</kc-tooltip>
+            </div>
             <div class="form-group clearfix block" data-ng-show="protocol == 'saml'">
                 <label class="col-md-2 control-label" for="samlEncrypt">Encrypt Assertions</label>
                 <div class="col-sm-6">
diff --git a/saml/saml-core/src/main/java/org/keycloak/saml/processing/api/saml/v2/sig/SAML2Signature.java b/saml/saml-core/src/main/java/org/keycloak/saml/processing/api/saml/v2/sig/SAML2Signature.java
index cb86c19..b4cf8a5 100755
--- a/saml/saml-core/src/main/java/org/keycloak/saml/processing/api/saml/v2/sig/SAML2Signature.java
+++ b/saml/saml-core/src/main/java/org/keycloak/saml/processing/api/saml/v2/sig/SAML2Signature.java
@@ -120,72 +120,9 @@ public class SAML2Signature {
     }
 
     /**
-     * Sign an RequestType at the root
-     *
-     * @param request
-     * @param keypair Key Pair
-     * @param digestMethod (Example: DigestMethod.SHA1)
-     * @param signatureMethod (Example: SignatureMethod.DSA_SHA1)
-     *
-     * @return
-     *
-     * @throws ParserConfigurationException
-     * @throws IOException
-     * @throws SAXException
-     * @throws XMLSignatureException
-     * @throws MarshalException
-     * @throws GeneralSecurityException
-     */
-    public Document sign(RequestAbstractType request, KeyPair keypair) throws SAXException, IOException,
-            ParserConfigurationException, GeneralSecurityException, MarshalException, XMLSignatureException {
-        SAML2Request saml2Request = new SAML2Request();
-        Document doc = saml2Request.convert(request);
-        doc.normalize();
-
-        Node theSibling = getNextSiblingOfIssuer(doc);
-        if (theSibling != null) {
-            this.sibling = theSibling;
-        }
-
-        return sign(doc, request.getID(), keypair);
-    }
-
-    /**
-     * Sign an ResponseType at the root
-     *
-     * @param response
-     * @param keypair Key Pair
-     * @param digestMethod (Example: DigestMethod.SHA1)
-     * @param signatureMethod (Example: SignatureMethod.DSA_SHA1)
-     *
-     * @return
-     *
-     * @throws ParserConfigurationException
-     * @throws XMLSignatureException
-     * @throws MarshalException
-     * @throws GeneralSecurityException
-     */
-    public Document sign(ResponseType response, KeyPair keypair) throws ParserConfigurationException, GeneralSecurityException,
-            MarshalException, XMLSignatureException {
-        SAML2Response saml2Request = new SAML2Response();
-        Document doc = saml2Request.convert(response);
-        doc.normalize();
-
-        Node theSibling = getNextSiblingOfIssuer(doc);
-        if (theSibling != null) {
-            this.sibling = theSibling;
-        }
-
-        return sign(doc, response.getID(), keypair);
-    }
-
-    /**
      * Sign an Document at the root
      *
-     * @param response
      * @param keyPair Key Pair
-     * @param digestMethod (Example: DigestMethod.SHA1)
-     * @param signatureMethod (Example: SignatureMethod.DSA_SHA1)
      *
      * @return
      *
@@ -194,7 +131,7 @@ public class SAML2Signature {
      * @throws MarshalException
      * @throws GeneralSecurityException
      */
-    public Document sign(Document doc, String referenceID, KeyPair keyPair) throws ParserConfigurationException,
+    public Document sign(Document doc, String referenceID, KeyPair keyPair, String canonicalizationMethodType) throws ParserConfigurationException,
             GeneralSecurityException, MarshalException, XMLSignatureException {
         String referenceURI = "#" + referenceID;
 
@@ -213,66 +150,9 @@ public class SAML2Signature {
                 dto.setX509Certificate(x509Certificate);
             }
 
-            return XMLSignatureUtil.sign(dto);
-        }
-        return XMLSignatureUtil.sign(doc, keyPair, digestMethod, signatureMethod, referenceURI);
-    }
-
-    /**
-     * Sign an assertion whose id value is provided in the response type
-     *
-     * @param response
-     * @param idValueOfAssertion
-     * @param keypair
-     * @param referenceURI
-     *
-     * @return
-     *
-     * @throws ParserConfigurationException
-     * @throws TransformerException
-     * @throws TransformerFactoryConfigurationError
-     * @throws XPathException
-     * @throws XMLSignatureException
-     * @throws MarshalException
-     * @throws GeneralSecurityException
-     */
-    public Document sign(ResponseType response, String idValueOfAssertion, KeyPair keypair, String referenceURI)
-            throws ParserConfigurationException, XPathException, TransformerFactoryConfigurationError, TransformerException,
-            GeneralSecurityException, MarshalException, XMLSignatureException {
-        SAML2Response saml2Response = new SAML2Response();
-        Document doc = saml2Response.convert(response);
-        doc.normalize();
-
-        Node theSibling = getNextSiblingOfIssuer(doc);
-        if (theSibling != null) {
-            this.sibling = theSibling;
+            return XMLSignatureUtil.sign(dto, canonicalizationMethodType);
         }
-
-        return sign(doc, idValueOfAssertion, keypair, referenceURI);
-    }
-
-    /**
-     * Sign a document
-     *
-     * @param doc
-     * @param idValueOfAssertion
-     * @param keypair
-     * @param referenceURI
-     *
-     * @return
-     *
-     * @throws ParserConfigurationException
-     * @throws XPathException
-     * @throws TransformerFactoryConfigurationError
-     * @throws TransformerException
-     * @throws GeneralSecurityException
-     * @throws MarshalException
-     * @throws XMLSignatureException
-     */
-    public Document sign(Document doc, String idValueOfAssertion, KeyPair keypair, String referenceURI)
-            throws ParserConfigurationException, XPathException, TransformerFactoryConfigurationError, TransformerException,
-            GeneralSecurityException, MarshalException, XMLSignatureException {
-        return sign(doc, idValueOfAssertion, keypair);
+        return XMLSignatureUtil.sign(doc, keyPair, digestMethod, signatureMethod, referenceURI, canonicalizationMethodType);
     }
 
     /**
@@ -283,11 +163,11 @@ public class SAML2Signature {
      *
      * @throws org.keycloak.saml.common.exceptions.ProcessingException
      */
-    public void signSAMLDocument(Document samlDocument, KeyPair keypair) throws ProcessingException {
+    public void signSAMLDocument(Document samlDocument, KeyPair keypair, String canonicalizationMethodType) throws ProcessingException {
         // Get the ID from the root
         String id = samlDocument.getDocumentElement().getAttribute(ID_ATTRIBUTE_NAME);
         try {
-            sign(samlDocument, id, keypair);
+            sign(samlDocument, id, keypair, canonicalizationMethodType);
         } catch (Exception e) {
             throw new ProcessingException(logger.signatureError(e));
         }
diff --git a/saml/saml-core/src/main/java/org/keycloak/saml/processing/core/util/XMLSignatureUtil.java b/saml/saml-core/src/main/java/org/keycloak/saml/processing/core/util/XMLSignatureUtil.java
index aa4322e..42437f1 100755
--- a/saml/saml-core/src/main/java/org/keycloak/saml/processing/core/util/XMLSignatureUtil.java
+++ b/saml/saml-core/src/main/java/org/keycloak/saml/processing/core/util/XMLSignatureUtil.java
@@ -106,7 +106,7 @@ public class XMLSignatureUtil {
 
     ;
 
-    private static String canonicalizationMethodType = CanonicalizationMethod.EXCLUSIVE_WITH_COMMENTS;
+    private static String canonicalizationMethodType = CanonicalizationMethod.EXCLUSIVE;
 
     private static XMLSignatureFactory fac = getXMLSignatureFactory();
 
@@ -131,16 +131,6 @@ public class XMLSignatureUtil {
     }
 
     /**
-     * Set the canonicalization method type
-     *
-     * @param canonical
-     */
-    public static void setCanonicalizationMethodType(String canonical) {
-        if (canonical != null)
-            canonicalizationMethodType = canonical;
-    }
-
-    /**
      * Use this method to not include the KeyInfo in the signature
      *
      * @param includeKeyInfoInSignature
@@ -152,115 +142,11 @@ public class XMLSignatureUtil {
     }
 
     /**
-     * Precheck whether the document that will be validated has the right signedinfo
-     *
-     * @param doc
-     *
-     * @return
-     */
-    public static boolean preCheckSignedInfo(Document doc) {
-        NodeList nl = doc.getElementsByTagNameNS(JBossSAMLURIConstants.XMLDSIG_NSURI.get(), "SignedInfo");
-        return nl != null ? nl.getLength() > 0 : false;
-    }
-
-    /**
-     * Sign a node in a document
-     *
-     * @param doc Document
-     * @param parentOfNodeToBeSigned Parent Node of the node to be signed
-     * @param signingKey Private Key
-     * @param certificate X509 Certificate holding the public key
-     * @param digestMethod (Example: DigestMethod.SHA1)
-     * @param signatureMethod (Example: SignatureMethod.DSA_SHA1)
-     * @param referenceURI
-     *
-     * @return Document that contains the signed node
-     *
-     * @throws XMLSignatureException
-     * @throws MarshalException
-     * @throws GeneralSecurityException
-     * @throws ParserConfigurationException
-     */
-    public static Document sign(Document doc, Node parentOfNodeToBeSigned, PrivateKey signingKey, X509Certificate certificate,
-                                String digestMethod, String signatureMethod, String referenceURI) throws ParserConfigurationException,
-            GeneralSecurityException, MarshalException, XMLSignatureException {
-        KeyPair keyPair = new KeyPair(certificate.getPublicKey(), signingKey);
-        return sign(doc, parentOfNodeToBeSigned, keyPair, digestMethod, signatureMethod, referenceURI);
-    }
-
-    /**
-     * Sign a node in a document
-     *
-     * @param doc
-     * @param nodeToBeSigned
-     * @param keyPair
-     * @param publicKey
-     * @param digestMethod
-     * @param signatureMethod
-     * @param referenceURI
-     *
-     * @return
-     *
-     * @throws ParserConfigurationException
-     * @throws XMLSignatureException
-     * @throws MarshalException
-     * @throws GeneralSecurityException
-     */
-    public static Document sign(Document doc, Node nodeToBeSigned, KeyPair keyPair, String digestMethod,
-                                String signatureMethod, String referenceURI) throws ParserConfigurationException, GeneralSecurityException,
-            MarshalException, XMLSignatureException {
-        if (nodeToBeSigned == null)
-            throw logger.nullArgumentError("Node to be signed");
-
-        if (logger.isTraceEnabled()) {
-            logger.trace("Document to be signed=" + DocumentUtil.asString(doc));
-        }
-
-        Node parentNode = nodeToBeSigned.getParentNode();
-
-        // Let us create a new Document
-        Document newDoc = DocumentUtil.createDocument();
-        // Import the node
-        Node signingNode = newDoc.importNode(nodeToBeSigned, true);
-        newDoc.appendChild(signingNode);
-
-        if (!referenceURI.isEmpty()) {
-            propagateIDAttributeSetup(nodeToBeSigned, newDoc.getDocumentElement());
-        }
-        newDoc = sign(newDoc, keyPair, digestMethod, signatureMethod, referenceURI);
-
-        // if the signed element is a SAMLv2.0 assertion we need to move the signature element to the position
-        // specified in the schema (before the assertion subject element).
-        if (nodeToBeSigned.getLocalName().equals("Assertion")
-                && WSTrustConstants.SAML2_ASSERTION_NS.equals(nodeToBeSigned.getNamespaceURI())) {
-            Node signatureNode = DocumentUtil.getElement(newDoc, new QName(WSTrustConstants.DSIG_NS, "Signature"));
-            Node subjectNode = DocumentUtil.getElement(newDoc, new QName(WSTrustConstants.SAML2_ASSERTION_NS, "Subject"));
-            if (signatureNode != null && subjectNode != null) {
-                newDoc.getDocumentElement().removeChild(signatureNode);
-                newDoc.getDocumentElement().insertBefore(signatureNode, subjectNode);
-            }
-        }
-
-        // Now let us import this signed doc into the original document we got in the method call
-        Node signedNode = doc.importNode(newDoc.getFirstChild(), true);
-
-        if (!referenceURI.isEmpty()) {
-            propagateIDAttributeSetup(newDoc.getDocumentElement(), (Element) signedNode);
-        }
-
-        parentNode.replaceChild(signedNode, nodeToBeSigned);
-        // doc.getDocumentElement().replaceChild(signedNode, nodeToBeSigned);
-
-        return doc;
-    }
-
-    /**
      * Sign a node in a document
      *
      * @param doc
      * @param nodeToBeSigned
      * @param keyPair
-     * @param publicKey
      * @param digestMethod
      * @param signatureMethod
      * @param referenceURI
@@ -273,7 +159,8 @@ public class XMLSignatureUtil {
      * @throws GeneralSecurityException
      */
     public static Document sign(Document doc, Node nodeToBeSigned, KeyPair keyPair, String digestMethod,
-                                String signatureMethod, String referenceURI, X509Certificate x509Certificate) throws ParserConfigurationException, GeneralSecurityException,
+                                String signatureMethod, String referenceURI, X509Certificate x509Certificate,
+                                String canonicalizationMethodType) throws ParserConfigurationException, GeneralSecurityException,
             MarshalException, XMLSignatureException {
         if (nodeToBeSigned == null)
             throw logger.nullArgumentError("Node to be signed");
@@ -293,7 +180,7 @@ public class XMLSignatureUtil {
         if (!referenceURI.isEmpty()) {
             propagateIDAttributeSetup(nodeToBeSigned, newDoc.getDocumentElement());
         }
-        newDoc = sign(newDoc, keyPair, digestMethod, signatureMethod, referenceURI, x509Certificate);
+        newDoc = sign(newDoc, keyPair, digestMethod, signatureMethod, referenceURI, x509Certificate, canonicalizationMethodType);
 
         // if the signed element is a SAMLv2.0 assertion we need to move the signature element to the position
         // specified in the schema (before the assertion subject element).
@@ -335,9 +222,9 @@ public class XMLSignatureUtil {
      * @throws XMLSignatureException
      */
     public static void sign(Element elementToSign, Node nextSibling, KeyPair keyPair, String digestMethod,
-                            String signatureMethod, String referenceURI)
+                            String signatureMethod, String referenceURI, String canonicalizationMethodType)
             throws GeneralSecurityException, MarshalException, XMLSignatureException {
-        sign(elementToSign, nextSibling, keyPair, digestMethod, signatureMethod, referenceURI, null);
+        sign(elementToSign, nextSibling, keyPair, digestMethod, signatureMethod, referenceURI, null, canonicalizationMethodType);
     }
 
     /**
@@ -357,14 +244,14 @@ public class XMLSignatureUtil {
      * @since 2.5.0
      */
     public static void sign(Element elementToSign, Node nextSibling, KeyPair keyPair, String digestMethod,
-                            String signatureMethod, String referenceURI, X509Certificate x509Certificate)
+                            String signatureMethod, String referenceURI, X509Certificate x509Certificate, String canonicalizationMethodType)
             throws GeneralSecurityException, MarshalException, XMLSignatureException {
         PrivateKey signingKey = keyPair.getPrivate();
         PublicKey publicKey = keyPair.getPublic();
 
         DOMSignContext dsc = new DOMSignContext(signingKey, elementToSign, nextSibling);
 
-        signImpl(dsc, digestMethod, signatureMethod, referenceURI, publicKey, x509Certificate);
+        signImpl(dsc, digestMethod, signatureMethod, referenceURI, publicKey, x509Certificate, canonicalizationMethodType);
     }
 
     /**
@@ -372,7 +259,6 @@ public class XMLSignatureUtil {
      * <code>sourceNode</code>.
      *
      * @param sourceNode
-     * @param destDocElement
      */
     public static void propagateIDAttributeSetup(Node sourceNode, Element destElement) {
         NamedNodeMap nnm = sourceNode.getAttributes();
@@ -389,8 +275,6 @@ public class XMLSignatureUtil {
      * Sign the root element
      *
      * @param doc
-     * @param signingKey
-     * @param publicKey
      * @param digestMethod
      * @param signatureMethod
      * @param referenceURI
@@ -401,17 +285,15 @@ public class XMLSignatureUtil {
      * @throws XMLSignatureException
      * @throws MarshalException
      */
-    public static Document sign(Document doc, KeyPair keyPair, String digestMethod, String signatureMethod, String referenceURI)
+    public static Document sign(Document doc, KeyPair keyPair, String digestMethod, String signatureMethod, String referenceURI, String canonicalizationMethodType)
             throws GeneralSecurityException, MarshalException, XMLSignatureException {
-        return sign(doc, keyPair, digestMethod, signatureMethod, referenceURI, null);
+        return sign(doc, keyPair, digestMethod, signatureMethod, referenceURI, null, canonicalizationMethodType);
     }
 
     /**
      * Sign the root element
      *
      * @param doc
-     * @param signingKey
-     * @param publicKey
      * @param digestMethod
      * @param signatureMethod
      * @param referenceURI
@@ -424,7 +306,7 @@ public class XMLSignatureUtil {
      * @since 2.5.0
      */
     public static Document sign(Document doc, KeyPair keyPair, String digestMethod, String signatureMethod, String referenceURI,
-                                X509Certificate x509Certificate)
+                                X509Certificate x509Certificate, String canonicalizationMethodType)
             throws GeneralSecurityException, MarshalException, XMLSignatureException {
         logger.trace("Document to be signed=" + DocumentUtil.asString(doc));
         PrivateKey signingKey = keyPair.getPrivate();
@@ -432,7 +314,7 @@ public class XMLSignatureUtil {
 
         DOMSignContext dsc = new DOMSignContext(signingKey, doc.getDocumentElement());
 
-        signImpl(dsc, digestMethod, signatureMethod, referenceURI, publicKey, x509Certificate);
+        signImpl(dsc, digestMethod, signatureMethod, referenceURI, publicKey, x509Certificate, canonicalizationMethodType);
 
         return doc;
     }
@@ -440,12 +322,6 @@ public class XMLSignatureUtil {
     /**
      * Sign the root element
      *
-     * @param doc
-     * @param signingKey
-     * @param publicKey
-     * @param digestMethod
-     * @param signatureMethod
-     * @param referenceURI
      *
      * @return
      *
@@ -453,7 +329,7 @@ public class XMLSignatureUtil {
      * @throws XMLSignatureException
      * @throws MarshalException
      */
-    public static Document sign(SignatureUtilTransferObject dto) throws GeneralSecurityException, MarshalException,
+    public static Document sign(SignatureUtilTransferObject dto, String canonicalizationMethodType) throws GeneralSecurityException, MarshalException,
             XMLSignatureException {
         Document doc = dto.getDocumentToBeSigned();
         KeyPair keyPair = dto.getKeyPair();
@@ -469,7 +345,7 @@ public class XMLSignatureUtil {
 
         DOMSignContext dsc = new DOMSignContext(signingKey, doc.getDocumentElement(), nextSibling);
 
-        signImpl(dsc, digestMethod, signatureMethod, referenceURI, publicKey, dto.getX509Certificate());
+        signImpl(dsc, digestMethod, signatureMethod, referenceURI, publicKey, dto.getX509Certificate(), canonicalizationMethodType);
 
         return doc;
     }
@@ -697,7 +573,7 @@ public class XMLSignatureUtil {
     }
 
     private static void signImpl(DOMSignContext dsc, String digestMethod, String signatureMethod, String referenceURI, PublicKey publicKey,
-                                 X509Certificate x509Certificate)
+                                 X509Certificate x509Certificate, String canonicalizationMethodType)
             throws GeneralSecurityException, MarshalException, XMLSignatureException {
         dsc.setDefaultNamespacePrefix("dsig");
 
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SAML2BindingBuilder.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SAML2BindingBuilder.java
index c59cd34..d2f3545 100755
--- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SAML2BindingBuilder.java
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SAML2BindingBuilder.java
@@ -21,6 +21,7 @@ import javax.ws.rs.core.CacheControl;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriBuilder;
+import javax.xml.crypto.dsig.CanonicalizationMethod;
 import javax.xml.namespace.QName;
 import java.io.IOException;
 import java.net.URI;
@@ -53,6 +54,12 @@ public class SAML2BindingBuilder<T extends SAML2BindingBuilder> {
     protected PublicKey encryptionPublicKey;
     protected String encryptionAlgorithm = "AES";
     protected boolean encrypt;
+    protected String canonicalizationMethodType = CanonicalizationMethod.EXCLUSIVE;
+
+    public T canonicalizationMethod(String method) {
+        this.canonicalizationMethodType = method;
+        return (T)this;
+    }
 
     public T signDocument() {
         this.sign = true;
@@ -260,7 +267,7 @@ public class SAML2BindingBuilder<T extends SAML2BindingBuilder> {
             samlSignature.setX509Certificate(signingCertificate);
         }
 
-        samlSignature.signSAMLDocument(samlDocument, signingKeyPair);
+        samlSignature.signSAMLDocument(samlDocument, signingKeyPair, canonicalizationMethodType);
     }
 
     protected void signAssertion(Document samlDocument) throws ProcessingException {
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SAML2BindingBuilder2.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SAML2BindingBuilder2.java
index 56c94ca..c377206 100755
--- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SAML2BindingBuilder2.java
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SAML2BindingBuilder2.java
@@ -21,6 +21,7 @@ import javax.ws.rs.core.CacheControl;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriBuilder;
+import javax.xml.crypto.dsig.CanonicalizationMethod;
 import javax.xml.namespace.QName;
 import java.io.IOException;
 import java.net.URI;
@@ -50,6 +51,12 @@ public class SAML2BindingBuilder2<T extends SAML2BindingBuilder2> {
     protected PublicKey encryptionPublicKey;
     protected String encryptionAlgorithm = "AES";
     protected boolean encrypt;
+    protected String canonicalizationMethodType = CanonicalizationMethod.EXCLUSIVE;
+
+    public T canonicalizationMethod(String method) {
+        this.canonicalizationMethodType = method;
+        return (T)this;
+    }
 
     public T signDocument() {
         this.sign = true;
@@ -237,7 +244,7 @@ public class SAML2BindingBuilder2<T extends SAML2BindingBuilder2> {
             samlSignature.setX509Certificate(signingCertificate);
         }
 
-        samlSignature.signSAMLDocument(samlDocument, signingKeyPair);
+        samlSignature.signSAMLDocument(samlDocument, signingKeyPair, canonicalizationMethodType);
     }
 
     protected void signAssertion(Document samlDocument) throws ProcessingException {
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocol.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocol.java
index 13233b3..e4e8f6e 100755
--- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocol.java
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocol.java
@@ -74,6 +74,7 @@ public class SamlProtocol implements LoginProtocol {
     public static final String SAML_SINGLE_LOGOUT_SERVICE_URL_REDIRECT_ATTRIBUTE = "saml_single_logout_service_url_redirect";
     public static final String SAML_FORCE_NAME_ID_FORMAT_ATTRIBUTE = "saml_force_name_id_format";
     public static final String SAML_NAME_ID_FORMAT_ATTRIBUTE = "saml_name_id_format";
+    public static final String SAML_CANONICALIZATION_METHOD_ATTRIBUTE = "saml_signature_canonicalization_method";
     public static final String LOGIN_PROTOCOL = "saml";
     public static final String SAML_BINDING = "saml_binding";
     public static final String SAML_IDP_INITIATED_LOGIN = "saml_idp_initiated_login";
@@ -89,6 +90,7 @@ public class SamlProtocol implements LoginProtocol {
     public static final String SAML_LOGOUT_BINDING = "saml.logout.binding";
     public static final String SAML_LOGOUT_REQUEST_ID = "SAML_LOGOUT_REQUEST_ID";
     public static final String SAML_LOGOUT_RELAY_STATE = "SAML_LOGOUT_RELAY_STATE";
+    public static final String SAML_LOGOUT_CANONICALIZATION = "SAML_LOGOUT_CANONICALIZATION";
     public static final String SAML_LOGOUT_BINDING_URI = "SAML_LOGOUT_BINDING_URI";
     public static final String SAML_LOGOUT_SIGNATURE_ALGORITHM = "saml.logout.signature.algorithm";
     public static final String SAML_NAME_ID = "SAML_NAME_ID";
@@ -338,11 +340,19 @@ public class SamlProtocol implements LoginProtocol {
         bindingBuilder.relayState(relayState);
 
         if (requiresRealmSignature(client)) {
+            String canonicalization = client.getAttribute(SAML_CANONICALIZATION_METHOD_ATTRIBUTE);
+            if (canonicalization != null) {
+                bindingBuilder.canonicalizationMethod(canonicalization);
+            }
             bindingBuilder.signatureAlgorithm(getSignatureAlgorithm(client))
                    .signWith(realm.getPrivateKey(), realm.getPublicKey(), realm.getCertificate())
                    .signDocument();
         }
         if (requiresAssertionSignature(client)) {
+            String canonicalization = client.getAttribute(SAML_CANONICALIZATION_METHOD_ATTRIBUTE);
+            if (canonicalization != null) {
+                bindingBuilder.canonicalizationMethod(canonicalization);
+            }
             bindingBuilder.signatureAlgorithm(getSignatureAlgorithm(client))
                     .signWith(realm.getPrivateKey(), realm.getPublicKey(), realm.getCertificate())
                     .signAssertions();
@@ -513,6 +523,10 @@ public class SamlProtocol implements LoginProtocol {
         String signingAlgorithm = userSession.getNote(SAML_LOGOUT_SIGNATURE_ALGORITHM);
         if (signingAlgorithm != null) {
             SignatureAlgorithm algorithm = SignatureAlgorithm.valueOf(signingAlgorithm);
+            String canonicalization = userSession.getNote(SAML_LOGOUT_CANONICALIZATION);
+            if (canonicalization != null) {
+                builder.canonicalizationMethod(canonicalization);
+            }
             builder.signatureAlgorithm(algorithm)
                     .signWith(realm.getPrivateKey(), realm.getPublicKey(), realm.getCertificate())
                     .signDocument();
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 fe4b13e..9b93942 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
@@ -340,6 +340,7 @@ public class SamlService {
                 if (relayState != null) userSession.setNote(SamlProtocol.SAML_LOGOUT_RELAY_STATE, relayState);
                 userSession.setNote(SamlProtocol.SAML_LOGOUT_REQUEST_ID, logoutRequest.getID());
                 userSession.setNote(SamlProtocol.SAML_LOGOUT_BINDING, logoutBinding);
+                userSession.setNote(SamlProtocol.SAML_LOGOUT_CANONICALIZATION, client.getAttribute(SamlProtocol.SAML_CANONICALIZATION_METHOD_ATTRIBUTE));
                 userSession.setNote(AuthenticationManager.KEYCLOAK_LOGOUT_PROTOCOL, SamlProtocol.LOGIN_PROTOCOL);
                 // remove client from logout requests
                 for (ClientSessionModel clientSession : userSession.getClientSessions()) {