keycloak-uncached

upload keys

10/21/2014 6:33:17 PM

Details

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 a334849..1252b49 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,7 +43,7 @@ module.controller('ApplicationCredentialsCtrl', function($scope, $location, real
     });
 });
 
-module.controller('ApplicationCertificateCtrl', function($scope, $location, $http, realm, application,
+module.controller('ApplicationCertificateCtrl', function($scope, $location, $http, $upload, realm, application,
                                                          ApplicationCertificate, ApplicationCertificateGenerate,
                                                          ApplicationCertificateDownload, Notifications) {
     $scope.realm = realm;
@@ -53,7 +53,59 @@ module.controller('ApplicationCertificateCtrl', function($scope, $location, $htt
         realmAlias: realm.realm
     };
 
+    $scope.files = [];
+
+    $scope.onFileSelect = function($files) {
+        $scope.files = $files;
+    };
+
+    $scope.clearFileSelect = function() {
+        $scope.files = null;
+    }
+
+    $scope.keyFormats = [
+        "JKS",
+        "PKCS12"
+    ];
+
     $scope.jks = jks;
+    $scope.jks.format = $scope.keyFormats[0];
+    $scope.uploadKeyFormat = $scope.keyFormats[0];
+
+    $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 + '/applications-by-id/' + application.id + '/certificates/upload/jks',
+                // method: POST or PUT,
+                // headers: {'headerKey': 'headerValue'}, withCredential: true,
+                data: {keystoreFormat: $scope.uploadKeyFormat,
+                       keyAlias: $scope.uploadKeyAlias,
+                       keyPassword: $scope.uploadKeyPassword,
+                       storePassword: $scope.uploadStorePassword
+                },
+                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) {
+                $scope.keyInfo = data;
+                Notifications.success("Keystore uploaded successfully.");
+            })
+                .error(function() {
+                    Notifications.error("The key store can not be uploaded. Please verify the file.");
+
+                });
+            //.then(success, error, progress);
+        }
+    };
+
+
+
 
     var keyInfo = ApplicationCertificate.get({ realm : realm.realm, application : application.id },
         function() {
@@ -87,7 +139,9 @@ module.controller('ApplicationCertificateCtrl', function($scope, $location, $htt
             var blob = new Blob([data], {
                 type: 'application/octet-stream'
             });
-            saveAs(blob, 'keystore' + '.jks');
+            var ext = ".jks";
+            if ($scope.jks.format == 'PKCS12') ext = ".p12";
+            saveAs(blob, 'keystore' + ext);
         }).error(function(){
             Notifications.error("Error downloading.");
         });
@@ -338,7 +392,7 @@ module.controller('ApplicationDetailCtrl', function($scope, realm, application, 
         $scope.application.redirectUris = [];
         $scope.accessType = $scope.accessTypes[0];
         $scope.protocol = $scope.protocols[0];
-        $scope.signatureAlgorithm = $scope.algorithms[1];
+        $scope.signatureAlgorithm = $scope.signatureAlgorithms[1];
     }
 
     if ($scope.application.attributes["saml.server.signature"]) {
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/application-detail.html b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/application-detail.html
index 6b71719..60d7ae5 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/application-detail.html
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/application-detail.html
@@ -63,7 +63,7 @@
                     </div>
                     <span tooltip-placement="right" tooltip="Should SAML documents be signed by the realm?" class="fa fa-info-circle"></span>
                 </div>
-                <div class="form-group">
+                <div class="form-group" data-ng-show="samlServerSignature && protocol == 'saml'">
                     <label class="col-sm-2 control-label" for="protocol">Signature Algorithm</label>
                     <div class="col-sm-6">
                         <div class="select-kc">
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/application-keys.html b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/application-keys.html
index c722e52..b8b232d 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/application-keys.html
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/application-keys.html
@@ -9,31 +9,99 @@
         </ol>
         <h2><span>{{application.name}}</span> Key Pair and Certificate <span tooltip-placement="right" tooltip="Application's key pair and certificate.  Used for more confidential interaction between application and auth server." class="fa fa-info-circle"></span></h2>
         <form class="form-horizontal" name="keyForm" novalidate kc-read-only="!access.manageRealm">
+            <fieldset>
+                <legend collapsed><span class="text">Import Keys and Cert</span> <span tooltip-placement="right" tooltip="Upload the client's key pair and cert." class="fa fa-info-circle"></span></legend>
+                <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 private key and certificate." class="fa fa-info-circle"></span>
+                </div>
+                <div class="form-group">
+                    <label class="col-sm-2 control-label" for="keyPassword">Key Password</label>
+                    <div class="col-sm-4">
+                        <input class="form-control" type="password" id="uploadKeyPassword" name="uploadKeyPassword" data-ng-model="uploadKeyPassword" 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="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">Upload Keys </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">Upload</button>
+                </div>
+            </fieldset>
             <fieldset class="form-group col-sm-10" data-ng-hide="!keyInfo.privateKey">
-                <legend collapsed><span class="text">Java Keystore Download</span>  <span tooltip-placement="right" tooltip="Client key pair, cert, and realm certificate will be stuffed into a Java keystore that you can use in your applications." class="fa fa-info-circle"></span></legend>
+                <legend collapsed><span class="text">Download Keys and Cert</span>  <span tooltip-placement="right" tooltip="Client key pair, cert, and realm certificate will be stuffed into a PKCS12 or Java keystore that you can use in your applications." class="fa fa-info-circle"></span></legend>
+                <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">
                     <label class="col-sm-2 control-label" for="keyPassword">Key Password</label>
                     <div class="col-sm-4">
-                        <input class="form-control" type="text" id="keyPassword" name="keyPassword" data-ng-model="jks.keyPassword" autofocus required>
+                        <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="text" id="storePassword" name="storePassword" data-ng-model="jks.storePassword" autofocus required>
+                        <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">
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SAML2BindingBuilder.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SAML2BindingBuilder.java
index ab0686c..74408f6 100755
--- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SAML2BindingBuilder.java
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SAML2BindingBuilder.java
@@ -158,7 +158,7 @@ public class SAML2BindingBuilder<T extends SAML2BindingBuilder> {
             QName encryptedAssertionElementQName = new QName(JBossSAMLURIConstants.ASSERTION_NSURI.get(),
                     JBossSAMLConstants.ENCRYPTED_ASSERTION.get(), samlNSPrefix);
 
-            byte[] secret = WSTrustUtil.createRandomSecret(128 / 8);
+            byte[] secret = WSTrustUtil.createRandomSecret(encryptionKeySize / 8);
             SecretKey secretKey = new SecretKeySpec(secret, encryptionAlgorithm);
 
             // encrypt the Assertion element and replace it with a EncryptedAssertion element.
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ClientCertificateResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ClientCertificateResource.java
index 84aac7e..068ee09 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/ClientCertificateResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/ClientCertificateResource.java
@@ -1,13 +1,22 @@
 package org.keycloak.services.resources.admin;
 
 import org.jboss.resteasy.annotations.cache.NoCache;
+import org.jboss.resteasy.plugins.providers.multipart.InputPart;
+import org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataInput;
 import org.jboss.resteasy.spi.BadRequestException;
 import org.jboss.resteasy.spi.NotAcceptableException;
 import org.jboss.resteasy.spi.NotFoundException;
+import org.keycloak.models.AdminRoles;
 import org.keycloak.models.ClientModel;
 import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.ModelDuplicateException;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.services.ForbiddenException;
+import org.keycloak.services.managers.RealmManager;
+import org.keycloak.services.resources.flows.Flows;
+import org.keycloak.util.JsonSerialization;
 import org.keycloak.util.PemUtils;
 
 import javax.ws.rs.Consumes;
@@ -16,17 +25,29 @@ import javax.ws.rs.POST;
 import javax.ws.rs.Path;
 import javax.ws.rs.Produces;
 import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.Context;
 import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
 import javax.ws.rs.core.StreamingOutput;
+import javax.ws.rs.core.UriInfo;
 import java.io.ByteArrayOutputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.io.InputStream;
 import java.io.OutputStream;
+import java.net.URI;
 import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
 import java.security.PrivateKey;
 import java.security.PublicKey;
+import java.security.UnrecoverableKeyException;
 import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
 import java.security.cert.X509Certificate;
+import java.util.List;
+import java.util.Map;
 
 /**
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -102,6 +123,49 @@ public class ClientCertificateResource {
         return info;
     }
 
+    @POST
+    @Path("upload/jks")
+    @Consumes(MediaType.MULTIPART_FORM_DATA)
+    @Produces(MediaType.APPLICATION_JSON)
+    public ClientKeyPairInfo uploadJks(@Context final UriInfo uriInfo, MultipartFormDataInput input) throws IOException {
+        auth.requireManage();
+        Map<String, List<InputPart>> uploadForm = input.getFormDataMap();
+        List<InputPart> inputParts = uploadForm.get("file");
+
+        String keystoreFormat = uploadForm.get("keystoreFormat").get(0).getBodyAsString();
+        String keyAlias = uploadForm.get("keyAlias").get(0).getBodyAsString();
+        String keyPassword = uploadForm.get("keyPassword").get(0).getBodyAsString();
+        String storePassword = uploadForm.get("storePassword").get(0).getBodyAsString();
+        System.out.println("format = '" + keystoreFormat + "'");
+        PrivateKey privateKey = null;
+        X509Certificate certificate = null;
+        try {
+            KeyStore keyStore = null;
+            if (keystoreFormat.equals("JKS")) keyStore = KeyStore.getInstance("JKS");
+            else keyStore = KeyStore.getInstance(keystoreFormat, "BC");
+            keyStore.load(inputParts.get(0).getBody(InputStream.class, null), storePassword.toCharArray());
+            privateKey = (PrivateKey)keyStore.getKey(keyAlias, keyPassword.toCharArray());
+            certificate = (X509Certificate)keyStore.getCertificate(keyAlias);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+        String privateKeyPem = KeycloakModelUtils.getPemFromKey(privateKey);
+        String publicKeyPem = KeycloakModelUtils.getPemFromKey(certificate.getPublicKey());
+        String certPem = KeycloakModelUtils.getPemFromCertificate(certificate);
+        client.setAttribute(ClientModel.PRIVATE_KEY, privateKeyPem);
+        client.setAttribute(ClientModel.PUBLIC_KEY, publicKeyPem);
+        client.setAttribute(ClientModel.X509CERTIFICATE, certPem);
+
+        ClientKeyPairInfo info = new ClientKeyPairInfo();
+        info.setCertificate(client.getAttribute(ClientModel.X509CERTIFICATE));
+        info.setPrivateKey(client.getAttribute(ClientModel.PRIVATE_KEY));
+        info.setPublicKey(client.getAttribute(ClientModel.PUBLIC_KEY));
+
+
+        return info;
+    }
+
+
     public static class KeyStoreConfig {
         protected Boolean realmCertificate;
         protected String storePassword;
@@ -164,11 +228,12 @@ public class ClientCertificateResource {
     @Path("/download")
     @Produces(MediaType.APPLICATION_OCTET_STREAM)
     @Consumes(MediaType.APPLICATION_JSON)
-    public byte[] getJavaKeyStore(final KeyStoreConfig config) {
+    public byte[] getKeystore(final KeyStoreConfig config) {
         auth.requireView();
-        if (config.getFormat() != null && !config.getFormat().equals("jks")) {
+        if (config.getFormat() != null && !config.getFormat().equals("JKS") && !config.getFormat().equals("PKCS12")) {
             throw new NotAcceptableException("Only support jks format.");
         }
+        String format = config.getFormat();
         if (client.getAttribute(ClientModel.PRIVATE_KEY) == null) {
             throw new NotFoundException("keypair not generated for client");
         }
@@ -180,7 +245,8 @@ public class ClientCertificateResource {
         }
         final KeyStore keyStore;
         try {
-            keyStore = KeyStore.getInstance("JKS");
+            if (format.equals("JKS")) keyStore = KeyStore.getInstance("JKS");
+            else keyStore = KeyStore.getInstance(format, "BC");
             keyStore.load(null, null);
             String keyAlias = config.getKeyAlias();
             if (keyAlias == null) keyAlias = client.getClientId();