keycloak-aplcache

Merge pull request #803 from patriot1burke/master saml

10/28/2014 2:04:11 PM

Changes

Details

diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/app.js b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/app.js
index b46b13f..6ebea5f 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/app.js
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/app.js
@@ -476,8 +476,8 @@ module.config([ '$routeProvider', function($routeProvider) {
             },
             controller : 'ApplicationClusteringNodeCtrl'
         })
-        .when('/realms/:realm/applications/:application/certificate', {
-            templateUrl : 'partials/application-keys.html',
+        .when('/realms/:realm/applications/:application/saml/keys', {
+            templateUrl : 'partials/application-saml-keys.html',
             resolve : {
                 realm : function(RealmLoader) {
                     return RealmLoader();
@@ -486,7 +486,31 @@ module.config([ '$routeProvider', function($routeProvider) {
                     return ApplicationLoader();
                 }
             },
-            controller : 'ApplicationCertificateCtrl'
+            controller : 'ApplicationSamlKeyCtrl'
+        })
+        .when('/realms/:realm/applications/:application/saml/:keyType/import/:attribute', {
+            templateUrl : 'partials/application-saml-key-import.html',
+            resolve : {
+                realm : function(RealmLoader) {
+                    return RealmLoader();
+                },
+                application : function(ApplicationLoader) {
+                    return ApplicationLoader();
+                }
+            },
+            controller : 'ApplicationCertificateImportCtrl'
+        })
+        .when('/realms/:realm/applications/:application/saml/:keyType/export/:attribute', {
+            templateUrl : 'partials/application-saml-key-export.html',
+            resolve : {
+                realm : function(RealmLoader) {
+                    return RealmLoader();
+                },
+                application : function(ApplicationLoader) {
+                    return ApplicationLoader();
+                }
+            },
+            controller : 'ApplicationCertificateExportCtrl'
         })
         .when('/realms/:realm/applications/:application/roles', {
             templateUrl : 'partials/application-role-list.html',
@@ -553,6 +577,9 @@ module.config([ '$routeProvider', function($routeProvider) {
                 },
                 application : function() {
                     return {};
+                },
+                serverInfo : function(ServerInfoLoader) {
+                    return ServerInfoLoader();
                 }
             },
             controller : 'ApplicationDetailCtrl'
@@ -568,6 +595,9 @@ module.config([ '$routeProvider', function($routeProvider) {
                 },
                 application : function(ApplicationLoader) {
                     return ApplicationLoader();
+                },
+                serverInfo : function(ServerInfoLoader) {
+                    return ServerInfoLoader();
                 }
             },
             controller : 'ApplicationDetailCtrl'
@@ -580,10 +610,26 @@ module.config([ '$routeProvider', function($routeProvider) {
                 },
                 applications : function(ApplicationListLoader) {
                     return ApplicationListLoader();
+                },
+                serverInfo : function(ServerInfoLoader) {
+                    return ServerInfoLoader();
                 }
+
             },
             controller : 'ApplicationListCtrl'
         })
+        .when('/import/application/:realm', {
+            templateUrl : 'partials/application-import.html',
+            resolve : {
+                realm : function(RealmLoader) {
+                    return RealmLoader();
+                },
+                serverInfo : function(ServerInfoLoader) {
+                    return ServerInfoLoader();
+                }
+            },
+            controller : 'ApplicationImportCtrl'
+        })
 
         // OAUTH Client
 
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/applications.js b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/applications.js
index e1efcba..3d80c2b 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/applications.js
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/applications.js
@@ -43,16 +43,82 @@ module.controller('ApplicationCredentialsCtrl', function($scope, $location, real
     });
 });
 
-module.controller('ApplicationCertificateCtrl', function($scope, $location, $http, $upload, realm, application,
+module.controller('ApplicationSamlKeyCtrl', function($scope, $location, $http, $upload, realm, application,
                                                          ApplicationCertificate, ApplicationCertificateGenerate,
                                                          ApplicationCertificateDownload, Notifications) {
     $scope.realm = realm;
     $scope.application = application;
-    var jks = {
-        keyAlias: application.name,
-        realmAlias: realm.realm
+
+    var signingKeyInfo = ApplicationCertificate.get({ realm : realm.realm, application : application.id, attribute: 'saml.signing' },
+        function() {
+            $scope.signingKeyInfo = signingKeyInfo;
+        }
+    );
+
+    $scope.generateSigningKey = function() {
+        var keyInfo = ApplicationCertificateGenerate.generate({ realm : realm.realm, application : application.id, attribute: 'saml.signing' },
+            function() {
+                Notifications.success('Signing key has been regenerated.');
+                $scope.signingKeyInfo = keyInfo;
+            },
+            function() {
+                Notifications.error("Signing key was not regenerated.");
+            }
+        );
+    };
+
+    $scope.importSigningKey = function() {
+        $location.url("/realms/" + realm.realm + "/applications/" + application.id + "/saml/Signing/import/saml.signing");
+    };
+
+    $scope.exportSigningKey = function() {
+        $location.url("/realms/" + realm.realm + "/applications/" + application.id + "/saml/Signing/export/saml.signing");
+    };
+
+    var encryptionKeyInfo = ApplicationCertificate.get({ realm : realm.realm, application : application.id, attribute: 'saml.encryption' },
+        function() {
+            $scope.encryptionKeyInfo = encryptionKeyInfo;
+        }
+    );
+
+    $scope.generateEncryptionKey = function() {
+        var keyInfo = ApplicationCertificateGenerate.generate({ realm : realm.realm, application : application.id, attribute: 'saml.encryption' },
+            function() {
+                Notifications.success('Encryption key has been regenerated.');
+                $scope.encryptionKeyInfo = keyInfo;
+            },
+            function() {
+                Notifications.error("Encryption key was not regenerated.");
+            }
+        );
+    };
+
+    $scope.importEncryptionKey = function() {
+        $location.url("/realms/" + realm.realm + "/applications/" + application.id + "/saml/Encryption/import/saml.encryption");
+    };
+
+    $scope.exportEncryptionKey = function() {
+        $location.url("/realms/" + realm.realm + "/applications/" + application.id + "/saml/Encryption/export/saml.encryption");
     };
 
+
+    $scope.$watch(function() {
+        return $location.path();
+    }, function() {
+        $scope.path = $location.path().substring(1).split("/");
+    });
+});
+
+module.controller('ApplicationCertificateImportCtrl', function($scope, $location, $http, $upload, realm, application, $routeParams,
+                                                         ApplicationCertificate, ApplicationCertificateGenerate,
+                                                         ApplicationCertificateDownload, Notifications) {
+
+    var keyType = $routeParams.keyType;
+    var attribute = $routeParams.attribute;
+    $scope.realm = realm;
+    $scope.application = application;
+    $scope.keyType = keyType;
+
     $scope.files = [];
 
     $scope.onFileSelect = function($files) {
@@ -68,8 +134,6 @@ module.controller('ApplicationCertificateCtrl', function($scope, $location, $htt
         "PKCS12"
     ];
 
-    $scope.jks = jks;
-    $scope.jks.format = $scope.keyFormats[0];
     $scope.uploadKeyFormat = $scope.keyFormats[0];
 
     $scope.uploadFile = function() {
@@ -77,13 +141,13 @@ module.controller('ApplicationCertificateCtrl', function($scope, $location, $htt
         for (var i = 0; i < $scope.files.length; i++) {
             var $file = $scope.files[i];
             $scope.upload = $upload.upload({
-                url: authUrl + '/admin/realms/' + realm.realm + '/applications-by-id/' + application.id + '/certificates/upload/jks',
+                url: authUrl + '/admin/realms/' + realm.realm + '/applications-by-id/' + application.id + '/certificates/' + attribute + '/upload',
                 // method: POST or PUT,
                 // headers: {'headerKey': 'headerValue'}, withCredential: true,
                 data: {keystoreFormat: $scope.uploadKeyFormat,
-                       keyAlias: $scope.uploadKeyAlias,
-                       keyPassword: $scope.uploadKeyPassword,
-                       storePassword: $scope.uploadStorePassword
+                    keyAlias: $scope.uploadKeyAlias,
+                    keyPassword: $scope.uploadKeyPassword,
+                    storePassword: $scope.uploadStorePassword
                 },
                 file: $file
                 /* set file formData name for 'Content-Desposition' header. Default: 'file' */
@@ -93,8 +157,8 @@ module.controller('ApplicationCertificateCtrl', function($scope, $location, $htt
             }).progress(function(evt) {
                 console.log('percent: ' + parseInt(100.0 * evt.loaded / evt.total));
             }).success(function(data, status, headers) {
-                $scope.keyInfo = data;
                 Notifications.success("Keystore uploaded successfully.");
+                $location.url("/realms/" + realm.realm + "/applications/" + application.id + "/saml/keys");
             })
                 .error(function() {
                     Notifications.error("The key store can not be uploaded. Please verify the file.");
@@ -104,30 +168,42 @@ module.controller('ApplicationCertificateCtrl', function($scope, $location, $htt
         }
     };
 
+    $scope.$watch(function() {
+        return $location.path();
+    }, function() {
+        $scope.path = $location.path().substring(1).split("/");
+    });
+});
 
+module.controller('ApplicationCertificateExportCtrl', function($scope, $location, $http, $upload, realm, application, $routeParams,
+                                                         ApplicationCertificate, ApplicationCertificateGenerate,
+                                                         ApplicationCertificateDownload, Notifications) {
+    var keyType = $routeParams.keyType;
+    var attribute = $routeParams.attribute;
+    $scope.realm = realm;
+    $scope.application = application;
+    $scope.keyType = keyType;
+    var jks = {
+        keyAlias: application.name,
+        realmAlias: realm.realm
+    };
 
+    $scope.keyFormats = [
+        "JKS",
+        "PKCS12"
+    ];
 
-    var keyInfo = ApplicationCertificate.get({ realm : realm.realm, application : application.id },
+    var keyInfo = ApplicationCertificate.get({ realm : realm.realm, application : application.id, attribute: attribute },
         function() {
             $scope.keyInfo = keyInfo;
         }
     );
+    $scope.jks = jks;
+    $scope.jks.format = $scope.keyFormats[0];
 
-    $scope.generate = function() {
-        var keyInfo = ApplicationCertificateGenerate.generate({ realm : realm.realm, application : application.id },
-            function() {
-                Notifications.success('Client keypair and cert has been changed.');
-                $scope.keyInfo = keyInfo;
-            },
-            function() {
-                Notifications.error("Client keypair and cert was not changed due to a problem.");
-            }
-        );
-    };
-
-    $scope.downloadJKS = function() {
+    $scope.download = function() {
         $http({
-            url: authUrl + '/admin/realms/' + realm.realm + '/applications-by-id/' + application.id + '/certificates/download',
+            url: authUrl + '/admin/realms/' + realm.realm + '/applications-by-id/' + application.id + '/certificates/' + attribute + '/download',
             method: 'POST',
             responseType: 'arraybuffer',
             data: $scope.jks,
@@ -290,9 +366,62 @@ module.controller('ApplicationRoleDetailCtrl', function($scope, realm, applicati
 
 });
 
-module.controller('ApplicationListCtrl', function($scope, realm, applications, Application, $location) {
+module.controller('ApplicationImportCtrl', function($scope, $location, $upload, realm, serverInfo, Notifications) {
+
+    $scope.realm = realm;
+    $scope.configFormats = serverInfo.applicationImporters;
+    $scope.configFormat = null;
+
+    $scope.files = [];
+
+    $scope.onFileSelect = function($files) {
+        $scope.files = $files;
+    };
+
+    $scope.clearFileSelect = function() {
+        $scope.files = null;
+    }
+
+    $scope.uploadFile = function() {
+        //$files: an array of files selected, each file has name, size, and type.
+        for (var i = 0; i < $scope.files.length; i++) {
+            var $file = $scope.files[i];
+            $scope.upload = $upload.upload({
+                url: authUrl + '/admin/realms/' + realm.realm + '/application-importers/' + $scope.configFormat.id + '/upload',
+                // method: POST or PUT,
+                // headers: {'headerKey': 'headerValue'}, withCredential: true,
+                data: {myObj: ""},
+                file: $file
+                /* set file formData name for 'Content-Desposition' header. Default: 'file' */
+                //fileFormDataName: myFile,
+                /* customize how data is added to formData. See #40#issuecomment-28612000 for example */
+                //formDataAppender: function(formData, key, val){}
+            }).progress(function(evt) {
+                console.log('percent: ' + parseInt(100.0 * evt.loaded / evt.total));
+            }).success(function(data, status, headers) {
+                Notifications.success("Uploaded successfully.");
+                $location.url("/realms/" + realm.realm + "/applications");
+            })
+                .error(function() {
+                    Notifications.error("The file can not be uploaded. Please verify the file.");
+
+                });
+            //.then(success, error, progress);
+        }
+    };
+
+    $scope.$watch(function() {
+        return $location.path();
+    }, function() {
+        $scope.path = $location.path().substring(1).split("/");
+    });
+});
+
+
+module.controller('ApplicationListCtrl', function($scope, realm, applications, Application, serverInfo, $location) {
     $scope.realm = realm;
     $scope.applications = applications;
+    $scope.importButton = serverInfo.applicationImporters.length > 0;
     $scope.$watch(function() {
         return $location.path();
     }, function() {
@@ -335,7 +464,7 @@ module.controller('ApplicationInstallationCtrl', function($scope, realm, applica
     }
 });
 
-module.controller('ApplicationDetailCtrl', function($scope, realm, application, Application, $location, Dialog, Notifications) {
+module.controller('ApplicationDetailCtrl', function($scope, realm, application, serverInfo, Application, $location, Dialog, Notifications) {
     console.log('ApplicationDetailCtrl');
 
     $scope.accessTypes = [
@@ -344,10 +473,8 @@ module.controller('ApplicationDetailCtrl', function($scope, realm, application, 
         "bearer-only"
     ];
 
-    $scope.protocols = [
-        "openid-connect",
-        "saml"
-    ];
+    $scope.protocols = serverInfo.protocols;
+
     $scope.signatureAlgorithms = [
         "RSA_SHA1",
         "RSA_SHA256",
@@ -375,11 +502,9 @@ module.controller('ApplicationDetailCtrl', function($scope, realm, application, 
         } else if (application.publicClient) {
             $scope.accessType = $scope.accessTypes[1];
         }
-        if (application.protocol == 'openid-connect') {
-            $scope.protocol = $scope.protocols[0];
-        } else if (application.protocol == 'saml') {
-            $scope.protocol = $scope.protocols[1];
-        } else { // protocol could be null due to older keycloak installs
+        if (application.protocol) {
+            $scope.protocol = $scope.protocols[$scope.protocols.indexOf(application.protocol)];
+        } else {
             $scope.protocol = $scope.protocols[0];
         }
         if (application.attributes['saml.signature.algorithm'] == 'RSA_SHA1') {
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/services.js b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/services.js
index ee4d635..d47e3ab 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/services.js
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/services.js
@@ -708,16 +708,18 @@ module.factory('ApplicationTestNodesAvailable', function($resource) {
 });
 
 module.factory('ApplicationCertificate', function($resource) {
-    return $resource(authUrl + '/admin/realms/:realm/applications-by-id/:application/certificates', {
+    return $resource(authUrl + '/admin/realms/:realm/applications-by-id/:application/certificates/:attribute', {
             realm : '@realm',
-            application : "@application"
+            application : "@application",
+            attribute: "@attribute"
         });
 });
 
 module.factory('ApplicationCertificateGenerate', function($resource) {
-    return $resource(authUrl + '/admin/realms/:realm/applications-by-id/:application/certificates/generate', {
+    return $resource(authUrl + '/admin/realms/:realm/applications-by-id/:application/certificates/:attribute/generate', {
             realm : '@realm',
-            application : "@application"
+            application : "@application",
+            attribute: "@attribute"
         },
         {
             generate : {
@@ -727,9 +729,10 @@ module.factory('ApplicationCertificateGenerate', function($resource) {
 });
 
 module.factory('ApplicationCertificateDownload', function($resource) {
-    return $resource(authUrl + '/admin/realms/:realm/applications-by-id/:application/certificates/download', {
+    return $resource(authUrl + '/admin/realms/:realm/applications-by-id/:application/certificates/:attribute/download', {
         realm : '@realm',
-        application : "@application"
+        application : "@application",
+        attribute: "@attribute"
     },
         {
             download : {
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/application-import.html b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/application-import.html
new file mode 100755
index 0000000..723bf01
--- /dev/null
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/application-import.html
@@ -0,0 +1,42 @@
+<div class="bs-sidebar col-md-3 clearfix" data-ng-include data-src="'partials/realm-menu.html'"></div>
+<div id="content-area" class="col-md-9" role="main">
+    <ul class="nav nav-tabs nav-tabs-pf">
+        <li class="active"><a href="">Application Import</a></li>
+    </ul>
+    <h2></h2>
+    <div id="content">
+        <h2><span>{{application.name}}</span> Application Import <span tooltip-placement="right" tooltip="Helper utility for importing application definitions from various formats." class="fa fa-info-circle"></span></h2>
+        <form class="form-horizontal" name="realmForm" novalidate>
+            <fieldset class="border-top">
+                <div class="form-group input-select">
+                    <label class="col-sm-2 control-label" for="configFormats">Format Option</label>
+                    <div class="col-sm-4">
+                        <div class="input-group">
+                            <div class="select-kc">
+                                <select id="configFormats" name="configFormats" ng-model="configFormat" ng-options="format.name for format in configFormats">
+                                    <option value="" selected> Select a Format </option>
+                                </select>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+                <div class="form-group">
+                    <label class="col-sm-2 control-label">Import File </label>
+                    <div class="col-sm-4">
+                        <div class="controls kc-button-input-file" data-ng-show="!files || files.length == 0">
+                            <a href="#" class="btn btn-default"><span class="kc-icon-upload">Icon: Upload</span>Choose a File...</a>
+                            <input id="import-file" type="file" class="transparent" ng-file-select="onFileSelect($files)">
+                        </div>
+                        <span class="kc-uploaded-file" data-ng-show="files.length > 0">
+                            {{files[0].name}}
+                        </span>
+                    </div>
+                </div>
+                <div class="pull-right form-actions" data-ng-show="files.length > 0">
+                    <button type="submit" data-ng-click="clearFileSelect()" class="btn btn-lg btn-default">Cancel</button>
+                    <button type="submit" data-ng-click="uploadFile()" class="btn btn-lg btn-primary">Import</button>
+                </div>
+            </fieldset>
+        </form>
+    </div>
+</div>
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/application-list.html b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/application-list.html
index f6eec65..364b4c7 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/application-list.html
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/application-list.html
@@ -19,7 +19,8 @@
                         </button>
                     </div>
                     <div class="pull-right">
-                        <a class="btn btn-primary" href="#/create/application/{{realm.realm}}">Add Application</a>
+                        <a class="btn btn-primary" href="#/import/application/{{realm.realm}}" data-ng-show="importButton">Import</a>
+                        <a class="btn btn-primary" href="#/create/application/{{realm.realm}}">Create</a>
                     </div>
                 </th>
             </tr>
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/application-saml-key-export.html b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/application-saml-key-export.html
new file mode 100755
index 0000000..692080a
--- /dev/null
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/application-saml-key-export.html
@@ -0,0 +1,62 @@
+<div class="bs-sidebar col-sm-3 " data-ng-include data-src="'partials/realm-menu.html'"></div>
+<div id="content-area" class="col-sm-9" role="main">
+    <kc-navigation-application></kc-navigation-application>
+    <div id="content">
+        <ol class="breadcrumb" data-ng-hide="create">
+            <li><a href="#/realms/{{realm.realm}}/applications">Applications</a></li>
+            <li><a href="#/realms/{{realm.realm}}/applications/{{application.id}}">{{application.name}}</a></li>
+            <li><a href="#/realms/{{realm.realm}}/applications/{{application.id}}/saml/keys">SAML Keys</a></li>
+            <li class="active">SAML {{keyType}} Key Export</li>
+        </ol>
+        <h2><span>{{application.name}}</span> SAML {{keyType}} Key Export <span tooltip-placement="right" tooltip="Export Appliations key and certificate to file." class="fa fa-info-circle"></span></h2>
+        <form class="form-horizontal" name="keyForm" novalidate kc-read-only="!access.manageRealm">
+            <fieldset class="form-group col-sm-10">
+                <div class="form-group">
+                    <label class="col-sm-2 control-label" for="downloadKeyFormat">Archive Format</label>
+                    <div class="col-sm-6">
+                        <div class="select-kc">
+                            <select id="downloadKeyFormat"
+                                    ng-model="jks.format"
+                                    ng-options="f for f in keyFormats">
+                            </select>
+                        </div>
+                    </div>
+                    <span tooltip-placement="right" tooltip="Java keystore or PKCS12 archive format." class="fa fa-info-circle"></span>
+                </div>
+                <div class="form-group">
+                    <label class="col-sm-2 control-label" for="keyAlias">Key Alias</label>
+                    <div class="col-sm-4">
+                        <input class="form-control" type="text" id="keyAlias" name="keyAlias" data-ng-model="jks.keyAlias" autofocus required>
+                    </div>
+                    <span tooltip-placement="right" tooltip="Archive alias for your private key and certificate." class="fa fa-info-circle"></span>
+                </div>
+                <div class="form-group" data-ng-hide="!keyInfo.privateKey">
+                    <label class="col-sm-2 control-label" for="keyPassword">Key Password</label>
+                    <div class="col-sm-4">
+                        <input class="form-control" type="password" id="keyPassword" name="keyPassword" data-ng-model="jks.keyPassword" autofocus required>
+                    </div>
+                    <span tooltip-placement="right" tooltip="Password to access the private key in the archive" class="fa fa-info-circle"></span>
+                </div>
+                <div class="form-group">
+                    <label class="col-sm-2 control-label" for="realmAlias">Realm Certificate Alias</label>
+                    <div class="col-sm-4">
+                        <input class="form-control" type="text" id="realmAlias" name="realmAlias" data-ng-model="jks.realmAlias" autofocus required>
+                    </div>
+                    <span tooltip-placement="right" tooltip="Realm certificate is stored in archive too.  This is the alias to it." class="fa fa-info-circle"></span>
+                </div>
+                <div class="form-group">
+                    <label class="col-sm-2 control-label" for="storePassword">Store Password</label>
+                    <div class="col-sm-4">
+                        <input class="form-control" type="password" id="storePassword" name="storePassword" data-ng-model="jks.storePassword" autofocus required>
+                    </div>
+                    <span tooltip-placement="right" tooltip="Password to access the archive itself" class="fa fa-info-circle"></span>
+               </div>
+                <div class="form-group" data-ng-show="access.manageRealm">
+                    <div class="pull-right">
+                        <button class="btn btn-primary" type="submit" data-ng-click="download()">Download</button>
+                    </div>
+                </div>
+            </fieldset>
+        </form>
+    </div>
+</div>
\ No newline at end of file
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/application-saml-key-import.html b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/application-saml-key-import.html
new file mode 100755
index 0000000..e72583e
--- /dev/null
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/application-saml-key-import.html
@@ -0,0 +1,59 @@
+<div class="bs-sidebar col-sm-3 " data-ng-include data-src="'partials/realm-menu.html'"></div>
+<div id="content-area" class="col-sm-9" role="main">
+    <kc-navigation-application></kc-navigation-application>
+    <div id="content">
+        <ol class="breadcrumb" data-ng-hide="create">
+            <li><a href="#/realms/{{realm.realm}}/applications">Applications</a></li>
+            <li><a href="#/realms/{{realm.realm}}/applications/{{application.id}}">{{application.name}}</a></li>
+            <li><a href="#/realms/{{realm.realm}}/applications/{{application.id}}/saml/keys">SAML Keys</a></li>
+            <li class="active">SAML {{keyType}} Key Import</li>
+        </ol>
+        <h2><span>{{application.name}}</span> SAML {{keyType}} Key Import <span tooltip-placement="right" tooltip="Upload Key." class="fa fa-info-circle"></span></h2>
+        <form class="form-horizontal" name="keyForm" novalidate kc-read-only="!access.manageRealm">
+            <fieldset>
+                <div class="form-group">
+                    <label class="col-sm-2 control-label" for="uploadKeyFormat">Archive Format</label>
+                    <div class="col-sm-6">
+                        <div class="select-kc">
+                            <select id="uploadKeyFormat"
+                                    ng-model="uploadKeyFormat"
+                                    ng-options="f for f in keyFormats">
+                            </select>
+                        </div>
+                    </div>
+                    <span tooltip-placement="right" tooltip="Java keystore or PKCS12 archive format." class="fa fa-info-circle"></span>
+                </div>
+                <div class="form-group">
+                    <label class="col-sm-2 control-label" for="uploadKeyAlias">Key Alias</label>
+                    <div class="col-sm-4">
+                        <input class="form-control" type="text" id="uploadKeyAlias" name="uploadKeyAlias" data-ng-model="uploadKeyAlias" autofocus required>
+                    </div>
+                    <span tooltip-placement="right" tooltip="Archive alias for your certificate." class="fa fa-info-circle"></span>
+                </div>
+                <div class="form-group">
+                    <label class="col-sm-2 control-label" for="uploadStorePassword">Store Password</label>
+                    <div class="col-sm-4">
+                        <input class="form-control" type="password" id="uploadStorePassword" name="uploadStorePassword" data-ng-model="uploadStorePassword" autofocus required>
+                    </div>
+                    <span tooltip-placement="right" tooltip="Password to access the archive itself" class="fa fa-info-circle"></span>
+                </div>
+                <div class="form-group">
+                    <label class="col-sm-2 control-label">Import File </label>
+                    <div class="col-sm-4">
+                        <div class="controls kc-button-input-file" data-ng-show="!files || files.length == 0">
+                            <a href="#" class="btn btn-default"><span class="kc-icon-upload">Icon: Upload</span>Choose a File...</a>
+                            <input id="import-file" type="file" class="transparent" ng-file-select="onFileSelect($files)">
+                        </div>
+                        <span class="kc-uploaded-file" data-ng-show="files.length > 0">
+                            {{files[0].name}}
+                        </span>
+                    </div>
+                </div>
+                <div class="pull-right form-actions" data-ng-show="files.length > 0">
+                    <button type="submit" data-ng-click="clearFileSelect()" class="btn btn-lg btn-default">Cancel</button>
+                    <button type="submit" data-ng-click="uploadFile()" class="btn btn-lg btn-primary">Import</button>
+                </div>
+            </fieldset>
+        </form>
+    </div>
+</div>
\ No newline at end of file
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/application-saml-keys.html b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/application-saml-keys.html
new file mode 100755
index 0000000..f871bb9
--- /dev/null
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/application-saml-keys.html
@@ -0,0 +1,66 @@
+<div class="bs-sidebar col-sm-3 " data-ng-include data-src="'partials/realm-menu.html'"></div>
+<div id="content-area" class="col-sm-9" role="main">
+    <kc-navigation-application></kc-navigation-application>
+    <div id="content">
+        <ol class="breadcrumb" data-ng-hide="create">
+            <li><a href="#/realms/{{realm.realm}}/applications">Applications</a></li>
+            <li><a href="#/realms/{{realm.realm}}/applications/{{application.id}}">{{application.name}}</a></li>
+            <li class="active">SAML Keys</li>
+        </ol>
+        <h2><span>{{application.name}}</span> SAML Keys <span tooltip-placement="right" tooltip="Application certificates used to sign and encrypt documents." class="fa fa-info-circle"></span></h2>
+        <form class="form-horizontal" name="keyForm" novalidate kc-read-only="!access.manageRealm">
+            <fieldset class="form-group col-sm-10" data-ng-show="application.attributes['saml.client.signature'] == 'true'">
+                <legend uncollapsed><span class="text">Signing Key</span>  <span tooltip-placement="right" tooltip="SAML Signing Key." class="fa fa-info-circle"></span></legend>
+                <div class="form-group" data-ng-hide="!signingKeyInfo.privateKey">
+                    <label class="col-sm-2 control-label" for="signingPrivateKey">Private key</label>
+
+                    <div class="col-sm-10">
+                        <textarea type="text" id="signingPrivateKey" name="signingPrivateKey" class="form-control" rows="5"
+                                  kc-select-action="click" readonly>{{signingKeyInfo.privateKey}}</textarea>
+                    </div>
+                </div>
+                <div class="form-group" data-ng-hide="!signingKeyInfo.certificate">
+                    <label class="col-sm-2 control-label" for="signingCert">Certificate</label>
+
+                    <div class="col-sm-10">
+                        <textarea type="text" id="signingCert" name="signingCert" class="form-control" rows="5"
+                                  kc-select-action="click" readonly>{{signingKeyInfo.certificate}}</textarea>
+                    </div>
+                </div>
+                <div class="form-group" data-ng-show="access.manageRealm">
+                    <div class="pull-right">
+                        <button class="btn btn-primary" type="submit" data-ng-click="generateSigningKey()">Generate new keys</button>
+                        <button class="btn btn-primary" type="submit" data-ng-click="importSigningKey()">Import</button>
+                        <button class="btn btn-primary" type="submit" data-ng-hide="!signingKeyInfo.certificate" data-ng-click="exportSigningKey()">Export</button>
+                    </div>
+                </div>
+            </fieldset>
+            <fieldset class="form-group col-sm-10" data-ng-show="application.attributes['saml.encrypt'] == 'true'">
+                <legend uncollapsed><span class="text">Encryption Key</span>  <span tooltip-placement="right" tooltip="SAML Encryption Key." class="fa fa-info-circle"></span></legend>
+                <div class="form-group" data-ng-hide="!encryptionKeyInfo.privateKey">
+                    <label class="col-sm-2 control-label" for="encryptionPrivateKey">Private key</label>
+
+                    <div class="col-sm-10">
+                        <textarea type="text" id="encryptionPrivateKey" name="encryptionPrivateKey" class="form-control" rows="5"
+                                  kc-select-action="click" readonly>{{encryptionKeyInfo.privateKey}}</textarea>
+                    </div>
+                </div>
+                <div class="form-group" data-ng-hide="!encryptionKeyInfo.certificate">
+                    <label class="col-sm-2 control-label" for="encryptionCert">Certificate</label>
+
+                    <div class="col-sm-10">
+                        <textarea type="text" id="encryptionCert" name="encryptionCert" class="form-control" rows="5"
+                                  kc-select-action="click" readonly>{{encryptionKeyInfo.certificate}}</textarea>
+                    </div>
+                </div>
+                <div class="form-group" data-ng-show="access.manageRealm">
+                    <div class="pull-right">
+                        <button class="btn btn-primary" type="submit" data-ng-click="generateEncryptionKey()">Generate new keys</button>
+                        <button class="btn btn-primary" type="submit" data-ng-click="importEncryptionKey()">Import</button>
+                        <button class="btn btn-primary" type="submit" data-ng-hide="!encryptionKeyInfo.certificate" data-ng-click="exportEncryptionKey()">Export</button>
+                    </div>
+                </div>
+            </fieldset>
+        </form>
+    </div>
+</div>
\ No newline at end of file
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/templates/kc-navigation-application.html b/forms/common-themes/src/main/resources/theme/admin/base/resources/templates/kc-navigation-application.html
index 8e3a963..0234327 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/templates/kc-navigation-application.html
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/templates/kc-navigation-application.html
@@ -1,7 +1,7 @@
 <ul class="nav nav-tabs nav-tabs-pf"  data-ng-show="!create">
     <li ng-class="{active: !path[4]}"><a href="#/realms/{{realm.realm}}/applications/{{application.id}}">Settings</a></li>
     <li ng-class="{active: path[4] == 'credentials'}" data-ng-show="!application.publicClient && application.protocol != 'saml'"><a href="#/realms/{{realm.realm}}/applications/{{application.id}}/credentials">Credentials</a></li>
-    <li ng-class="{active: path[4] == 'certificate'}" data-ng-show="application.protocol == 'saml' && application.attributes['saml.client.signature'] == 'true'"><a href="#/realms/{{realm.realm}}/applications/{{application.id}}/certificate">Application Keys</a></li>
+    <li ng-class="{active: path[4] == 'saml'}" data-ng-show="application.protocol == 'saml' && (application.attributes['saml.client.signature'] == 'true' || application.attributes['saml.encrypt'] == 'true')"><a href="#/realms/{{realm.realm}}/applications/{{application.id}}/saml/keys">SAML Keys</a></li>
     <li ng-class="{active: path[4] == 'roles'}"><a href="#/realms/{{realm.realm}}/applications/{{application.id}}/roles">Roles</a></li>
     <li ng-class="{active: path[4] == 'claims'}" data-ng-show="!application.bearerOnly"><a href="#/realms/{{realm.realm}}/applications/{{application.id}}/claims">Claims</a></li>
     <li ng-class="{active: path[4] == 'scope-mappings'}" data-ng-show="!application.bearerOnly"><a href="#/realms/{{realm.realm}}/applications/{{application.id}}/scope-mappings">Scope</a></li>
diff --git a/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/KeycloakAuthenticatorValve.java b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/KeycloakAuthenticatorValve.java
index 93836a4..9d25e92 100755
--- a/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/KeycloakAuthenticatorValve.java
+++ b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/KeycloakAuthenticatorValve.java
@@ -93,8 +93,8 @@ public class KeycloakAuthenticatorValve extends FormAuthenticator implements Lif
         if (json == null) {
             return null;
         }
-        log.info("**** using " + AdapterConstants.AUTH_DATA_PARAM_NAME);
-        log.info(json);
+        log.debug("**** using " + AdapterConstants.AUTH_DATA_PARAM_NAME);
+        log.debug(json);
         return new ByteArrayInputStream(json.getBytes());
     }
 
@@ -103,7 +103,7 @@ public class KeycloakAuthenticatorValve extends FormAuthenticator implements Lif
         if (is == null) {
             String path = context.getServletContext().getInitParameter("keycloak.config.file");
             if (path == null) {
-                log.info("**** using /WEB-INF/keycloak.json");
+                log.debug("**** using /WEB-INF/keycloak.json");
                 is = context.getServletContext().getResourceAsStream("/WEB-INF/keycloak.json");
             } else {
                 try {
@@ -165,7 +165,7 @@ public class KeycloakAuthenticatorValve extends FormAuthenticator implements Lif
         CatalinaHttpFacade facade = new CatalinaHttpFacade(request, response);
         KeycloakDeployment deployment = deploymentContext.resolveDeployment(facade);
         if (deployment == null || !deployment.isConfigured()) {
-            log.info("*** deployment isn't configured return false");
+            log.debug("*** deployment isn't configured return false");
             return false;
         }
         AdapterTokenStore tokenStore = getTokenStore(request, facade, deployment);
diff --git a/integration/tomcat7/adapter/pom.xml b/integration/tomcat7/adapter/pom.xml
index f60daa3..5f27a92 100755
--- a/integration/tomcat7/adapter/pom.xml
+++ b/integration/tomcat7/adapter/pom.xml
@@ -14,6 +14,12 @@
 	<description />
 
 	<dependencies>
+        <dependency>
+            <groupId>org.jboss.logging</groupId>
+            <artifactId>jboss-logging</artifactId>
+            <version>${jboss.logging.version}</version>
+            <scope>provided</scope>
+        </dependency>
 		<dependency>
 			<groupId>org.keycloak</groupId>
 			<artifactId>keycloak-core</artifactId>
diff --git a/integration/tomcat7/adapter/src/main/java/org/keycloak/adapters/tomcat7/AuthenticatedActionsValve.java b/integration/tomcat7/adapter/src/main/java/org/keycloak/adapters/tomcat7/AuthenticatedActionsValve.java
index c7ea65e..aae9b29 100755
--- a/integration/tomcat7/adapter/src/main/java/org/keycloak/adapters/tomcat7/AuthenticatedActionsValve.java
+++ b/integration/tomcat7/adapter/src/main/java/org/keycloak/adapters/tomcat7/AuthenticatedActionsValve.java
@@ -5,6 +5,7 @@ import org.apache.catalina.Valve;
 import org.apache.catalina.connector.Request;
 import org.apache.catalina.connector.Response;
 import org.apache.catalina.valves.ValveBase;
+import org.jboss.logging.Logger;
 import org.keycloak.adapters.AdapterDeploymentContext;
 import org.keycloak.adapters.AuthenticatedActionsHandler;
 import org.keycloak.adapters.KeycloakDeployment;
@@ -12,7 +13,6 @@ import org.keycloak.adapters.KeycloakDeployment;
 import javax.management.ObjectName;
 import javax.servlet.ServletException;
 import java.io.IOException;
-import java.util.logging.Logger;
 
 /**
  * Pre-installed actions that must be authenticated
@@ -22,16 +22,16 @@ import java.util.logging.Logger;
  * CORS Origin Check and Response headers
  * k_query_bearer_token: Get bearer token from server for Javascripts CORS requests
  *
- * @author <a href="mailto:ungarida@gmail.com">Davide Ungari</a>
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
  * @version $Revision: 1 $
  */
 public class AuthenticatedActionsValve extends ValveBase {
-    private static final Logger log = Logger.getLogger(""+AuthenticatedActionsValve.class);
+    private static final Logger log = Logger.getLogger(AuthenticatedActionsValve.class);
     protected AdapterDeploymentContext deploymentContext;
 
-    public AuthenticatedActionsValve(AdapterDeploymentContext deploymentContext, Valve next, Container container, ObjectName controller) {
+    public AuthenticatedActionsValve(AdapterDeploymentContext deploymentContext, Valve next, Container container) {
         this.deploymentContext = deploymentContext;
-        if (next == null) throw new RuntimeException("WTF is next null?!");
+        if (next == null) throw new RuntimeException("Next valve is null!!!");
         setNext(next);
         setContainer(container);
     }
@@ -39,7 +39,7 @@ public class AuthenticatedActionsValve extends ValveBase {
 
     @Override
     public void invoke(Request request, Response response) throws IOException, ServletException {
-        log.finer("AuthenticatedActionsValve.invoke" + request.getRequestURI());
+        log.debugv("AuthenticatedActionsValve.invoke {0}", request.getRequestURI());
         CatalinaHttpFacade facade = new CatalinaHttpFacade(request, response);
         KeycloakDeployment deployment = deploymentContext.resolveDeployment(facade);
         if (deployment != null && deployment.isConfigured()) {
@@ -51,6 +51,4 @@ public class AuthenticatedActionsValve extends ValveBase {
         }
         getNext().invoke(request, response);
     }
-
-
-}
\ No newline at end of file
+}
diff --git a/integration/tomcat7/adapter/src/main/java/org/keycloak/adapters/tomcat7/CatalinaUserSessionManagement.java b/integration/tomcat7/adapter/src/main/java/org/keycloak/adapters/tomcat7/CatalinaUserSessionManagement.java
index 66258dd..8263e94 100755
--- a/integration/tomcat7/adapter/src/main/java/org/keycloak/adapters/tomcat7/CatalinaUserSessionManagement.java
+++ b/integration/tomcat7/adapter/src/main/java/org/keycloak/adapters/tomcat7/CatalinaUserSessionManagement.java
@@ -5,20 +5,19 @@ import org.apache.catalina.Session;
 import org.apache.catalina.SessionEvent;
 import org.apache.catalina.SessionListener;
 import org.apache.catalina.realm.GenericPrincipal;
+import org.jboss.logging.Logger;
 
 import java.io.IOException;
 import java.util.List;
-import java.util.logging.Logger;
 
 /**
  * Manages relationship to users and sessions so that forced admin logout can be implemented
  *
- * @author <a href="mailto:ungarida@gmail.com">Davide Ungari</a>
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
  * @version $Revision: 1 $
  */
 public class CatalinaUserSessionManagement implements SessionListener {
-
-    private static final Logger log = Logger.getLogger(""+CatalinaUserSessionManagement.class);
+    private static final Logger log = Logger.getLogger(CatalinaUserSessionManagement.class);
 
     public void login(Session session) {
         session.addSessionListener(this);
@@ -32,7 +31,7 @@ public class CatalinaUserSessionManagement implements SessionListener {
     }
 
     public void logoutHttpSessions(Manager sessionManager, List<String> sessionIds) {
-        log.fine("logoutHttpSessions: " + sessionIds);
+        log.debug("logoutHttpSessions: " + sessionIds);
 
         for (String sessionId : sessionIds) {
             logoutSession(sessionManager, sessionId);
@@ -40,14 +39,13 @@ public class CatalinaUserSessionManagement implements SessionListener {
     }
 
     protected void logoutSession(Manager manager, String httpSessionId) {
-        log.fine("logoutHttpSession: " + httpSessionId);
+        log.debug("logoutHttpSession: " + httpSessionId);
 
         Session session;
         try {
             session = manager.findSession(httpSessionId);
         } catch (IOException ioe) {
-            log.warning("IO exception when looking for session " + httpSessionId);
-            ioe.printStackTrace();
+            log.warn("IO exception when looking for session " + httpSessionId, ioe);
             return;
         }
 
@@ -58,7 +56,7 @@ public class CatalinaUserSessionManagement implements SessionListener {
         try {
             session.expire();
         } catch (Exception e) {
-            log.warning("Session not present or already invalidated.");
+            log.warnf("Session not present or already invalidated.");
         }
     }
 
@@ -70,7 +68,7 @@ public class CatalinaUserSessionManagement implements SessionListener {
 
         // Look up the single session id associated with this session (if any)
         Session session = event.getSession();
-        log.fine("Session " + session.getId() + " destroyed");
+        log.debugf("Session %s destroyed", session.getId());
 
         GenericPrincipal principal = (GenericPrincipal) session.getPrincipal();
         if (principal == null) return;
diff --git a/integration/tomcat7/adapter/src/main/java/org/keycloak/adapters/tomcat7/CorsPreflightChecker.java b/integration/tomcat7/adapter/src/main/java/org/keycloak/adapters/tomcat7/CorsPreflightChecker.java
index 7dc3f40..f4fc302 100755
--- a/integration/tomcat7/adapter/src/main/java/org/keycloak/adapters/tomcat7/CorsPreflightChecker.java
+++ b/integration/tomcat7/adapter/src/main/java/org/keycloak/adapters/tomcat7/CorsPreflightChecker.java
@@ -2,17 +2,17 @@ package org.keycloak.adapters.tomcat7;
 
 import org.apache.catalina.connector.Request;
 import org.apache.catalina.connector.Response;
+import org.jboss.logging.Logger;
 import org.keycloak.adapters.KeycloakDeployment;
 
 import javax.servlet.http.HttpServletResponse;
-import java.util.logging.Logger;
 
 /**
- * @author <a href="mailto:ungarida@gmail.com">Davide Ungari</a>
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
  * @version $Revision: 1 $
  */
 public class CorsPreflightChecker {
-    private static final Logger log = Logger.getLogger(""+CorsPreflightChecker.class);
+    private static final Logger log = Logger.getLogger(CorsPreflightChecker.class);
     protected KeycloakDeployment deployment;
 
     public CorsPreflightChecker(KeycloakDeployment deployment) {
@@ -20,17 +20,17 @@ public class CorsPreflightChecker {
     }
 
     public boolean checkCorsPreflight(Request request, Response response) {
-        log.finer("checkCorsPreflight " + request.getRequestURI());
+        log.debugv("checkCorsPreflight {0}", request.getRequestURI());
         if (!request.getMethod().equalsIgnoreCase("OPTIONS")) {
-            log.finer("checkCorsPreflight: not options ");
+            log.debug("checkCorsPreflight: not options ");
             return false;
 
         }
         if (request.getHeader("Origin") == null) {
-            log.finer("checkCorsPreflight: no origin header");
+            log.debug("checkCorsPreflight: no origin header");
             return false;
         }
-        log.finer("Preflight request returning");
+        log.debug("Preflight request returning");
         response.setStatus(HttpServletResponse.SC_OK);
         String origin = request.getHeader("Origin");
         response.setHeader("Access-Control-Allow-Origin", origin);
diff --git a/model/api/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java b/model/api/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java
index 574a76a..f5a1b3a 100755
--- a/model/api/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java
+++ b/model/api/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java
@@ -132,6 +132,7 @@ public final class KeycloakModelUtils {
     }
 
     public static void generateClientKeyPairCertificate(ClientModel client) {
+        String subject = client.getClientId();
         KeyPair keyPair = null;
         try {
             keyPair = KeyPairGenerator.getInstance("RSA").generateKeyPair();
@@ -140,7 +141,7 @@ public final class KeycloakModelUtils {
         }
         X509Certificate certificate = null;
         try {
-            certificate = CertificateUtils.generateV1SelfSignedCertificate(keyPair, client.getClientId());
+            certificate = CertificateUtils.generateV1SelfSignedCertificate(keyPair, subject);
         } catch (Exception e) {
             throw new RuntimeException(e);
         }
diff --git a/saml/saml-protocol/pom.xml b/saml/saml-protocol/pom.xml
index ea64438..7e9c389 100755
--- a/saml/saml-protocol/pom.xml
+++ b/saml/saml-protocol/pom.xml
@@ -19,6 +19,11 @@
     </properties>
     <dependencies>
         <dependency>
+            <groupId>org.jboss.resteasy</groupId>
+            <artifactId>resteasy-multipart-provider</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
             <groupId>org.keycloak</groupId>
             <artifactId>keycloak-core</artifactId>
             <version>${project.version}</version>
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/EntityDescriptorImporter.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/EntityDescriptorImporter.java
new file mode 100755
index 0000000..32f0990
--- /dev/null
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/EntityDescriptorImporter.java
@@ -0,0 +1,21 @@
+package org.keycloak.protocol.saml;
+
+import org.keycloak.exportimport.ApplicationImporter;
+import org.keycloak.models.RealmModel;
+import org.keycloak.services.resources.admin.RealmAuth;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class EntityDescriptorImporter implements ApplicationImporter {
+    @Override
+    public Object createJaxrsService(RealmModel realm, RealmAuth auth) {
+        return new EntityDescriptorImporterService(realm, auth);
+    }
+
+    @Override
+    public void close() {
+
+    }
+}
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/EntityDescriptorImporterFactory.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/EntityDescriptorImporterFactory.java
new file mode 100755
index 0000000..43252b7
--- /dev/null
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/EntityDescriptorImporterFactory.java
@@ -0,0 +1,37 @@
+package org.keycloak.protocol.saml;
+
+import org.keycloak.Config;
+import org.keycloak.exportimport.ApplicationImporter;
+import org.keycloak.exportimport.ApplicationImporterFactory;
+import org.keycloak.models.KeycloakSession;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class EntityDescriptorImporterFactory implements ApplicationImporterFactory {
+    @Override
+    public String getDisplayName() {
+        return "SAML 2.0 Entity Descriptor";
+    }
+
+    @Override
+    public ApplicationImporter create(KeycloakSession session) {
+        return new EntityDescriptorImporter();
+    }
+
+    @Override
+    public void init(Config.Scope config) {
+
+    }
+
+    @Override
+    public void close() {
+
+    }
+
+    @Override
+    public String getId() {
+        return "saml2-entity-descriptor";
+    }
+}
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/EntityDescriptorImporterService.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/EntityDescriptorImporterService.java
new file mode 100755
index 0000000..833fa2d
--- /dev/null
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/EntityDescriptorImporterService.java
@@ -0,0 +1,144 @@
+package org.keycloak.protocol.saml;
+
+import org.jboss.resteasy.plugins.providers.multipart.InputPart;
+import org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataInput;
+import org.keycloak.models.ApplicationModel;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.services.resources.admin.RealmAuth;
+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.core.parsers.saml.SAMLParser;
+import org.picketlink.identity.federation.core.saml.v2.util.SAMLMetadataUtil;
+import org.picketlink.identity.federation.core.util.CoreConfigUtil;
+import org.picketlink.identity.federation.saml.v2.metadata.EndpointType;
+import org.picketlink.identity.federation.saml.v2.metadata.EntitiesDescriptorType;
+import org.picketlink.identity.federation.saml.v2.metadata.EntityDescriptorType;
+import org.picketlink.identity.federation.saml.v2.metadata.KeyDescriptorType;
+import org.picketlink.identity.federation.saml.v2.metadata.KeyTypes;
+import org.picketlink.identity.federation.saml.v2.metadata.SPSSODescriptorType;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.UriInfo;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.cert.X509Certificate;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class EntityDescriptorImporterService {
+    protected RealmModel realm;
+    protected RealmAuth auth;
+
+    public EntityDescriptorImporterService(RealmModel realm, RealmAuth auth) {
+        this.realm = realm;
+        this.auth = auth;
+    }
+
+    @POST
+    @Path("upload")
+    @Consumes(MediaType.MULTIPART_FORM_DATA)
+    public void updateEntityDescriptor(@Context final UriInfo uriInfo, MultipartFormDataInput input) throws IOException {
+        auth.requireManage();
+
+        Map<String, List<InputPart>> uploadForm = input.getFormDataMap();
+        List<InputPart> inputParts = uploadForm.get("file");
+
+        InputStream is = inputParts.get(0).getBody(InputStream.class, null);
+
+        loadEntityDescriptors(is, realm);
+
+    }
+
+    public static void loadEntityDescriptors(InputStream is, RealmModel realm) {
+        Object metadata = null;
+        try {
+            metadata = new SAMLParser().parse(is);
+        } catch (ParsingException e) {
+            throw new RuntimeException(e);
+        }
+        EntitiesDescriptorType entities;
+
+        if (EntitiesDescriptorType.class.isInstance(metadata)) {
+            entities = (EntitiesDescriptorType) metadata;
+        } else {
+            entities = new EntitiesDescriptorType();
+            entities.addEntityDescriptor(metadata);
+        }
+
+        for (Object o : entities.getEntityDescriptor()) {
+            EntityDescriptorType entity = (EntityDescriptorType)o;
+            String entityId = entity.getEntityID();
+            ApplicationModel app = realm.addApplication(entityId);
+            app.setFullScopeAllowed(true);
+            app.setProtocol(SamlProtocol.LOGIN_PROTOCOL);
+            app.setAttribute(SamlProtocol.SAML_SERVER_SIGNATURE, SamlProtocol.ATTRIBUTE_TRUE_VALUE); // default to true
+            app.setAttribute(SamlProtocol.SAML_SIGNATURE_ALGORITHM, SignatureAlgorithm.RSA_SHA256.toString());
+            app.setAttribute(SamlProtocol.SAML_AUTHNSTATEMENT, SamlProtocol.ATTRIBUTE_TRUE_VALUE);
+            SPSSODescriptorType spDescriptorType = CoreConfigUtil.getSPDescriptor(entity);
+            if (spDescriptorType.isWantAssertionsSigned()) {
+                app.setAttribute(SamlProtocol.SAML_ASSERTION_SIGNATURE, SamlProtocol.ATTRIBUTE_TRUE_VALUE);
+            }
+            String adminUrl = getLogoutLocation(spDescriptorType, JBossSAMLURIConstants.SAML_HTTP_POST_BINDING.get());
+            if (adminUrl != null) app.setManagementUrl(adminUrl);
+
+            String urlPattern = CoreConfigUtil.getServiceURL(spDescriptorType, JBossSAMLURIConstants.SAML_HTTP_POST_BINDING.get());
+            if (urlPattern == null) {
+                urlPattern = CoreConfigUtil.getServiceURL(spDescriptorType, JBossSAMLURIConstants.SAML_HTTP_REDIRECT_BINDING.get());
+            }
+            if (urlPattern != null) {
+                app.addRedirectUri(urlPattern);
+            }
+
+            for (KeyDescriptorType keyDescriptor : spDescriptorType.getKeyDescriptor()) {
+                X509Certificate cert = null;
+                try {
+                    cert = SAMLMetadataUtil.getCertificate(keyDescriptor);
+                } catch (ConfigurationException e) {
+                    throw new RuntimeException(e);
+                } catch (ProcessingException e) {
+                    throw new RuntimeException(e);
+                }
+                String certPem = KeycloakModelUtils.getPemFromCertificate(cert);
+                if (keyDescriptor.getUse() == KeyTypes.SIGNING) {
+                    app.setAttribute(SamlProtocol.SAML_CLIENT_SIGNATURE_ATTRIBUTE, SamlProtocol.ATTRIBUTE_TRUE_VALUE);
+                    app.setAttribute(SamlProtocol.SAML_SIGNING_CERTIFICATE_ATTRIBUTE, certPem);
+                } else if (keyDescriptor.getUse() == KeyTypes.ENCRYPTION) {
+                    app.setAttribute(SamlProtocol.SAML_ENCRYPT, SamlProtocol.ATTRIBUTE_TRUE_VALUE);
+                    app.setAttribute(SamlProtocol.SAML_ENCRYPTION_CERTIFICATE_ATTRIBUTE, certPem);
+                }
+            }
+        }
+    }
+
+    public static String getLogoutLocation(SPSSODescriptorType idp, String bindingURI) {
+        String logoutResponseLocation = null;
+
+        List<EndpointType> endpoints = idp.getSingleLogoutService();
+        for (EndpointType endpoint : endpoints) {
+            if (endpoint.getBinding().toString().equals(bindingURI)) {
+                if (endpoint.getLocation() != null) {
+                    logoutResponseLocation = endpoint.getLocation().toString();
+                } else {
+                    logoutResponseLocation = null;
+                }
+
+                break;
+            }
+
+        }
+        return logoutResponseLocation;
+    }
+
+
+}
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 99b7861..e5d1571 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
@@ -17,8 +17,8 @@ import org.keycloak.protocol.LoginProtocol;
 import org.keycloak.services.managers.ClientSessionCode;
 import org.keycloak.services.managers.ResourceAdminManager;
 import org.keycloak.services.resources.RealmsResource;
+import org.keycloak.services.resources.admin.ClientAttributeCertificateResource;
 import org.keycloak.services.resources.flows.Flows;
-import org.keycloak.util.PemUtils;
 import org.picketlink.common.constants.GeneralConstants;
 import org.picketlink.common.constants.JBossSAMLURIConstants;
 import org.picketlink.identity.federation.core.saml.v2.constants.X500SAMLProfileConstants;
@@ -35,6 +35,13 @@ import java.security.PublicKey;
  */
 public class SamlProtocol implements LoginProtocol {
     protected static final Logger logger = Logger.getLogger(SamlProtocol.class);
+
+
+    public static final String ATTRIBUTE_TRUE_VALUE = "true";
+    public static final String ATTRIBUTE_FALSE_VALUE = "false";
+    public static final String SAML_SIGNING_CERTIFICATE_ATTRIBUTE = "saml.signing." + ClientAttributeCertificateResource.X509CERTIFICATE;
+    public static final String SAML_ENCRYPTION_CERTIFICATE_ATTRIBUTE = "saml.encryption." + ClientAttributeCertificateResource.X509CERTIFICATE;
+    public static final String SAML_CLIENT_SIGNATURE_ATTRIBUTE = "saml.client.signature";
     public static final String LOGIN_PROTOCOL = "saml";
     public static final String SAML_BINDING = "saml_binding";
     public static final String SAML_POST_BINDING = "post";
@@ -46,7 +53,7 @@ public class SamlProtocol implements LoginProtocol {
     public static final String SAML_SIGNATURE_ALGORITHM = "saml.signature.algorithm";
     public static final String SAML_ENCRYPT = "saml.encrypt";
     public static final String SAML_FORCE_POST_BINDING = "saml.force.post.binding";
-    public static final String REQUEST_ID = "REQUEST_ID";
+    public static final String SAML_REQUEST_ID = "SAML_REQUEST_ID";
 
     protected KeycloakSession session;
 
@@ -114,7 +121,7 @@ public class SamlProtocol implements LoginProtocol {
     public Response authenticated(UserSessionModel userSession, ClientSessionCode accessCode) {
         ClientSessionModel clientSession = accessCode.getClientSession();
         ClientModel client = clientSession.getClient();
-        String requestID = clientSession.getNote(REQUEST_ID);
+        String requestID = clientSession.getNote(SAML_REQUEST_ID);
         String relayState = clientSession.getNote(GeneralConstants.RELAY_STATE);
         String redirectUri = clientSession.getRedirectUri();
         String responseIssuer = getResponseIssuer(realm);
@@ -155,7 +162,7 @@ public class SamlProtocol implements LoginProtocol {
         if (requiresEncryption(client)) {
             PublicKey publicKey = null;
             try {
-                publicKey = PemUtils.decodePublicKey(client.getAttribute(ClientModel.PUBLIC_KEY));
+                publicKey = SamlProtocolUtils.getEncryptionValidationKey(client);
             } catch (Exception e) {
                 logger.error("failed", e);
                 return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Failed to process response");
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
index 66828b2..7ddabe9 100755
--- 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
@@ -6,11 +6,9 @@ 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;
+import java.security.cert.Certificate;
 
 /**
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -19,11 +17,11 @@ import java.security.cert.X509Certificate;
 public class SamlProtocolUtils {
 
     public static void verifyDocumentSignature(ClientModel client, Document document) throws VerificationException {
-        if (!"true".equals(client.getAttribute("saml.client.signature"))) {
+        if (!"true".equals(client.getAttribute(SamlProtocol.SAML_CLIENT_SIGNATURE_ATTRIBUTE))) {
             return;
         }
         SAML2Signature saml2Signature = new SAML2Signature();
-        PublicKey publicKey = getPublicKey(client);
+        PublicKey publicKey = getSignatureValidationKey(client);
         try {
             if (!saml2Signature.validate(document, publicKey)) {
                 throw new VerificationException("Invalid signature on document");
@@ -33,16 +31,24 @@ public class SamlProtocolUtils {
         }
     }
 
-    public static PublicKey getPublicKey(ClientModel client) throws VerificationException {
-        String publicKeyPem = client.getAttribute(ClientModel.PUBLIC_KEY);
-        if (publicKeyPem == null) throw new VerificationException("Client does not have a public key.");
-        PublicKey publicKey = null;
+    public static PublicKey getSignatureValidationKey(ClientModel client) throws VerificationException {
+        return getPublicKey(client, SamlProtocol.SAML_SIGNING_CERTIFICATE_ATTRIBUTE);
+    }
+
+    public static PublicKey getEncryptionValidationKey(ClientModel client) throws VerificationException {
+        return getPublicKey(client, SamlProtocol.SAML_ENCRYPTION_CERTIFICATE_ATTRIBUTE);
+    }
+
+    public static PublicKey getPublicKey(ClientModel client, String attribute) throws VerificationException {
+        String certPem = client.getAttribute(attribute);
+        if (certPem == null) throw new VerificationException("Client does not have a public key.");
+        Certificate cert = null;
         try {
-            publicKey = PemUtils.decodePublicKey(publicKeyPem);
+            cert = PemUtils.decodeCertificate(certPem);
         } catch (Exception e) {
-            throw new VerificationException("Could not decode public key", e);
+            throw new VerificationException("Could not decode cert", e);
         }
-        return publicKey;
+        return cert.getPublicKey();
     }
 
 
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 0fc67a8..8f51234 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
@@ -198,7 +198,7 @@ public class SamlService {
             clientSession.setAction(ClientSessionModel.Action.AUTHENTICATE);
             clientSession.setNote(SamlProtocol.SAML_BINDING, getBindingType());
             clientSession.setNote(GeneralConstants.RELAY_STATE, relayState);
-            clientSession.setNote(SamlProtocol.REQUEST_ID, requestAbstractType.getID());
+            clientSession.setNote(SamlProtocol.SAML_REQUEST_ID, requestAbstractType.getID());
 
             Response response = authManager.checkNonFormAuthentication(session, clientSession, realm, uriInfo, request, clientConnection, headers, event);
             if (response != null) return response;
@@ -309,7 +309,7 @@ public class SamlService {
             // todo maybe a flag?
             // SamlProtocolUtils.verifyDocumentSignature(client, documentHolder.getSamlDocument());
 
-            PublicKey publicKey = SamlProtocolUtils.getPublicKey(client);
+            PublicKey publicKey = SamlProtocolUtils.getSignatureValidationKey(client);
 
 
             UriBuilder builder = UriBuilder.fromPath("/")
diff --git a/saml/saml-protocol/src/main/resources/META-INF/services/org.keycloak.exportimport.ApplicationImporterFactory b/saml/saml-protocol/src/main/resources/META-INF/services/org.keycloak.exportimport.ApplicationImporterFactory
new file mode 100755
index 0000000..0971c24
--- /dev/null
+++ b/saml/saml-protocol/src/main/resources/META-INF/services/org.keycloak.exportimport.ApplicationImporterFactory
@@ -0,0 +1 @@
+org.keycloak.protocol.saml.EntityDescriptorImporterFactory
\ No newline at end of file
diff --git a/services/src/main/java/org/keycloak/exportimport/ApplicationImporter.java b/services/src/main/java/org/keycloak/exportimport/ApplicationImporter.java
new file mode 100755
index 0000000..f4c526e
--- /dev/null
+++ b/services/src/main/java/org/keycloak/exportimport/ApplicationImporter.java
@@ -0,0 +1,15 @@
+package org.keycloak.exportimport;
+
+import org.keycloak.models.RealmModel;
+import org.keycloak.provider.Provider;
+import org.keycloak.services.resources.admin.RealmAuth;
+
+/**
+ * Provider plugin interface for importing applications from an arbitrary configuration format
+ *
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public interface ApplicationImporter extends Provider {
+    public Object createJaxrsService(RealmModel realm, RealmAuth auth);
+}
diff --git a/services/src/main/java/org/keycloak/exportimport/ApplicationImporterFactory.java b/services/src/main/java/org/keycloak/exportimport/ApplicationImporterFactory.java
new file mode 100755
index 0000000..b73e2e6
--- /dev/null
+++ b/services/src/main/java/org/keycloak/exportimport/ApplicationImporterFactory.java
@@ -0,0 +1,13 @@
+package org.keycloak.exportimport;
+
+import org.keycloak.provider.ProviderFactory;
+
+/**
+ * Provider plugin interface for importing applications from an arbitrary configuration format
+ *
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public interface ApplicationImporterFactory extends ProviderFactory<ApplicationImporter> {
+    public String getDisplayName();
+}
diff --git a/services/src/main/java/org/keycloak/exportimport/ApplicationImportSpi.java b/services/src/main/java/org/keycloak/exportimport/ApplicationImportSpi.java
new file mode 100755
index 0000000..4493b19
--- /dev/null
+++ b/services/src/main/java/org/keycloak/exportimport/ApplicationImportSpi.java
@@ -0,0 +1,26 @@
+package org.keycloak.exportimport;
+
+import org.keycloak.provider.Provider;
+import org.keycloak.provider.ProviderFactory;
+import org.keycloak.provider.Spi;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class ApplicationImportSpi implements Spi {
+
+    @Override
+    public String getName() {
+        return "application-import";
+    }
+
+    @Override
+    public Class<? extends Provider> getProviderClass() {
+        return ApplicationImporter.class;
+    }
+
+    @Override
+    public Class<? extends ProviderFactory> getProviderFactoryClass() {
+        return ApplicationImporterFactory.class;
+    }
+}
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ApplicationResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ApplicationResource.java
index 5e97698..5d4f176 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/ApplicationResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/ApplicationResource.java
@@ -120,9 +120,9 @@ public class ApplicationResource {
         return ModelToRepresentation.toRepresentation(application);
     }
 
-    @Path("certificates")
-    public ClientCertificateResource getCertficateResource() {
-        return new ClientCertificateResource(realm, auth, application, session);
+    @Path("certificates/{attr}")
+    public ClientAttributeCertificateResource getCertficateResource(@PathParam("attr") String attributePrefix) {
+        return new ClientAttributeCertificateResource(realm, auth, application, session, attributePrefix);
     }
 
 
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/OAuthClientResource.java b/services/src/main/java/org/keycloak/services/resources/admin/OAuthClientResource.java
index 4a03d08..63805e4 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/OAuthClientResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/OAuthClientResource.java
@@ -24,6 +24,7 @@ import javax.ws.rs.GET;
 import javax.ws.rs.POST;
 import javax.ws.rs.PUT;
 import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
 import javax.ws.rs.core.Context;
 import javax.ws.rs.core.MediaType;
@@ -72,9 +73,9 @@ public class OAuthClientResource  {
         return new ClaimResource(oauthClient, auth);
     }
 
-    @Path("certificates")
-    public ClientCertificateResource getCertficateResource() {
-        return new ClientCertificateResource(realm, auth, oauthClient, session);
+    @Path("certificates/{attr}")
+    public ClientAttributeCertificateResource getCertficateResource(@PathParam("attr") String attributePrefix) {
+        return new ClientAttributeCertificateResource(realm, auth, oauthClient, session, attributePrefix);
     }
 
 
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
index bc4167e..a8f3707 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
@@ -8,6 +8,7 @@ import org.keycloak.events.Event;
 import org.keycloak.events.EventQuery;
 import org.keycloak.events.EventStoreProvider;
 import org.keycloak.events.EventType;
+import org.keycloak.exportimport.ApplicationImporter;
 import org.keycloak.models.ApplicationModel;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.ModelDuplicateException;
@@ -74,6 +75,17 @@ public class RealmAdminResource {
     }
 
     /**
+     * Base path for importing applications under this realm.
+     *
+     * @return
+     */
+    @Path("application-importers/{formatId}")
+    public Object getApplicationImporter(@PathParam("formatId") String formatId) {
+        ApplicationImporter importer = session.getProvider(ApplicationImporter.class, formatId);
+        return importer.createJaxrsService(realm, auth);
+    }
+
+    /**
      * Base path for managing applications under this realm.
      *
      * @return
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ServerInfoAdminResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ServerInfoAdminResource.java
index 42f3264..a0af968 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/ServerInfoAdminResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/ServerInfoAdminResource.java
@@ -2,9 +2,14 @@ package org.keycloak.services.resources.admin;
 
 import org.keycloak.Version;
 import org.keycloak.events.EventListenerProvider;
+import org.keycloak.exportimport.ApplicationImporter;
+import org.keycloak.exportimport.ApplicationImporterFactory;
 import org.keycloak.freemarker.Theme;
 import org.keycloak.freemarker.ThemeProvider;
 import org.keycloak.models.KeycloakSession;
+import org.keycloak.protocol.LoginProtocol;
+import org.keycloak.protocol.LoginProtocolFactory;
+import org.keycloak.provider.ProviderFactory;
 import org.keycloak.social.SocialProvider;
 import org.keycloak.util.ProviderLoader;
 
@@ -37,6 +42,8 @@ public class ServerInfoAdminResource {
         setSocialProviders(info);
         setThemes(info);
         setEventListeners(info);
+        setProtocols(info);
+        setApplicationImporters(info);
         return info;
     }
 
@@ -69,6 +76,26 @@ public class ServerInfoAdminResource {
         }
     }
 
+
+    private void setProtocols(ServerInfoRepresentation info) {
+        info.protocols = new LinkedList<String>();
+        for (ProviderFactory p : session.getKeycloakSessionFactory().getProviderFactories(LoginProtocol.class)) {
+            info.protocols.add(p.getId());
+        }
+        Collections.sort(info.protocols);
+    }
+
+    private void setApplicationImporters(ServerInfoRepresentation info) {
+        info.applicationImporters = new LinkedList<Map<String, String>>();
+        for (ProviderFactory p : session.getKeycloakSessionFactory().getProviderFactories(ApplicationImporter.class)) {
+            ApplicationImporterFactory factory = (ApplicationImporterFactory)p;
+            Map<String, String> data = new HashMap<String, String>();
+            data.put("id", factory.getId());
+            data.put("name", factory.getDisplayName());
+            info.applicationImporters.add(data);
+        }
+    }
+
     public static class ServerInfoRepresentation {
 
         private String version;
@@ -76,6 +103,8 @@ public class ServerInfoAdminResource {
         private Map<String, List<String>> themes;
 
         private List<String> socialProviders;
+        private List<String> protocols;
+        private List<Map<String, String>> applicationImporters;
 
 
         private List<String> eventListeners;
@@ -102,6 +131,14 @@ public class ServerInfoAdminResource {
         public List<String> getEventListeners() {
             return eventListeners;
         }
+
+        public List<String> getProtocols() {
+            return protocols;
+        }
+
+        public List<Map<String, String>> getApplicationImporters() {
+            return applicationImporters;
+        }
     }
 
 }
diff --git a/services/src/main/resources/META-INF/services/org.keycloak.provider.Spi b/services/src/main/resources/META-INF/services/org.keycloak.provider.Spi
index 62aa8e7..7cea20d 100755
--- a/services/src/main/resources/META-INF/services/org.keycloak.provider.Spi
+++ b/services/src/main/resources/META-INF/services/org.keycloak.provider.Spi
@@ -1 +1,2 @@
-org.keycloak.protocol.LoginProtocolSpi
\ No newline at end of file
+org.keycloak.protocol.LoginProtocolSpi
+org.keycloak.exportimport.ApplicationImportSpi
\ No newline at end of file
diff --git a/testsuite/integration/pom.xml b/testsuite/integration/pom.xml
index b7b5428..a117e14 100755
--- a/testsuite/integration/pom.xml
+++ b/testsuite/integration/pom.xml
@@ -224,6 +224,18 @@
         <plugins>
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-jar-plugin</artifactId>
+                <version>2.2</version>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>test-jar</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-deploy-plugin</artifactId>
                 <configuration>
                     <skip>true</skip>
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/saml/SamlBindingTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/saml/SamlBindingTest.java
index 2700d0f..302fc91 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/saml/SamlBindingTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/saml/SamlBindingTest.java
@@ -1,15 +1,40 @@
 package org.keycloak.testsuite.saml;
 
+import org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataOutput;
 import org.junit.Assert;
 import org.junit.ClassRule;
 import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
+import org.keycloak.Config;
+import org.keycloak.models.ApplicationModel;
+import org.keycloak.models.Constants;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.UserSessionModel;
+import org.keycloak.protocol.oidc.TokenManager;
+import org.keycloak.representations.AccessToken;
+import org.keycloak.services.managers.RealmManager;
+import org.keycloak.services.resources.admin.AdminRoot;
 import org.keycloak.testsuite.pages.LoginPage;
 import org.keycloak.testsuite.rule.WebResource;
 import org.keycloak.testsuite.rule.WebRule;
 import org.openqa.selenium.WebDriver;
 
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.ClientRequestContext;
+import javax.ws.rs.client.ClientRequestFilter;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+import java.io.IOException;
+import java.io.InputStream;
+
 /**
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
  * @version $Revision: 1 $
@@ -24,10 +49,12 @@ public class SamlBindingTest {
 
             initializeSamlSecuredWar("/saml/simple-post", "/sales-post",  "post.war", classLoader);
             initializeSamlSecuredWar("/saml/signed-post", "/sales-post-sig",  "post-sig.war", classLoader);
+            initializeSamlSecuredWar("/saml/signed-metadata", "/sales-metadata",  "post-metadata.war", classLoader);
             initializeSamlSecuredWar("/saml/signed-get", "/employee-sig",  "employee-sig.war", classLoader);
             initializeSamlSecuredWar("/saml/bad-client-signed-post", "/bad-client-sales-post-sig",  "bad-client-post-sig.war", classLoader);
             initializeSamlSecuredWar("/saml/bad-realm-signed-post", "/bad-realm-sales-post-sig",  "bad-realm-post-sig.war", classLoader);
             initializeSamlSecuredWar("/saml/encrypted-post", "/sales-post-enc",  "post-enc.war", classLoader);
+            uploadSP();
 
         }
 
@@ -113,5 +140,64 @@ public class SamlBindingTest {
         Assert.assertTrue(driver.getPageSource().contains("null"));
     }
 
+    private static String createToken() {
+        KeycloakSession session = keycloakRule.startSession();
+        try {
+            RealmManager manager = new RealmManager(session);
+
+            RealmModel adminRealm = manager.getRealm(Config.getAdminRealm());
+            ApplicationModel adminConsole = adminRealm.getApplicationByName(Constants.ADMIN_CONSOLE_APPLICATION);
+            TokenManager tm = new TokenManager();
+            UserModel admin = session.users().getUserByUsername("admin", adminRealm);
+            UserSessionModel userSession = session.sessions().createUserSession(adminRealm, admin, "admin", null, "form", false);
+            AccessToken token = tm.createClientAccessToken(tm.getAccess(null, adminConsole, admin), adminRealm, adminConsole, admin, userSession);
+            return tm.encodeToken(adminRealm, token);
+        } finally {
+            keycloakRule.stopSession(session, true);
+        }
+    }
+
+
+    @Test
+    public void testMetadataPostSignedLoginLogout() throws Exception {
+
+        driver.navigate().to("http://localhost:8081/sales-metadata/");
+        Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/auth/realms/demo/protocol/saml");
+        loginPage.login("bburke", "password");
+        Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/sales-metadata/");
+        String pageSource = driver.getPageSource();
+        Assert.assertTrue(pageSource.contains("bburke"));
+        driver.navigate().to("http://localhost:8081/sales-metadata?GLO=true");
+        Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/auth/realms/demo/protocol/saml");
+
+    }
+
+    public static void uploadSP() {
+        String token = createToken();
+        final String authHeader = "Bearer " + token;
+        ClientRequestFilter authFilter = new ClientRequestFilter() {
+            @Override
+            public void filter(ClientRequestContext requestContext) throws IOException {
+                requestContext.getHeaders().add(HttpHeaders.AUTHORIZATION, authHeader);
+            }
+        };
+        Client client = ClientBuilder.newBuilder().register(authFilter).build();
+        UriBuilder authBase = UriBuilder.fromUri("http://localhost:8081/auth");
+        WebTarget adminRealms = client.target(AdminRoot.realmsUrl(authBase));
+
+
+        MultipartFormDataOutput formData = new MultipartFormDataOutput();
+        InputStream is = SamlBindingTest.class.getResourceAsStream("/saml/sp-metadata.xml");
+        Assert.assertNotNull(is);
+        formData.addFormData("file", is, MediaType.APPLICATION_XML_TYPE);
+
+        WebTarget upload = adminRealms.path("demo/application-importers/saml2-entity-descriptor/upload");
+        System.out.println(upload.getUri());
+        Response response = upload.request().post(Entity.entity(formData, MediaType.MULTIPART_FORM_DATA));
+        Assert.assertEquals(204, response.getStatus());
+        response.close();
+        client.close();
+    }
+
 
 }
diff --git a/testsuite/integration/src/test/resources/saml/signed-metadata/WEB-INF/keystore.jks b/testsuite/integration/src/test/resources/saml/signed-metadata/WEB-INF/keystore.jks
new file mode 100755
index 0000000..144830b
Binary files /dev/null and b/testsuite/integration/src/test/resources/saml/signed-metadata/WEB-INF/keystore.jks differ
diff --git a/testsuite/integration/src/test/resources/saml/signed-metadata/WEB-INF/picketlink.xml b/testsuite/integration/src/test/resources/saml/signed-metadata/WEB-INF/picketlink.xml
new file mode 100755
index 0000000..17d37df
--- /dev/null
+++ b/testsuite/integration/src/test/resources/saml/signed-metadata/WEB-INF/picketlink.xml
@@ -0,0 +1,31 @@
+<PicketLink xmlns="urn:picketlink:identity-federation:config:2.1">
+	<PicketLinkSP xmlns="urn:picketlink:identity-federation:config:2.1"
+		ServerEnvironment="tomcat" BindingType="POST" SupportsSignatures="true">
+		<IdentityURL>${idp-sig.url::http://localhost:8081/auth/realms/demo/protocol/saml}
+		</IdentityURL>
+		<ServiceURL>${sales-post-sig.url::http://localhost:8081/sales-metadata/}
+		</ServiceURL>
+		<KeyProvider
+			ClassName="org.picketlink.identity.federation.core.impl.KeyStoreKeyManager">
+			<Auth Key="KeyStoreURL" Value="saml/signed-post/WEB-INF/keystore.jks" />
+			<Auth Key="KeyStorePass" Value="store123" />
+			<Auth Key="SigningKeyPass" Value="test123" />
+			<Auth Key="SigningKeyAlias" Value="http://localhost:8080/sales-post-sig/" />
+			<ValidatingAlias Key="localhost" Value="demo" />
+			<ValidatingAlias Key="127.0.0.1" Value="demo" />
+		</KeyProvider>
+
+	</PicketLinkSP>
+	<Handlers xmlns="urn:picketlink:identity-federation:handler:config:2.1">
+		<Handler
+			class="org.picketlink.identity.federation.web.handlers.saml2.SAML2LogOutHandler" />
+		<Handler
+			class="org.picketlink.identity.federation.web.handlers.saml2.SAML2AuthenticationHandler" />
+		<Handler
+			class="org.picketlink.identity.federation.web.handlers.saml2.RolesGenerationHandler" />
+		<Handler
+			class="org.picketlink.identity.federation.web.handlers.saml2.SAML2SignatureGenerationHandler" />
+		<Handler
+			class="org.picketlink.identity.federation.web.handlers.saml2.SAML2SignatureValidationHandler" />
+	</Handlers>
+</PicketLink>
diff --git a/testsuite/integration/src/test/resources/saml/sp-metadata.xml b/testsuite/integration/src/test/resources/saml/sp-metadata.xml
new file mode 100755
index 0000000..9b8b899
--- /dev/null
+++ b/testsuite/integration/src/test/resources/saml/sp-metadata.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<EntitiesDescriptor Name="urn:mace:shibboleth:testshib:two"
+                    xmlns="urn:oasis:names:tc:SAML:2.0:metadata"
+        >
+    <EntityDescriptor entityID="http://localhost:8081/sales-metadata/">
+        <SPSSODescriptor AuthnRequestsSigned="true"
+                protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol urn:oasis:names:tc:SAML:1.1:protocol http://schemas.xmlsoap.org/ws/2003/07/secext">
+            <NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:transient
+            </NameIDFormat>
+            <SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="http://localhost:8081/sales-metadata/"/>
+            <AssertionConsumerService
+                    Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="http://localhost:8081/sales-metadata/"
+                    index="1" isDefault="true" />
+            <KeyDescriptor use="signing">
+                <dsig:KeyInfo xmlns:dsig="http://www.w3.org/2000/09/xmldsig#">
+                    <dsig:X509Data>
+                        <dsig:X509Certificate>
+                            MIIB1DCCAT0CBgFJGP5dZDANBgkqhkiG9w0BAQsFADAwMS4wLAYDVQQDEyVodHRwOi8vbG9jYWxob3N0OjgwODAvc2FsZXMtcG9zdC1zaWcvMB4XDTE0MTAxNjEyNDQyM1oXDTI0MTAxNjEyNDYwM1owMDEuMCwGA1UEAxMlaHR0cDovL2xvY2FsaG9zdDo4MDgwL3NhbGVzLXBvc3Qtc2lnLzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1RvGu8RjemSJA23nnMksoHA37MqY1DDTxOECY4rPAd9egr7GUNIXE0y1MokaR5R2crNpN8RIRwR8phQtQDjXL82c6W+NLQISxztarQJ7rdNJIYwHY0d5ri1XRpDP8zAuxubPYiMAVYcDkIcvlbBpwh/dRM5I2eElRK+eSiaMkCUCAwEAATANBgkqhkiG9w0BAQsFAAOBgQCLms6htnPaY69k1ntm9a5jgwSn/K61cdai8R8B0ccY7zvinn9AfRD7fiROQpFyY29wKn8WCLrJ86NBXfgFUGyR5nLNHVy3FghE36N2oHy53uichieMxffE6vhkKJ4P8ChfJMMOZlmCPsQPDvjoAghHt4mriFiQgRdPgIy/zDjSNw==
+                        </dsig:X509Certificate>
+                    </dsig:X509Data>
+                </dsig:KeyInfo>
+            </KeyDescriptor>
+        </SPSSODescriptor>
+        <Organization>
+            <OrganizationName xmlns="urn:oasis:names:tc:SAML:2.0:metadata"
+                              xml:lang="en">JBoss</OrganizationName>
+            <OrganizationDisplayName xmlns="urn:oasis:names:tc:SAML:2.0:metadata"
+                                     xml:lang="en">JBoss by Red Hat</OrganizationDisplayName>
+            <OrganizationURL xmlns="urn:oasis:names:tc:SAML:2.0:metadata"
+                             xml:lang="en">http://localhost:8080/sales-metadata/</OrganizationURL>
+        </Organization>
+        <ContactPerson contactType="technical">
+            <GivenName>The</GivenName>
+            <SurName>Admin</SurName>
+            <EmailAddress>admin@mycompany.com</EmailAddress>
+        </ContactPerson>
+    </EntityDescriptor>
+</EntitiesDescriptor>
\ No newline at end of file
diff --git a/testsuite/integration/src/test/resources/saml/testsaml.json b/testsuite/integration/src/test/resources/saml/testsaml.json
index 9196dd7..8098d5b 100755
--- a/testsuite/integration/src/test/resources/saml/testsaml.json
+++ b/testsuite/integration/src/test/resources/saml/testsaml.json
@@ -57,9 +57,8 @@
                 "saml.signature.algorithm": "RSA_SHA256",
                 "saml.client.signature": "true",
                 "saml.authnstatement": "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=="
+                "saml.signing.private.key": "MIICWwIBAAKBgQDVG8a7xGN6ZIkDbeecySygcDfsypjUMNPE4QJjis8B316CvsZQ0hcTTLUyiRpHlHZys2k3xEhHBHymFC1AONcvzZzpb40tAhLHO1qtAnut00khjAdjR3muLVdGkM/zMC7G5s9iIwBVhwOQhy+VsGnCH91EzkjZ4SVEr55KJoyQJQIDAQABAoGADaTtoG/+foOZUiLjRWKL/OmyavK9vjgyFtThNkZY4qHOh0h3og0RdSbgIxAsIpEa1FUwU2W5yvI6mNeJ3ibFgCgcxqPk6GkAC7DWfQfdQ8cS+dCuaFTs8ObIQEvU50YzeNPiiFxRA+MnauCUXaKm/PnDfjd4tPgru7XZvlGh0wECQQDsBbN2cKkBKpr/b5oJiBcBaSZtWiMNuYBDn9x8uORj+Gy/49BUIMHF2EWyxOWz6ocP5YiynNRkPe21Zus7PEr1AkEA5yWQOkxUTIg43s4pxNSeHtL+Ebqcg54lY2xOQK0yufxUVZI8ODctAKmVBMiCKpU3mZQquOaQicuGtocpgxlScQI/YM31zZ5nsxLGf/5GL6KhzPJT0IYn2nk7IoFu7bjn9BjwgcPurpLA52TNMYWQsTqAKwT6DEhG1NaRqNWNpb4VAkBehObAYBwMm5udyHIeEc+CzUalm0iLLa0eRdiN7AUVNpCJ2V2Uo0NcxPux1AgeP5xXydXafDXYkwhINWcNO9qRAkEA58ckAC5loUGwU5dLaugsGH/a2Q8Ac8bmPglwfCstYDpl8Gp/eimb1eKyvDEELOhyImAv4/uZV9wN85V0xZXWsw==",
+                "saml.signing.certificate": "MIIB1DCCAT0CBgFJGP5dZDANBgkqhkiG9w0BAQsFADAwMS4wLAYDVQQDEyVodHRwOi8vbG9jYWxob3N0OjgwODAvc2FsZXMtcG9zdC1zaWcvMB4XDTE0MTAxNjEyNDQyM1oXDTI0MTAxNjEyNDYwM1owMDEuMCwGA1UEAxMlaHR0cDovL2xvY2FsaG9zdDo4MDgwL3NhbGVzLXBvc3Qtc2lnLzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1RvGu8RjemSJA23nnMksoHA37MqY1DDTxOECY4rPAd9egr7GUNIXE0y1MokaR5R2crNpN8RIRwR8phQtQDjXL82c6W+NLQISxztarQJ7rdNJIYwHY0d5ri1XRpDP8zAuxubPYiMAVYcDkIcvlbBpwh/dRM5I2eElRK+eSiaMkCUCAwEAATANBgkqhkiG9w0BAQsFAAOBgQCLms6htnPaY69k1ntm9a5jgwSn/K61cdai8R8B0ccY7zvinn9AfRD7fiROQpFyY29wKn8WCLrJ86NBXfgFUGyR5nLNHVy3FghE36N2oHy53uichieMxffE6vhkKJ4P8ChfJMMOZlmCPsQPDvjoAghHt4mriFiQgRdPgIy/zDjSNw=="
             }
         },
         {
@@ -76,9 +75,8 @@
                 "saml.server.signature": "true",
                 "saml.client.signature": "true",
                 "saml.authnstatement": "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=="
+                "saml.signing.private.key": "MIICWwIBAAKBgQDVG8a7xGN6ZIkDbeecySygcDfsypjUMNPE4QJjis8B316CvsZQ0hcTTLUyiRpHlHZys2k3xEhHBHymFC1AONcvzZzpb40tAhLHO1qtAnut00khjAdjR3muLVdGkM/zMC7G5s9iIwBVhwOQhy+VsGnCH91EzkjZ4SVEr55KJoyQJQIDAQABAoGADaTtoG/+foOZUiLjRWKL/OmyavK9vjgyFtThNkZY4qHOh0h3og0RdSbgIxAsIpEa1FUwU2W5yvI6mNeJ3ibFgCgcxqPk6GkAC7DWfQfdQ8cS+dCuaFTs8ObIQEvU50YzeNPiiFxRA+MnauCUXaKm/PnDfjd4tPgru7XZvlGh0wECQQDsBbN2cKkBKpr/b5oJiBcBaSZtWiMNuYBDn9x8uORj+Gy/49BUIMHF2EWyxOWz6ocP5YiynNRkPe21Zus7PEr1AkEA5yWQOkxUTIg43s4pxNSeHtL+Ebqcg54lY2xOQK0yufxUVZI8ODctAKmVBMiCKpU3mZQquOaQicuGtocpgxlScQI/YM31zZ5nsxLGf/5GL6KhzPJT0IYn2nk7IoFu7bjn9BjwgcPurpLA52TNMYWQsTqAKwT6DEhG1NaRqNWNpb4VAkBehObAYBwMm5udyHIeEc+CzUalm0iLLa0eRdiN7AUVNpCJ2V2Uo0NcxPux1AgeP5xXydXafDXYkwhINWcNO9qRAkEA58ckAC5loUGwU5dLaugsGH/a2Q8Ac8bmPglwfCstYDpl8Gp/eimb1eKyvDEELOhyImAv4/uZV9wN85V0xZXWsw==",
+                "saml.signing.certificate": "MIIB1DCCAT0CBgFJGP5dZDANBgkqhkiG9w0BAQsFADAwMS4wLAYDVQQDEyVodHRwOi8vbG9jYWxob3N0OjgwODAvc2FsZXMtcG9zdC1zaWcvMB4XDTE0MTAxNjEyNDQyM1oXDTI0MTAxNjEyNDYwM1owMDEuMCwGA1UEAxMlaHR0cDovL2xvY2FsaG9zdDo4MDgwL3NhbGVzLXBvc3Qtc2lnLzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1RvGu8RjemSJA23nnMksoHA37MqY1DDTxOECY4rPAd9egr7GUNIXE0y1MokaR5R2crNpN8RIRwR8phQtQDjXL82c6W+NLQISxztarQJ7rdNJIYwHY0d5ri1XRpDP8zAuxubPYiMAVYcDkIcvlbBpwh/dRM5I2eElRK+eSiaMkCUCAwEAATANBgkqhkiG9w0BAQsFAAOBgQCLms6htnPaY69k1ntm9a5jgwSn/K61cdai8R8B0ccY7zvinn9AfRD7fiROQpFyY29wKn8WCLrJ86NBXfgFUGyR5nLNHVy3FghE36N2oHy53uichieMxffE6vhkKJ4P8ChfJMMOZlmCPsQPDvjoAghHt4mriFiQgRdPgIy/zDjSNw=="
             }
         },
         {
@@ -95,9 +93,8 @@
                 "saml.server.signature": "true",
                 "saml.client.signature": "true",
                 "saml.authnstatement": "true",
-                "privateKey": "MIICXQIBAAKBgQDb7kwJPkGdU34hicplwfp6/WmNcaLh94TSc7Jyr9Undp5pkyLgb0DE7EIE+6kSs4LsqCb8HDkB0nLD5DXbBJFd8n0WGoKstelvtg6FtVJMnwN7k7yZbfkPECWH9zF70VeOo9vbzrApNRnct8ZhH5fbflRB4JMA9L9R+LbURdoSKQIDAQABAoGBANtbZG9bruoSGp2s5zhzLzd4hczT6Jfk3o9hYjzNb5Z60ymN3Z1omXtQAdEiiNHkRdNxK+EM7TcKBfmoJqcaeTkW8cksVEAW23ip8W9/XsLqmbU2mRrJiKa+KQNDSHqJi1VGyimi4DDApcaqRZcaKDFXg2KDr/Qt5JFD/o9IIIPZAkEA+ZENdBIlpbUfkJh6Ln+bUTss/FZ1FsrcPZWu13rChRMrsmXsfzu9kZUWdUeQ2Dj5AoW2Q7L/cqdGXS7Mm5XhcwJBAOGZq9axJY5YhKrsksvYRLhQbStmGu5LG75suF+rc/44sFq+aQM7+oeRr4VY88Mvz7mk4esdfnk7ae+cCazqJvMCQQCx1L1cZw3yfRSn6S6u8XjQMjWE/WpjulujeoRiwPPY9WcesOgLZZtYIH8nRL6ehEJTnMnahbLmlPFbttxPRUanAkA11MtSIVcKzkhp2KV2ipZrPJWwI18NuVJXb+3WtjypTrGWFZVNNkSjkLnHIeCYlJIGhDd8OL9zAiBXEm6kmgLNAkBWAg0tK2hCjvzsaA505gWQb4X56uKWdb0IzN+fOLB3Qt7+fLqbVQNQoNGzqey6B4MoS1fUKAStqdGTFYPG/+9t",
-                "publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDb7kwJPkGdU34hicplwfp6/WmNcaLh94TSc7Jyr9Undp5pkyLgb0DE7EIE+6kSs4LsqCb8HDkB0nLD5DXbBJFd8n0WGoKstelvtg6FtVJMnwN7k7yZbfkPECWH9zF70VeOo9vbzrApNRnct8ZhH5fbflRB4JMA9L9R+LbURdoSKQIDAQAB",
-                "X509Certificate": "MIIB1DCCAT0CBgFJGVacCDANBgkqhkiG9w0BAQsFADAwMS4wLAYDVQQDEyVodHRwOi8vbG9jYWxob3N0OjgwODAvc2FsZXMtcG9zdC1lbmMvMB4XDTE0MTAxNjE0MjA0NloXDTI0MTAxNjE0MjIyNlowMDEuMCwGA1UEAxMlaHR0cDovL2xvY2FsaG9zdDo4MDgwL3NhbGVzLXBvc3QtZW5jLzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA2+5MCT5BnVN+IYnKZcH6ev1pjXGi4feE0nOycq/VJ3aeaZMi4G9AxOxCBPupErOC7Kgm/Bw5AdJyw+Q12wSRXfJ9FhqCrLXpb7YOhbVSTJ8De5O8mW35DxAlh/cxe9FXjqPb286wKTUZ3LfGYR+X235UQeCTAPS/Ufi21EXaEikCAwEAATANBgkqhkiG9w0BAQsFAAOBgQBMrfGD9QFfx5v7ld/OAto5rjkTe3R1Qei8XRXfcs83vLaqEzjEtTuLGrJEi55kXuJgBpVmQpnwCCkkjSy0JxbqLDdVi9arfWUxEGmOr01ZHycELhDNaQcFqVMPr5kRHIHgktT8hK2IgCvd3Fy9/JCgUgCPxKfhwecyEOKxUc857g=="
+                "saml.signing.private.key": "MIICXQIBAAKBgQDb7kwJPkGdU34hicplwfp6/WmNcaLh94TSc7Jyr9Undp5pkyLgb0DE7EIE+6kSs4LsqCb8HDkB0nLD5DXbBJFd8n0WGoKstelvtg6FtVJMnwN7k7yZbfkPECWH9zF70VeOo9vbzrApNRnct8ZhH5fbflRB4JMA9L9R+LbURdoSKQIDAQABAoGBANtbZG9bruoSGp2s5zhzLzd4hczT6Jfk3o9hYjzNb5Z60ymN3Z1omXtQAdEiiNHkRdNxK+EM7TcKBfmoJqcaeTkW8cksVEAW23ip8W9/XsLqmbU2mRrJiKa+KQNDSHqJi1VGyimi4DDApcaqRZcaKDFXg2KDr/Qt5JFD/o9IIIPZAkEA+ZENdBIlpbUfkJh6Ln+bUTss/FZ1FsrcPZWu13rChRMrsmXsfzu9kZUWdUeQ2Dj5AoW2Q7L/cqdGXS7Mm5XhcwJBAOGZq9axJY5YhKrsksvYRLhQbStmGu5LG75suF+rc/44sFq+aQM7+oeRr4VY88Mvz7mk4esdfnk7ae+cCazqJvMCQQCx1L1cZw3yfRSn6S6u8XjQMjWE/WpjulujeoRiwPPY9WcesOgLZZtYIH8nRL6ehEJTnMnahbLmlPFbttxPRUanAkA11MtSIVcKzkhp2KV2ipZrPJWwI18NuVJXb+3WtjypTrGWFZVNNkSjkLnHIeCYlJIGhDd8OL9zAiBXEm6kmgLNAkBWAg0tK2hCjvzsaA505gWQb4X56uKWdb0IzN+fOLB3Qt7+fLqbVQNQoNGzqey6B4MoS1fUKAStqdGTFYPG/+9t",
+                "saml.signing.certificate": "MIIB1DCCAT0CBgFJGVacCDANBgkqhkiG9w0BAQsFADAwMS4wLAYDVQQDEyVodHRwOi8vbG9jYWxob3N0OjgwODAvc2FsZXMtcG9zdC1lbmMvMB4XDTE0MTAxNjE0MjA0NloXDTI0MTAxNjE0MjIyNlowMDEuMCwGA1UEAxMlaHR0cDovL2xvY2FsaG9zdDo4MDgwL3NhbGVzLXBvc3QtZW5jLzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA2+5MCT5BnVN+IYnKZcH6ev1pjXGi4feE0nOycq/VJ3aeaZMi4G9AxOxCBPupErOC7Kgm/Bw5AdJyw+Q12wSRXfJ9FhqCrLXpb7YOhbVSTJ8De5O8mW35DxAlh/cxe9FXjqPb286wKTUZ3LfGYR+X235UQeCTAPS/Ufi21EXaEikCAwEAATANBgkqhkiG9w0BAQsFAAOBgQBMrfGD9QFfx5v7ld/OAto5rjkTe3R1Qei8XRXfcs83vLaqEzjEtTuLGrJEi55kXuJgBpVmQpnwCCkkjSy0JxbqLDdVi9arfWUxEGmOr01ZHycELhDNaQcFqVMPr5kRHIHgktT8hK2IgCvd3Fy9/JCgUgCPxKfhwecyEOKxUc857g=="
             }
         },
         {
@@ -116,9 +113,10 @@
                 "saml.client.signature": "true",
                 "saml.encrypt": "true",
                 "saml.authnstatement": "true",
-                "privateKey": "MIICXQIBAAKBgQDb7kwJPkGdU34hicplwfp6/WmNcaLh94TSc7Jyr9Undp5pkyLgb0DE7EIE+6kSs4LsqCb8HDkB0nLD5DXbBJFd8n0WGoKstelvtg6FtVJMnwN7k7yZbfkPECWH9zF70VeOo9vbzrApNRnct8ZhH5fbflRB4JMA9L9R+LbURdoSKQIDAQABAoGBANtbZG9bruoSGp2s5zhzLzd4hczT6Jfk3o9hYjzNb5Z60ymN3Z1omXtQAdEiiNHkRdNxK+EM7TcKBfmoJqcaeTkW8cksVEAW23ip8W9/XsLqmbU2mRrJiKa+KQNDSHqJi1VGyimi4DDApcaqRZcaKDFXg2KDr/Qt5JFD/o9IIIPZAkEA+ZENdBIlpbUfkJh6Ln+bUTss/FZ1FsrcPZWu13rChRMrsmXsfzu9kZUWdUeQ2Dj5AoW2Q7L/cqdGXS7Mm5XhcwJBAOGZq9axJY5YhKrsksvYRLhQbStmGu5LG75suF+rc/44sFq+aQM7+oeRr4VY88Mvz7mk4esdfnk7ae+cCazqJvMCQQCx1L1cZw3yfRSn6S6u8XjQMjWE/WpjulujeoRiwPPY9WcesOgLZZtYIH8nRL6ehEJTnMnahbLmlPFbttxPRUanAkA11MtSIVcKzkhp2KV2ipZrPJWwI18NuVJXb+3WtjypTrGWFZVNNkSjkLnHIeCYlJIGhDd8OL9zAiBXEm6kmgLNAkBWAg0tK2hCjvzsaA505gWQb4X56uKWdb0IzN+fOLB3Qt7+fLqbVQNQoNGzqey6B4MoS1fUKAStqdGTFYPG/+9t",
-                "publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDb7kwJPkGdU34hicplwfp6/WmNcaLh94TSc7Jyr9Undp5pkyLgb0DE7EIE+6kSs4LsqCb8HDkB0nLD5DXbBJFd8n0WGoKstelvtg6FtVJMnwN7k7yZbfkPECWH9zF70VeOo9vbzrApNRnct8ZhH5fbflRB4JMA9L9R+LbURdoSKQIDAQAB",
-                "X509Certificate": "MIIB1DCCAT0CBgFJGVacCDANBgkqhkiG9w0BAQsFADAwMS4wLAYDVQQDEyVodHRwOi8vbG9jYWxob3N0OjgwODAvc2FsZXMtcG9zdC1lbmMvMB4XDTE0MTAxNjE0MjA0NloXDTI0MTAxNjE0MjIyNlowMDEuMCwGA1UEAxMlaHR0cDovL2xvY2FsaG9zdDo4MDgwL3NhbGVzLXBvc3QtZW5jLzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA2+5MCT5BnVN+IYnKZcH6ev1pjXGi4feE0nOycq/VJ3aeaZMi4G9AxOxCBPupErOC7Kgm/Bw5AdJyw+Q12wSRXfJ9FhqCrLXpb7YOhbVSTJ8De5O8mW35DxAlh/cxe9FXjqPb286wKTUZ3LfGYR+X235UQeCTAPS/Ufi21EXaEikCAwEAATANBgkqhkiG9w0BAQsFAAOBgQBMrfGD9QFfx5v7ld/OAto5rjkTe3R1Qei8XRXfcs83vLaqEzjEtTuLGrJEi55kXuJgBpVmQpnwCCkkjSy0JxbqLDdVi9arfWUxEGmOr01ZHycELhDNaQcFqVMPr5kRHIHgktT8hK2IgCvd3Fy9/JCgUgCPxKfhwecyEOKxUc857g=="
+                "saml.signing.private.key": "MIICXQIBAAKBgQDb7kwJPkGdU34hicplwfp6/WmNcaLh94TSc7Jyr9Undp5pkyLgb0DE7EIE+6kSs4LsqCb8HDkB0nLD5DXbBJFd8n0WGoKstelvtg6FtVJMnwN7k7yZbfkPECWH9zF70VeOo9vbzrApNRnct8ZhH5fbflRB4JMA9L9R+LbURdoSKQIDAQABAoGBANtbZG9bruoSGp2s5zhzLzd4hczT6Jfk3o9hYjzNb5Z60ymN3Z1omXtQAdEiiNHkRdNxK+EM7TcKBfmoJqcaeTkW8cksVEAW23ip8W9/XsLqmbU2mRrJiKa+KQNDSHqJi1VGyimi4DDApcaqRZcaKDFXg2KDr/Qt5JFD/o9IIIPZAkEA+ZENdBIlpbUfkJh6Ln+bUTss/FZ1FsrcPZWu13rChRMrsmXsfzu9kZUWdUeQ2Dj5AoW2Q7L/cqdGXS7Mm5XhcwJBAOGZq9axJY5YhKrsksvYRLhQbStmGu5LG75suF+rc/44sFq+aQM7+oeRr4VY88Mvz7mk4esdfnk7ae+cCazqJvMCQQCx1L1cZw3yfRSn6S6u8XjQMjWE/WpjulujeoRiwPPY9WcesOgLZZtYIH8nRL6ehEJTnMnahbLmlPFbttxPRUanAkA11MtSIVcKzkhp2KV2ipZrPJWwI18NuVJXb+3WtjypTrGWFZVNNkSjkLnHIeCYlJIGhDd8OL9zAiBXEm6kmgLNAkBWAg0tK2hCjvzsaA505gWQb4X56uKWdb0IzN+fOLB3Qt7+fLqbVQNQoNGzqey6B4MoS1fUKAStqdGTFYPG/+9t",
+                "saml.signing.certificate": "MIIB1DCCAT0CBgFJGVacCDANBgkqhkiG9w0BAQsFADAwMS4wLAYDVQQDEyVodHRwOi8vbG9jYWxob3N0OjgwODAvc2FsZXMtcG9zdC1lbmMvMB4XDTE0MTAxNjE0MjA0NloXDTI0MTAxNjE0MjIyNlowMDEuMCwGA1UEAxMlaHR0cDovL2xvY2FsaG9zdDo4MDgwL3NhbGVzLXBvc3QtZW5jLzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA2+5MCT5BnVN+IYnKZcH6ev1pjXGi4feE0nOycq/VJ3aeaZMi4G9AxOxCBPupErOC7Kgm/Bw5AdJyw+Q12wSRXfJ9FhqCrLXpb7YOhbVSTJ8De5O8mW35DxAlh/cxe9FXjqPb286wKTUZ3LfGYR+X235UQeCTAPS/Ufi21EXaEikCAwEAATANBgkqhkiG9w0BAQsFAAOBgQBMrfGD9QFfx5v7ld/OAto5rjkTe3R1Qei8XRXfcs83vLaqEzjEtTuLGrJEi55kXuJgBpVmQpnwCCkkjSy0JxbqLDdVi9arfWUxEGmOr01ZHycELhDNaQcFqVMPr5kRHIHgktT8hK2IgCvd3Fy9/JCgUgCPxKfhwecyEOKxUc857g==",
+                "saml.encryption.private.key": "MIICXQIBAAKBgQDb7kwJPkGdU34hicplwfp6/WmNcaLh94TSc7Jyr9Undp5pkyLgb0DE7EIE+6kSs4LsqCb8HDkB0nLD5DXbBJFd8n0WGoKstelvtg6FtVJMnwN7k7yZbfkPECWH9zF70VeOo9vbzrApNRnct8ZhH5fbflRB4JMA9L9R+LbURdoSKQIDAQABAoGBANtbZG9bruoSGp2s5zhzLzd4hczT6Jfk3o9hYjzNb5Z60ymN3Z1omXtQAdEiiNHkRdNxK+EM7TcKBfmoJqcaeTkW8cksVEAW23ip8W9/XsLqmbU2mRrJiKa+KQNDSHqJi1VGyimi4DDApcaqRZcaKDFXg2KDr/Qt5JFD/o9IIIPZAkEA+ZENdBIlpbUfkJh6Ln+bUTss/FZ1FsrcPZWu13rChRMrsmXsfzu9kZUWdUeQ2Dj5AoW2Q7L/cqdGXS7Mm5XhcwJBAOGZq9axJY5YhKrsksvYRLhQbStmGu5LG75suF+rc/44sFq+aQM7+oeRr4VY88Mvz7mk4esdfnk7ae+cCazqJvMCQQCx1L1cZw3yfRSn6S6u8XjQMjWE/WpjulujeoRiwPPY9WcesOgLZZtYIH8nRL6ehEJTnMnahbLmlPFbttxPRUanAkA11MtSIVcKzkhp2KV2ipZrPJWwI18NuVJXb+3WtjypTrGWFZVNNkSjkLnHIeCYlJIGhDd8OL9zAiBXEm6kmgLNAkBWAg0tK2hCjvzsaA505gWQb4X56uKWdb0IzN+fOLB3Qt7+fLqbVQNQoNGzqey6B4MoS1fUKAStqdGTFYPG/+9t",
+                "saml.encryption.certificate": "MIIB1DCCAT0CBgFJGVacCDANBgkqhkiG9w0BAQsFADAwMS4wLAYDVQQDEyVodHRwOi8vbG9jYWxob3N0OjgwODAvc2FsZXMtcG9zdC1lbmMvMB4XDTE0MTAxNjE0MjA0NloXDTI0MTAxNjE0MjIyNlowMDEuMCwGA1UEAxMlaHR0cDovL2xvY2FsaG9zdDo4MDgwL3NhbGVzLXBvc3QtZW5jLzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA2+5MCT5BnVN+IYnKZcH6ev1pjXGi4feE0nOycq/VJ3aeaZMi4G9AxOxCBPupErOC7Kgm/Bw5AdJyw+Q12wSRXfJ9FhqCrLXpb7YOhbVSTJ8De5O8mW35DxAlh/cxe9FXjqPb286wKTUZ3LfGYR+X235UQeCTAPS/Ufi21EXaEikCAwEAATANBgkqhkiG9w0BAQsFAAOBgQBMrfGD9QFfx5v7ld/OAto5rjkTe3R1Qei8XRXfcs83vLaqEzjEtTuLGrJEi55kXuJgBpVmQpnwCCkkjSy0JxbqLDdVi9arfWUxEGmOr01ZHycELhDNaQcFqVMPr5kRHIHgktT8hK2IgCvd3Fy9/JCgUgCPxKfhwecyEOKxUc857g=="
             }
         },
         {
@@ -136,9 +134,8 @@
                 "saml.client.signature": "true",
                 "saml.signature.algorithm": "RSA_SHA1",
                 "saml.authnstatement": "true",
-                "privateKey": "MIICXQIBAAKBgQC+9kVgPFpshjS2aT2g52lqTv2lqb1jgvXZVk7iFF4LAO6SdCXKXRZI4SuzIRkVNpE1a42V1kQRlaozoFklgvX5sje8tkpa9ylq+bxGXM9RRycqRu2B+oWUV7Aqq7Bs0Xud0WeHQYRcEoCjqsFKGy65qkLRDdT70FTJgpSHts+gDwIDAQABAoGANU1efgc6ojIvwn7Lsf8GAKN9z2D6uS0T3I9nw1k2CtI+xWhgKAUltEANx5lEfBRYIdYclidRpqrk8DYgzASrDYTHXzqVBJfAk1VrAGpqyRq+TNMLUHkXiTiSDOQ6WqhX93UGMmAgQm1RsLa6+fy1BO/B2y85+Yf2OUylsKS6avECQQDslRDiNFdtEjdvyOL20tQ7+W+eKVxVxKAyQ3gFjIIDizELZt+Jq1Wz6XV9NhK1JFtlVugeD1tlW/+K16fEmDYXAkEAzqKoN/JeGb20rfQldAUWdQbb0jrQAYlgoSU/9fYH9YVJT8vnkfhPBTwIw9H9euf1//lRP/jHltHd5ch4230YyQJBAN3rOkoltPiABPZbpuLGgwS7BwOCYrWlWmurtBLoaTCvyVKbrgXybNL1pBrOtR+rufvGWLeRyja65Gs1vY6BBQMCQQCTsNq/MjJj/522f7yNUl2cw4w2lOa7Um+IflFbAcDqkZu2ty0Kvgns2d4B6INeZ5ECpjaWnMA7YkFRzZnkd2NRAkB8lEY56ScnNigoZkkjtEUd2ejdhZPYuS9SKfv9zHwN+I+DE2vVFZz8GPq/iLcMx13PkZaYaJNQ4FtQY/hRLSn5",
-                "publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC+9kVgPFpshjS2aT2g52lqTv2lqb1jgvXZVk7iFF4LAO6SdCXKXRZI4SuzIRkVNpE1a42V1kQRlaozoFklgvX5sje8tkpa9ylq+bxGXM9RRycqRu2B+oWUV7Aqq7Bs0Xud0WeHQYRcEoCjqsFKGy65qkLRDdT70FTJgpSHts+gDwIDAQAB",
-                "X509Certificate": "MIIB0DCCATkCBgFJH5u0EDANBgkqhkiG9w0BAQsFADAuMSwwKgYDVQQDEyNodHRwOi8vbG9jYWxob3N0OjgwODAvZW1wbG95ZWUtc2lnLzAeFw0xNDEwMTcxOTMzNThaFw0yNDEwMTcxOTM1MzhaMC4xLDAqBgNVBAMTI2h0dHA6Ly9sb2NhbGhvc3Q6ODA4MC9lbXBsb3llZS1zaWcvMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC+9kVgPFpshjS2aT2g52lqTv2lqb1jgvXZVk7iFF4LAO6SdCXKXRZI4SuzIRkVNpE1a42V1kQRlaozoFklgvX5sje8tkpa9ylq+bxGXM9RRycqRu2B+oWUV7Aqq7Bs0Xud0WeHQYRcEoCjqsFKGy65qkLRDdT70FTJgpSHts+gDwIDAQABMA0GCSqGSIb3DQEBCwUAA4GBACKyPLGqMX8GsIrCfJU8eVnpaqzTXMglLVo/nTcfAnWe9UAdVe8N3a2PXpDBvuqNA/DEAhVcQgxdlOTWnB6s8/yLTRuH0bZgb3qGdySif+lU+E7zZ/SiDzavAvn+ABqemnzHcHyhYO+hNRGHvUbW5OAii9Vdjhm8BI32YF1NwhKp"
+                "saml.signing.private.key": "MIICXQIBAAKBgQC+9kVgPFpshjS2aT2g52lqTv2lqb1jgvXZVk7iFF4LAO6SdCXKXRZI4SuzIRkVNpE1a42V1kQRlaozoFklgvX5sje8tkpa9ylq+bxGXM9RRycqRu2B+oWUV7Aqq7Bs0Xud0WeHQYRcEoCjqsFKGy65qkLRDdT70FTJgpSHts+gDwIDAQABAoGANU1efgc6ojIvwn7Lsf8GAKN9z2D6uS0T3I9nw1k2CtI+xWhgKAUltEANx5lEfBRYIdYclidRpqrk8DYgzASrDYTHXzqVBJfAk1VrAGpqyRq+TNMLUHkXiTiSDOQ6WqhX93UGMmAgQm1RsLa6+fy1BO/B2y85+Yf2OUylsKS6avECQQDslRDiNFdtEjdvyOL20tQ7+W+eKVxVxKAyQ3gFjIIDizELZt+Jq1Wz6XV9NhK1JFtlVugeD1tlW/+K16fEmDYXAkEAzqKoN/JeGb20rfQldAUWdQbb0jrQAYlgoSU/9fYH9YVJT8vnkfhPBTwIw9H9euf1//lRP/jHltHd5ch4230YyQJBAN3rOkoltPiABPZbpuLGgwS7BwOCYrWlWmurtBLoaTCvyVKbrgXybNL1pBrOtR+rufvGWLeRyja65Gs1vY6BBQMCQQCTsNq/MjJj/522f7yNUl2cw4w2lOa7Um+IflFbAcDqkZu2ty0Kvgns2d4B6INeZ5ECpjaWnMA7YkFRzZnkd2NRAkB8lEY56ScnNigoZkkjtEUd2ejdhZPYuS9SKfv9zHwN+I+DE2vVFZz8GPq/iLcMx13PkZaYaJNQ4FtQY/hRLSn5",
+                "saml.signing.certificate": "MIIB0DCCATkCBgFJH5u0EDANBgkqhkiG9w0BAQsFADAuMSwwKgYDVQQDEyNodHRwOi8vbG9jYWxob3N0OjgwODAvZW1wbG95ZWUtc2lnLzAeFw0xNDEwMTcxOTMzNThaFw0yNDEwMTcxOTM1MzhaMC4xLDAqBgNVBAMTI2h0dHA6Ly9sb2NhbGhvc3Q6ODA4MC9lbXBsb3llZS1zaWcvMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC+9kVgPFpshjS2aT2g52lqTv2lqb1jgvXZVk7iFF4LAO6SdCXKXRZI4SuzIRkVNpE1a42V1kQRlaozoFklgvX5sje8tkpa9ylq+bxGXM9RRycqRu2B+oWUV7Aqq7Bs0Xud0WeHQYRcEoCjqsFKGy65qkLRDdT70FTJgpSHts+gDwIDAQABMA0GCSqGSIb3DQEBCwUAA4GBACKyPLGqMX8GsIrCfJU8eVnpaqzTXMglLVo/nTcfAnWe9UAdVe8N3a2PXpDBvuqNA/DEAhVcQgxdlOTWnB6s8/yLTRuH0bZgb3qGdySif+lU+E7zZ/SiDzavAvn+ABqemnzHcHyhYO+hNRGHvUbW5OAii9Vdjhm8BI32YF1NwhKp"
             }
         }
     ],
diff --git a/testsuite/pom.xml b/testsuite/pom.xml
index 8780d19..9c2186d 100755
--- a/testsuite/pom.xml
+++ b/testsuite/pom.xml
@@ -26,6 +26,7 @@
     </build>
     <modules>
         <module>integration</module>
+        <module>tomcat7</module>
         <module>performance</module>
         <module>tools</module>
         <module>performance-web</module>
diff --git a/testsuite/tomcat7/pom.xml b/testsuite/tomcat7/pom.xml
new file mode 100755
index 0000000..419d6e8
--- /dev/null
+++ b/testsuite/tomcat7/pom.xml
@@ -0,0 +1,513 @@
+<?xml version="1.0"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <parent>
+        <artifactId>keycloak-testsuite-pom</artifactId>
+        <groupId>org.keycloak</groupId>
+        <version>1.1.0-Alpha1-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>keycloak-testsuite-tomcat7</artifactId>
+    <name>Keycloak Tomcat 7Integration TestSuite</name>
+    <description />
+
+   <dependencies>
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-dependencies-server-all</artifactId>
+            <version>${project.version}</version>
+            <type>pom</type>
+        </dependency>
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-admin-client</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>log4j</groupId>
+            <artifactId>log4j</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-log4j12</artifactId>
+            <version>${slf4j.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.jboss.spec.javax.servlet</groupId>
+            <artifactId>jboss-servlet-api_3.0_spec</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.jboss.resteasy</groupId>
+            <artifactId>jaxrs-api</artifactId>
+            <version>${resteasy.version.latest}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.jboss.resteasy</groupId>
+            <artifactId>resteasy-jaxrs</artifactId>
+            <version>${resteasy.version.latest}</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>log4j</groupId>
+                    <artifactId>log4j</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.slf4j</groupId>
+                    <artifactId>slf4j-api</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.slf4j</groupId>
+                    <artifactId>slf4j-simple</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>org.jboss.resteasy</groupId>
+            <artifactId>resteasy-client</artifactId>
+            <version>${resteasy.version.latest}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.jboss.resteasy</groupId>
+            <artifactId>resteasy-crypto</artifactId>
+            <version>${resteasy.version.latest}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.jboss.resteasy</groupId>
+            <artifactId>resteasy-multipart-provider</artifactId>
+            <version>${resteasy.version.latest}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.jboss.resteasy</groupId>
+            <artifactId>resteasy-jackson-provider</artifactId>
+            <version>${resteasy.version.latest}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.jboss.resteasy</groupId>
+            <artifactId>resteasy-undertow</artifactId>
+            <version>${resteasy.version.latest}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.google.zxing</groupId>
+            <artifactId>javase</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.bouncycastle</groupId>
+            <artifactId>bcprov-jdk16</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpclient</artifactId>
+            <version>${keycloak.apache.httpcomponents.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-ldap-federation</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-undertow-adapter</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>federation-properties-example</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.jboss.logging</groupId>
+            <artifactId>jboss-logging</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.undertow</groupId>
+            <artifactId>undertow-servlet</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.undertow</groupId>
+            <artifactId>undertow-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.codehaus.jackson</groupId>
+            <artifactId>jackson-core-asl</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.codehaus.jackson</groupId>
+            <artifactId>jackson-mapper-asl</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.codehaus.jackson</groupId>
+            <artifactId>jackson-xc</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.hamcrest</groupId>
+            <artifactId>hamcrest-all</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.hibernate.javax.persistence</groupId>
+            <artifactId>hibernate-jpa-2.0-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.h2database</groupId>
+            <artifactId>h2</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.hibernate</groupId>
+            <artifactId>hibernate-entitymanager</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.icegreen</groupId>
+            <artifactId>greenmail</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.slf4j</groupId>
+                    <artifactId>slf4j-api</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>org.infinispan</groupId>
+            <artifactId>infinispan-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.seleniumhq.selenium</groupId>
+            <artifactId>selenium-java</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>xml-apis</groupId>
+            <artifactId>xml-apis</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.seleniumhq.selenium</groupId>
+            <artifactId>selenium-chrome-driver</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.picketbox</groupId>
+            <artifactId>picketbox-ldap</artifactId>
+            <type>test-jar</type>
+        </dependency>
+        <dependency>
+            <groupId>org.picketbox</groupId>
+            <artifactId>picketbox-ldap</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.picketlink</groupId>
+            <artifactId>picketlink-wildlfy-common</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.wildfly</groupId>
+            <artifactId>wildfly-undertow</artifactId>
+            <version>${wildfly.version}</version>
+            <scope>test</scope>
+        </dependency>
+       <dependency>
+           <groupId>org.keycloak</groupId>
+           <artifactId>keycloak-testsuite-integration</artifactId>
+           <version>${project.version}</version>
+           <scope>test</scope>
+       </dependency>
+       <dependency>
+           <groupId>org.keycloak</groupId>
+           <artifactId>keycloak-testsuite-integration</artifactId>
+           <version>${project.version}</version>
+           <type>test-jar</type>
+           <scope>test</scope>
+       </dependency>
+
+       <dependency>
+           <groupId>org.apache.tomcat</groupId>
+           <artifactId>tomcat-catalina</artifactId>
+           <version>7.0.54</version>
+       </dependency>
+       <dependency>
+           <groupId>org.apache.tomcat</groupId>
+           <artifactId>tomcat-util</artifactId>
+           <version>7.0.54</version>
+       </dependency>
+       <dependency>
+           <groupId>org.apache.tomcat.embed</groupId>
+           <artifactId>tomcat-embed-core</artifactId>
+           <version>7.0.54</version>
+       </dependency>
+
+    </dependencies>
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-jar-plugin</artifactId>
+                <version>2.2</version>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>test-jar</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-deploy-plugin</artifactId>
+                <configuration>
+                    <skip>true</skip>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <source>${maven.compiler.source}</source>
+                    <target>${maven.compiler.target}</target>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>exec-maven-plugin</artifactId>
+                <configuration>
+                    <workingDirectory>${project.basedir}</workingDirectory>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>keycloak-server</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.codehaus.mojo</groupId>
+                        <artifactId>exec-maven-plugin</artifactId>
+                        <configuration>
+                            <mainClass>org.keycloak.testutils.KeycloakServer</mainClass>
+                        </configuration>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+        <profile>
+            <id>mail-server</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.codehaus.mojo</groupId>
+                        <artifactId>exec-maven-plugin</artifactId>
+                        <configuration>
+                            <mainClass>org.keycloak.testutils.MailServer</mainClass>
+                        </configuration>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+        <profile>
+            <id>totp</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.codehaus.mojo</groupId>
+                        <artifactId>exec-maven-plugin</artifactId>
+                        <configuration>
+                            <mainClass>org.keycloak.testutils.TotpGenerator</mainClass>
+                        </configuration>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+
+        <profile>
+            <id>jpa</id>
+
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-surefire-plugin</artifactId>
+                        <configuration>
+                            <systemPropertyVariables>
+                                <keycloak.realm.provider>jpa</keycloak.realm.provider>
+                                <keycloak.user.provider>jpa</keycloak.user.provider>
+                                <keycloak.eventStore.provider>jpa</keycloak.eventStore.provider>
+                                <keycloak.userSessions.provider>jpa</keycloak.userSessions.provider>
+                            </systemPropertyVariables>
+                        </configuration>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+
+        <profile>
+            <id>mongo</id>
+
+            <properties>
+                <keycloak.connectionsMongo.host>localhost</keycloak.connectionsMongo.host>
+                <keycloak.connectionsMongo.port>27018</keycloak.connectionsMongo.port>
+                <keycloak.connectionsMongo.db>keycloak</keycloak.connectionsMongo.db>
+                <keycloak.connectionsMongo.clearOnStartup>true</keycloak.connectionsMongo.clearOnStartup>
+                <keycloak.connectionsMongo.bindIp>127.0.0.1</keycloak.connectionsMongo.bindIp>
+            </properties>
+
+            <build>
+                <plugins>
+
+                    <!-- Postpone tests to "integration-test" phase, so that we can bootstrap embedded mongo on 27018 before running tests -->
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-surefire-plugin</artifactId>
+                        <executions>
+                            <execution>
+                                <id>test</id>
+                                <phase>integration-test</phase>
+                                <goals>
+                                    <goal>test</goal>
+                                </goals>
+                                <configuration>
+                                    <systemPropertyVariables>
+                                        <keycloak.realm.provider>mongo</keycloak.realm.provider>
+                                        <keycloak.user.provider>mongo</keycloak.user.provider>
+                                        <keycloak.audit.provider>mongo</keycloak.audit.provider>
+                                        <keycloak.userSessions.provider>mongo</keycloak.userSessions.provider>
+                                        <keycloak.connectionsMongo.host>${keycloak.connectionsMongo.host}</keycloak.connectionsMongo.host>
+                                        <keycloak.connectionsMongo.port>${keycloak.connectionsMongo.port}</keycloak.connectionsMongo.port>
+                                        <keycloak.connectionsMongo.db>${keycloak.connectionsMongo.db}</keycloak.connectionsMongo.db>
+                                        <keycloak.connectionsMongo.clearOnStartup>${keycloak.connectionsMongo.clearOnStartup}</keycloak.connectionsMongo.clearOnStartup>
+                                        <keycloak.connectionsMongo.bindIp>${keycloak.connectionsMongo.bindIp}</keycloak.connectionsMongo.bindIp>
+                                    </systemPropertyVariables>
+                                </configuration>
+                            </execution>
+                            <execution>
+                                <id>default-test</id>
+                                <configuration>
+                                    <skip>true</skip>
+                                </configuration>
+                            </execution>
+                        </executions>
+                    </plugin>
+
+                    <!-- Embedded mongo -->
+                    <plugin>
+                        <groupId>com.github.joelittlejohn.embedmongo</groupId>
+                        <artifactId>embedmongo-maven-plugin</artifactId>
+                        <executions>
+                            <execution>
+                                <id>start-mongodb</id>
+                                <phase>pre-integration-test</phase>
+                                <goals>
+                                    <goal>start</goal>
+                                </goals>
+                                <configuration>
+                                    <port>${keycloak.connectionsMongo.port}</port>
+                                    <logging>file</logging>
+                                    <logFile>${project.build.directory}/mongodb.log</logFile>
+                                    <bindIp>${keycloak.connectionsMongo.bindIp}</bindIp>
+                                </configuration>
+                            </execution>
+                            <execution>
+                                <id>stop-mongodb</id>
+                                <phase>post-integration-test</phase>
+                                <goals>
+                                    <goal>stop</goal>
+                                </goals>
+                            </execution>
+                        </executions>
+                    </plugin>
+                </plugins>
+            </build>
+
+        </profile>
+
+        <profile>
+            <id>infinispan</id>
+
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-surefire-plugin</artifactId>
+                        <configuration>
+                            <systemPropertyVariables>
+                                <keycloak.realm.cache.provider>infinispan</keycloak.realm.cache.provider>
+                                <keycloak.user.cache.provider>infinispan</keycloak.user.cache.provider>
+                                <keycloak.userSessions.provider>infinispan</keycloak.userSessions.provider>
+                            </systemPropertyVariables>
+                        </configuration>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+
+        <!-- MySQL -->
+        <profile>
+            <activation>
+                <property>
+                    <name>keycloak.connectionsJpa.driver</name>
+                    <value>com.mysql.jdbc.Driver</value>
+                </property>
+            </activation>
+            <id>mysql</id>
+            <dependencies>
+                <dependency>
+                    <groupId>mysql</groupId>
+                    <artifactId>mysql-connector-java</artifactId>
+                    <version>${mysql.version}</version>
+                </dependency>
+            </dependencies>
+        </profile>
+
+        <!-- PostgreSQL -->
+        <profile>
+            <activation>
+                <property>
+                    <name>keycloak.connectionsJpa.driver</name>
+                    <value>org.postgresql.Driver</value>
+                </property>
+            </activation>
+            <id>postgresql</id>
+            <dependencies>
+                <dependency>
+                    <groupId>org.postgresql</groupId>
+                    <artifactId>postgresql</artifactId>
+                    <version>${postgresql.version}</version>
+                </dependency>
+            </dependencies>
+        </profile>
+
+        <profile>
+            <id>clean-jpa</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.liquibase</groupId>
+                        <artifactId>liquibase-maven-plugin</artifactId>
+                        <configuration>
+                            <changeLogFile>META-INF/jpa-changelog-master.xml</changeLogFile>
+
+                            <url>${keycloak.connectionsJpa.url}</url>
+                            <driver>${keycloak.connectionsJpa.driver}</driver>
+                            <username>${keycloak.connectionsJpa.user}</username>
+                            <password>${keycloak.connectionsJpa.password}</password>
+
+                            <promptOnNonLocalDatabase>false</promptOnNonLocalDatabase>
+                        </configuration>
+                        <executions>
+                            <execution>
+                                <id>clean-jpa</id>
+                                <phase>clean</phase>
+                                <goals>
+                                    <goal>dropAll</goal>
+                                </goals>
+                            </execution>
+                        </executions>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+</project>
diff --git a/testsuite/tomcat7/src/test/java/org/keycloak/testsuite/tomcat7/TomcatAdapterTest.java b/testsuite/tomcat7/src/test/java/org/keycloak/testsuite/tomcat7/TomcatAdapterTest.java
new file mode 100755
index 0000000..7807233
--- /dev/null
+++ b/testsuite/tomcat7/src/test/java/org/keycloak/testsuite/tomcat7/TomcatAdapterTest.java
@@ -0,0 +1,24 @@
+package org.keycloak.testsuite.tomcat7;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.startup.Tomcat;
+
+import java.io.File;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class TomcatAdapterTest {
+    public void startServer() throws LifecycleException {
+        Tomcat tomcat = new Tomcat();
+        tomcat.setPort(8080);
+        //tomcat.addWebapp()
+        File base = new File(System.getProperty("java.io.tmpdir"));
+        Context rootCtx = tomcat.addContext("/app", base.getAbsolutePath());
+        //Tomcat.addServlet(rootCtx, "dateServlet", new DatePrintServlet());
+        rootCtx.addServletMapping("/date", "dateServlet");
+        tomcat.start();
+        tomcat.getServer().await();
+    }}
diff --git a/testsuite/tomcat7/src/test/java/org/keycloak/testsuite/Tomcat7Test.java b/testsuite/tomcat7/src/test/java/org/keycloak/testsuite/Tomcat7Test.java
new file mode 100755
index 0000000..71ecbae
--- /dev/null
+++ b/testsuite/tomcat7/src/test/java/org/keycloak/testsuite/Tomcat7Test.java
@@ -0,0 +1,283 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2012, Red Hat, Inc., and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.keycloak.testsuite;
+
+import org.apache.http.NameValuePair;
+import org.apache.http.client.CookieStore;
+import org.apache.http.client.utils.URLEncodedUtils;
+import org.apache.http.cookie.Cookie;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.jboss.resteasy.client.jaxrs.ResteasyClient;
+import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder;
+import org.jboss.resteasy.client.jaxrs.engines.ApacheHttpClient4Engine;
+import org.junit.Assert;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.keycloak.OAuth2Constants;
+import org.keycloak.adapters.HttpClientBuilder;
+import org.keycloak.protocol.oidc.OpenIDConnectService;
+import org.keycloak.services.resources.LoginActionsService;
+import org.keycloak.testsuite.Constants;
+import org.keycloak.testsuite.OAuthClient;
+import org.keycloak.testsuite.OAuthClient.AccessTokenResponse;
+import org.keycloak.testsuite.rule.KeycloakRule;
+import org.keycloak.testsuite.rule.WebRule;
+import org.keycloak.util.BasicAuthHelper;
+import org.openqa.selenium.WebDriver;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Form;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+import java.net.URI;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class Tomcat7Test {
+
+    @ClassRule
+    public static KeycloakRule keycloakRule = new KeycloakRule();
+
+    public static class BrowserLogin implements Runnable
+    {
+
+        private WebDriver driver;
+
+        public BrowserLogin() {
+            driver = WebRule.createWebDriver();
+        }
+
+        @Override
+        public void run() {
+            driver.manage().deleteAllCookies();
+            OAuthClient oauth = new OAuthClient(driver);
+            oauth.doLogin("test-user@localhost", "password");
+            String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
+            AccessTokenResponse response = oauth.doAccessTokenRequest(code, "password");
+            Assert.assertEquals(200, response.getStatusCode());
+            count.incrementAndGet();
+
+        }
+    }
+
+    public static AtomicLong count = new AtomicLong(0);
+
+    public static class JaxrsClientLogin implements Runnable
+    {
+        ResteasyClient client;
+
+        private String baseUrl = Constants.AUTH_SERVER_ROOT;
+
+        private String realm = "test";
+
+        private String responseType = OAuth2Constants.CODE;
+
+        private String grantType = "authorization_code";
+
+        private String clientId = "test-app";
+
+        private String redirectUri = "http://localhost:8081/app/auth";
+
+
+        public JaxrsClientLogin() {
+            DefaultHttpClient httpClient = (DefaultHttpClient) new HttpClientBuilder().build();
+            httpClient.setCookieStore(new CookieStore() {
+                @Override
+                public void addCookie(Cookie cookie) {
+                    //To change body of implemented methods use File | Settings | File Templates.
+                }
+
+                @Override
+                public List<Cookie> getCookies() {
+                    return Collections.emptyList();
+                }
+
+                @Override
+                public boolean clearExpired(Date date) {
+                    return false;  //To change body of implemented methods use File | Settings | File Templates.
+                }
+
+                @Override
+                public void clear() {
+                    //To change body of implemented methods use File | Settings | File Templates.
+                }
+            });
+            ApacheHttpClient4Engine engine = new ApacheHttpClient4Engine(httpClient);
+            this.client = new ResteasyClientBuilder().httpEngine(engine).build();
+        }
+
+        public String getLoginFormUrl(String state) {
+            UriBuilder b = OpenIDConnectService.loginPageUrl(UriBuilder.fromUri(baseUrl));
+            if (responseType != null) {
+                b.queryParam(OAuth2Constants.RESPONSE_TYPE, responseType);
+            }
+            if (clientId != null) {
+                b.queryParam(OAuth2Constants.CLIENT_ID, clientId);
+            }
+            if (redirectUri != null) {
+                b.queryParam(OAuth2Constants.REDIRECT_URI, redirectUri);
+            }
+            if (state != null) {
+                b.queryParam(OAuth2Constants.STATE, state);
+            }
+            return b.build(realm).toString();
+        }
+
+        public String getProcessLoginUrl(String state) {
+            UriBuilder b = LoginActionsService.processLoginUrl(UriBuilder.fromUri(baseUrl));
+            if (clientId != null) {
+                b.queryParam(OAuth2Constants.CLIENT_ID, clientId);
+            }
+            if (redirectUri != null) {
+                b.queryParam(OAuth2Constants.REDIRECT_URI, redirectUri);
+            }
+            if (state != null) {
+                b.queryParam(OAuth2Constants.STATE, state);
+            }
+            return b.build(realm).toString();
+        }
+
+        static Pattern actionParser = Pattern.compile("action=\"([^\"]+)\"");
+
+        public void run() {
+            //this.client = new ResteasyClientBuilder().build();
+            String state = "42";
+            String loginFormUrl = getLoginFormUrl(state);
+            String html = client.target(loginFormUrl).request().get(String.class);
+            Matcher matcher = actionParser.matcher(html);
+            matcher.find();
+            String actionUrl = matcher.group(1);
+            if (!actionUrl.startsWith("http")) {
+                actionUrl = UriBuilder.fromUri(actionUrl).scheme("http").host("localhost").port(8081).build().toString();
+            }
+            Form form = new Form();
+            form.param("username", "test-user@localhost");
+            form.param("password", "password");
+            Response response = client.target(actionUrl).request().post(Entity.form(form));
+            URI uri = null;
+            Assert.assertEquals(302, response.getStatus());
+            uri = response.getLocation();
+            for (String header : response.getHeaders().keySet()) {
+                for (Object value : response.getHeaders().get(header)) {
+                    System.out.println(header + ": " + value);
+                }
+            }
+            response.close();
+
+            Assert.assertNotNull(uri);
+            String code = getCode(uri);
+            Assert.assertNotNull(code);
+
+            form = new Form();
+            form.param(OAuth2Constants.GRANT_TYPE, grantType)
+                    .param(OAuth2Constants.CODE, code)
+                    .param(OAuth2Constants.REDIRECT_URI, redirectUri);
+
+            String authorization = BasicAuthHelper.createHeader(clientId, "password");
+
+            String res = client.target(OpenIDConnectService.accessCodeToTokenUrl(UriBuilder.fromUri(baseUrl)).build(realm)).request()
+                    .header(HttpHeaders.AUTHORIZATION, authorization)
+                    .post(Entity.form(form), String.class);
+            count.incrementAndGet();
+            //client.close();
+        }
+
+        public String getCode(URI uri) {
+            Map<String, String> m = new HashMap<String, String>();
+            List<NameValuePair> pairs = URLEncodedUtils.parse(uri, "UTF-8");
+            for (NameValuePair p : pairs) {
+                if (p.getName().equals("code")) return p.getValue();
+                m.put(p.getName(), p.getValue());
+            }
+            return null;
+        }
+
+
+        public void close()
+        {
+            client.close();
+        }
+    }
+
+    @Test
+    public void perfJaxrsClientLogin()
+    {
+        long ITERATIONS = 3;
+        JaxrsClientLogin login = new JaxrsClientLogin();
+        long start = System.currentTimeMillis();
+        for (int i = 0; i < ITERATIONS; i++) {
+            //System.out.println("*************************");
+            login.run();
+        }
+        long end = System.currentTimeMillis() - start;
+        System.out.println("took: " + end);
+    }
+
+    @Test
+    public void perfBrowserLogin()
+    {
+        long ITERATIONS = 3;
+        long start = System.currentTimeMillis();
+        BrowserLogin login = new BrowserLogin();
+        for (int i = 0; i < ITERATIONS; i++) {
+            //System.out.println("----------------------------------");
+            login.run();
+        }
+        long end = System.currentTimeMillis() - start;
+        System.out.println("took: " + end);
+    }
+
+    @Test
+    public void multiThread() throws Exception {
+        int num_threads = 20;
+        Thread[] threads = new Thread[num_threads];
+        for (int i = 0; i < num_threads; i++) {
+            threads[i] = new Thread(new Runnable() {
+                @Override
+                public void run() {
+                    perfJaxrsClientLogin();
+                }
+            });
+        }
+        long start = System.currentTimeMillis();
+        for (int i = 0; i < num_threads; i++) {
+            threads[i].start();
+        }
+        for (int i = 0; i < num_threads; i++) {
+            threads[i].join();
+        }
+        long end = System.currentTimeMillis() - start;
+        System.out.println(count.toString() + " took: " + end);
+        System.out.println(count.floatValue() / ((float)end) * 1000+ " logins/s");
+    }
+
+}