upload keys
This commit is contained in:
parent
d3375962ad
commit
668497df4d
5 changed files with 199 additions and 11 deletions
|
@ -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"]) {
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Reference in a new issue