keycloak-uncached
Changes
forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/applications.js 60(+57 -3)
forms/common-themes/src/main/resources/theme/admin/base/resources/partials/application-detail.html 2(+1 -1)
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();