saml encryption
This commit is contained in:
parent
638ce06f77
commit
3e5afcde9e
8 changed files with 140 additions and 28 deletions
|
@ -299,7 +299,7 @@ module.controller('ApplicationDetailCtrl', function($scope, realm, application,
|
|||
$scope.create = !application.name;
|
||||
$scope.samlServerSignature = false;
|
||||
$scope.samlClientSignature = false;
|
||||
$scope.samlServerEncrypt = false;
|
||||
$scope.samlEncrypt = false;
|
||||
if (!$scope.create) {
|
||||
if (!application.attributes) {
|
||||
application.attributes = {};
|
||||
|
@ -335,9 +335,9 @@ module.controller('ApplicationDetailCtrl', function($scope, realm, application,
|
|||
$scope.samlClientSignature = true;
|
||||
}
|
||||
}
|
||||
if ($scope.application.attributes["samlServerEncrypt"]) {
|
||||
if ($scope.application.attributes["samlServerEncrypt"] == "true") {
|
||||
$scope.samlServerEncrypt = true;
|
||||
if ($scope.application.attributes["samlEncrypt"]) {
|
||||
if ($scope.application.attributes["samlEncrypt"] == "true") {
|
||||
$scope.samlEncrypt = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -406,10 +406,10 @@ module.controller('ApplicationDetailCtrl', function($scope, realm, application,
|
|||
$scope.application.attributes["samlClientSignature"] = "false";
|
||||
|
||||
}
|
||||
if ($scope.samlServerEncrypt == true) {
|
||||
$scope.application.attributes["samlServerEncrypt"] = "true";
|
||||
if ($scope.samlEncrypt == true) {
|
||||
$scope.application.attributes["samlEncrypt"] = "true";
|
||||
} else {
|
||||
$scope.application.attributes["samlServerEncrypt"] = "false";
|
||||
$scope.application.attributes["samlEncrypt"] = "false";
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -57,18 +57,18 @@
|
|||
<span tooltip-placement="right" tooltip="'Confidential' applications require a secret to initiate login protocol. 'Public' clients do not require a secret. 'Bearer-only' applications are web services that never initiate a login." class="fa fa-info-circle"></span>
|
||||
</div>
|
||||
<div class="form-group clearfix block" data-ng-show="protocol == 'saml'">
|
||||
<label class="col-sm-2 control-label" for="samlServerSignature">Server Signatures</label>
|
||||
<label class="col-sm-2 control-label" for="samlServerSignature">Sign SAML Documents</label>
|
||||
<div class="col-sm-6">
|
||||
<input ng-model="samlServerSignature" ng-click="switchChange()" name="samlServerSignature" id="samlServerSignature" onoffswitch />
|
||||
</div>
|
||||
<span tooltip-placement="right" tooltip="Should server sent SAML requests and responses be signed by the realm?" class="fa fa-info-circle"></span>
|
||||
<span tooltip-placement="right" tooltip="Should SAML documents be signed by the realm?" class="fa fa-info-circle"></span>
|
||||
</div>
|
||||
<div class="form-group clearfix block" data-ng-show="protocol == 'saml'">
|
||||
<label class="col-sm-2 control-label" for="samlServerEncrypt">Server Encryption</label>
|
||||
<label class="col-sm-2 control-label" for="samlEncrypt">Encrypt SAML Documents</label>
|
||||
<div class="col-sm-6">
|
||||
<input ng-model="samlServerEncrypt" ng-click="switchChange()" name="samlServerEncrypt" id="samlServerEncrypt" onoffswitch />
|
||||
<input ng-model="samlEncrypt" ng-click="switchChange()" name="samlEncrypt" id="samlEncrypt" onoffswitch />
|
||||
</div>
|
||||
<span tooltip-placement="right" tooltip="Should server sent SAML requests and responses be encrypted by the realm?" class="fa fa-info-circle"></span>
|
||||
<span tooltip-placement="right" tooltip="Should SAML asserts be encrypted with client's public key?" class="fa fa-info-circle"></span>
|
||||
</div>
|
||||
<div class="form-group clearfix block" data-ng-show="protocol == 'saml'">
|
||||
<label class="col-sm-2 control-label" for="samlClientSignature">Client Signatures</label>
|
||||
|
|
|
@ -174,9 +174,8 @@ public class SALM2PostBindingLoginResponseBuilder extends SAML2PostBindingBuilde
|
|||
throw logger.samlAssertionMarshallError(e);
|
||||
}
|
||||
|
||||
if (signed) {
|
||||
signDocument(samlResponseDocument);
|
||||
}
|
||||
encryptAndSign(samlResponseDocument);
|
||||
|
||||
return samlResponseDocument;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,15 +1,23 @@
|
|||
package org.keycloak.protocol.saml;
|
||||
|
||||
import org.picketlink.common.constants.GeneralConstants;
|
||||
import org.picketlink.common.constants.JBossSAMLConstants;
|
||||
import org.picketlink.common.constants.JBossSAMLURIConstants;
|
||||
import org.picketlink.common.exceptions.ConfigurationException;
|
||||
import org.picketlink.common.exceptions.ProcessingException;
|
||||
import org.picketlink.common.util.DocumentUtil;
|
||||
import org.picketlink.identity.federation.core.util.XMLEncryptionUtil;
|
||||
import org.picketlink.identity.federation.core.wstrust.WSTrustUtil;
|
||||
import org.picketlink.identity.federation.web.util.PostBindingUtil;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Node;
|
||||
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import javax.ws.rs.core.CacheControl;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.xml.namespace.QName;
|
||||
import java.io.IOException;
|
||||
import java.security.KeyPair;
|
||||
import java.security.PrivateKey;
|
||||
|
@ -31,6 +39,10 @@ public class SAML2PostBindingBuilder<T extends SAML2PostBindingBuilder> {
|
|||
protected String relayState;
|
||||
protected String destination;
|
||||
protected String responseIssuer;
|
||||
protected int encryptionKeySize = 128;
|
||||
protected PublicKey encryptionPublicKey;
|
||||
protected String encryptionAlgorithm = "AES";
|
||||
protected boolean encrypt;
|
||||
|
||||
public T sign(KeyPair keyPair) {
|
||||
this.signingKeyPair = keyPair;
|
||||
|
@ -51,6 +63,29 @@ public class SAML2PostBindingBuilder<T extends SAML2PostBindingBuilder> {
|
|||
return (T)this;
|
||||
}
|
||||
|
||||
public T sign(PrivateKey privateKey, PublicKey publicKey, X509Certificate cert) {
|
||||
this.signingKeyPair = new KeyPair(publicKey, privateKey);
|
||||
this.signingCertificate = cert;
|
||||
this.signed = true;
|
||||
return (T)this;
|
||||
}
|
||||
|
||||
public T encrypt(PublicKey publicKey) {
|
||||
encrypt = true;
|
||||
encryptionPublicKey = publicKey;
|
||||
return (T)this;
|
||||
}
|
||||
|
||||
public T encryptionAlgorithm(String alg) {
|
||||
this.encryptionAlgorithm = alg;
|
||||
return (T)this;
|
||||
}
|
||||
|
||||
public T encryptionKeySize(int size) {
|
||||
this.encryptionKeySize = size;
|
||||
return (T)this;
|
||||
}
|
||||
|
||||
public T signatureDigestMethod(String method) {
|
||||
this.signatureDigestMethod = method;
|
||||
return (T)this;
|
||||
|
@ -61,13 +96,6 @@ public class SAML2PostBindingBuilder<T extends SAML2PostBindingBuilder> {
|
|||
return (T)this;
|
||||
}
|
||||
|
||||
public T sign(PrivateKey privateKey, PublicKey publicKey, X509Certificate cert) {
|
||||
this.signingKeyPair = new KeyPair(publicKey, privateKey);
|
||||
this.signingCertificate = cert;
|
||||
this.signed = true;
|
||||
return (T)this;
|
||||
}
|
||||
|
||||
public T destination(String destination) {
|
||||
this.destination = destination;
|
||||
return (T)this;
|
||||
|
@ -83,7 +111,48 @@ public class SAML2PostBindingBuilder<T extends SAML2PostBindingBuilder> {
|
|||
return (T)this;
|
||||
}
|
||||
|
||||
private String getSAMLNSPrefix(Document samlResponseDocument) {
|
||||
Node assertionElement = samlResponseDocument.getDocumentElement()
|
||||
.getElementsByTagNameNS(JBossSAMLURIConstants.ASSERTION_NSURI.get(), JBossSAMLConstants.ASSERTION.get()).item(0);
|
||||
|
||||
if (assertionElement == null) {
|
||||
throw new IllegalStateException("Unable to find assertion in saml response document");
|
||||
}
|
||||
|
||||
return assertionElement.getPrefix();
|
||||
}
|
||||
|
||||
protected void encryptDocument(Document samlDocument) throws ProcessingException {
|
||||
String samlNSPrefix = getSAMLNSPrefix(samlDocument);
|
||||
|
||||
try {
|
||||
QName encryptedAssertionElementQName = new QName(JBossSAMLURIConstants.ASSERTION_NSURI.get(),
|
||||
JBossSAMLConstants.ENCRYPTED_ASSERTION.get(), samlNSPrefix);
|
||||
|
||||
byte[] secret = WSTrustUtil.createRandomSecret(128 / 8);
|
||||
SecretKey secretKey = new SecretKeySpec(secret, encryptionAlgorithm);
|
||||
|
||||
// encrypt the Assertion element and replace it with a EncryptedAssertion element.
|
||||
XMLEncryptionUtil.encryptElement(new QName(JBossSAMLURIConstants.ASSERTION_NSURI.get(),
|
||||
JBossSAMLConstants.ASSERTION.get(), samlNSPrefix), samlDocument, encryptionPublicKey,
|
||||
secretKey, encryptionKeySize, encryptedAssertionElementQName, true);
|
||||
} catch (Exception e) {
|
||||
throw new ProcessingException("failed to encrypt", e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected void encryptAndSign(Document samlDocument) throws ProcessingException {
|
||||
if (encrypt) {
|
||||
encryptDocument(samlDocument);
|
||||
signDocument(samlDocument);
|
||||
return;
|
||||
}
|
||||
if (signed) {
|
||||
signDocument(samlDocument);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
protected void signDocument(Document samlDocument) throws ProcessingException {
|
||||
SamlProtocolUtils.signDocument(samlDocument, signingKeyPair, signatureMethod, signatureDigestMethod, signingCertificate);
|
||||
|
|
|
@ -45,9 +45,7 @@ public class SAML2PostBindingErrorResponseBuilder extends SAML2PostBindingBuilde
|
|||
responseType.setStatus(JBossSAMLAuthnResponseFactory.createStatusTypeForResponder(status));
|
||||
responseType.setDestination(destination);
|
||||
|
||||
if (signed) {
|
||||
signDocument(samlResponse);
|
||||
}
|
||||
encryptAndSign(samlResponse);
|
||||
return samlResponse;
|
||||
}
|
||||
|
||||
|
|
|
@ -40,9 +40,7 @@ public class SAML2PostBindingLogoutResponseBuilder extends SAML2PostBindingBuild
|
|||
public String buildRequestString() {
|
||||
try {
|
||||
Document logoutRequestDocument = new SAML2Request().convert(createLogoutRequest());
|
||||
if (signed) {
|
||||
signDocument(logoutRequestDocument);
|
||||
}
|
||||
encryptAndSign(logoutRequestDocument);
|
||||
byte[] responseBytes = DocumentUtil.getDocumentAsString(logoutRequestDocument).getBytes("UTF-8");
|
||||
return PostBindingUtil.base64Encode(new String(responseBytes));
|
||||
} catch (Exception e) {
|
||||
|
|
|
@ -18,6 +18,7 @@ import org.keycloak.services.managers.ClientSessionCode;
|
|||
import org.keycloak.services.managers.ResourceAdminManager;
|
||||
import org.keycloak.services.resources.RealmsResource;
|
||||
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;
|
||||
|
@ -26,6 +27,7 @@ import org.picketlink.identity.federation.web.handlers.saml2.SAML2LogOutHandler;
|
|||
import javax.ws.rs.core.HttpHeaders;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
import java.security.PublicKey;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
|
@ -125,6 +127,16 @@ public class SalmProtocol implements LoginProtocol {
|
|||
if (requiresRealmSignature(client)) {
|
||||
builder.sign(realm.getPrivateKey(), realm.getPublicKey());
|
||||
}
|
||||
if (requiresEncryption(client)) {
|
||||
PublicKey publicKey = null;
|
||||
try {
|
||||
publicKey = PemUtils.decodePublicKey(client.getAttribute(ClientModel.PUBLIC_KEY));
|
||||
} catch (Exception e) {
|
||||
logger.error("failed", e);
|
||||
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Failed to process response");
|
||||
}
|
||||
builder.encrypt(publicKey);
|
||||
}
|
||||
try {
|
||||
return builder.buildLoginResponse();
|
||||
} catch (Exception e) {
|
||||
|
@ -137,6 +149,10 @@ public class SalmProtocol implements LoginProtocol {
|
|||
return "true".equals(client.getAttribute("samlServerSignature"));
|
||||
}
|
||||
|
||||
private boolean requiresEncryption(ClientModel client) {
|
||||
return "true".equals(client.getAttribute("samlEncrypt"));
|
||||
}
|
||||
|
||||
public void initClaims(SALM2PostBindingLoginResponseBuilder builder, ClientModel model, UserModel user) {
|
||||
if (ClaimMask.hasEmail(model.getAllowedClaimsMask())) {
|
||||
builder.attribute(X500SAMLProfileConstants.EMAIL_ADDRESS.getFriendlyName(), user.getEmail());
|
||||
|
@ -166,6 +182,19 @@ public class SalmProtocol implements LoginProtocol {
|
|||
if (requiresRealmSignature(client)) {
|
||||
logoutBuilder.sign(realm.getPrivateKey(), realm.getPublicKey());
|
||||
}
|
||||
/*
|
||||
if (requiresEncryption(client)) {
|
||||
PublicKey publicKey = null;
|
||||
try {
|
||||
publicKey = PemUtils.decodePublicKey(client.getAttribute(ClientModel.PUBLIC_KEY));
|
||||
} catch (Exception e) {
|
||||
logger.error("failed", e);
|
||||
return;
|
||||
}
|
||||
logoutBuilder.encrypt(publicKey);
|
||||
}
|
||||
*/
|
||||
|
||||
String logoutRequestString = null;
|
||||
try {
|
||||
logoutRequestString = logoutBuilder.buildRequestString();
|
||||
|
|
|
@ -56,6 +56,25 @@
|
|||
"publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDVG8a7xGN6ZIkDbeecySygcDfsypjUMNPE4QJjis8B316CvsZQ0hcTTLUyiRpHlHZys2k3xEhHBHymFC1AONcvzZzpb40tAhLHO1qtAnut00khjAdjR3muLVdGkM/zMC7G5s9iIwBVhwOQhy+VsGnCH91EzkjZ4SVEr55KJoyQJQIDAQAB",
|
||||
"X509Certificate": "MIIB1DCCAT0CBgFJGP5dZDANBgkqhkiG9w0BAQsFADAwMS4wLAYDVQQDEyVodHRwOi8vbG9jYWxob3N0OjgwODAvc2FsZXMtcG9zdC1zaWcvMB4XDTE0MTAxNjEyNDQyM1oXDTI0MTAxNjEyNDYwM1owMDEuMCwGA1UEAxMlaHR0cDovL2xvY2FsaG9zdDo4MDgwL3NhbGVzLXBvc3Qtc2lnLzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1RvGu8RjemSJA23nnMksoHA37MqY1DDTxOECY4rPAd9egr7GUNIXE0y1MokaR5R2crNpN8RIRwR8phQtQDjXL82c6W+NLQISxztarQJ7rdNJIYwHY0d5ri1XRpDP8zAuxubPYiMAVYcDkIcvlbBpwh/dRM5I2eElRK+eSiaMkCUCAwEAATANBgkqhkiG9w0BAQsFAAOBgQCLms6htnPaY69k1ntm9a5jgwSn/K61cdai8R8B0ccY7zvinn9AfRD7fiROQpFyY29wKn8WCLrJ86NBXfgFUGyR5nLNHVy3FghE36N2oHy53uichieMxffE6vhkKJ4P8ChfJMMOZlmCPsQPDvjoAghHt4mriFiQgRdPgIy/zDjSNw=="
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "http://localhost:8080/sales-post-enc/",
|
||||
"enabled": true,
|
||||
"protocol": "saml",
|
||||
"fullScopeAllowed": true,
|
||||
"baseUrl": "http://localhost:8080/sales-post-enc",
|
||||
"adminUrl": "http://localhost:8080/sales-post-enc",
|
||||
"redirectUris": [
|
||||
"http://localhost:8080/sales-post-enc/*"
|
||||
],
|
||||
"attributes": {
|
||||
"samlServerSignature": "true",
|
||||
"samlClientSignature": "true",
|
||||
"samlEncrypt": "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=="
|
||||
}
|
||||
}
|
||||
],
|
||||
"roles" : {
|
||||
|
|
Loading…
Reference in a new issue