From 668497df4d0b23771c64ae502bcd4d2b5ca6844c Mon Sep 17 00:00:00 2001 From: Bill Burke Date: Tue, 21 Oct 2014 16:33:17 -0400 Subject: [PATCH] upload keys --- .../resources/js/controllers/applications.js | 60 ++++++++++++++- .../partials/application-detail.html | 2 +- .../resources/partials/application-keys.html | 74 ++++++++++++++++++- .../protocol/saml/SAML2BindingBuilder.java | 2 +- .../admin/ClientCertificateResource.java | 72 +++++++++++++++++- 5 files changed, 199 insertions(+), 11 deletions(-) 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 a334849bfa..1252b49187 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 6b7171956c..60d7ae5a22 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 @@ -
+
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 c722e52bda..b8b232dd5c 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 @@

{{application.name}} Key Pair and Certificate

+
+ Import Keys and Cert +
+ +
+
+ +
+
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ + + {{files[0].name}} + +
+
+
+ + +
+
- Java Keystore Download + Download Keys and Cert +
+ +
+
+ +
+
+ +
+
- +
+
+
- +
+
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 ab0686ce7a..74408f65ae 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 { 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 84aac7ebb7..068ee09558 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 Bill Burke @@ -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> uploadForm = input.getFormDataMap(); + List 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();