Repository: cxf-fediz Updated Branches: refs/heads/master 10ca19558 -> d1c0d7e23
Separate signing keys from validation keys for SAML SSO Project: http://git-wip-us.apache.org/repos/asf/cxf-fediz/repo Commit: http://git-wip-us.apache.org/repos/asf/cxf-fediz/commit/bb6d06d6 Tree: http://git-wip-us.apache.org/repos/asf/cxf-fediz/tree/bb6d06d6 Diff: http://git-wip-us.apache.org/repos/asf/cxf-fediz/diff/bb6d06d6 Branch: refs/heads/master Commit: bb6d06d63218fbc59e182eb3cb804eff1b7ab397 Parents: 10ca195 Author: Colm O hEigeartaigh <[email protected]> Authored: Fri Mar 25 10:43:38 2016 +0000 Committer: Colm O hEigeartaigh <[email protected]> Committed: Fri Mar 25 10:43:38 2016 +0000 ---------------------------------------------------------------------- .../idp/beans/samlsso/AuthnRequestParser.java | 15 +- .../beans/samlsso/AuthnRequestValidator.java | 232 +++++++++++++++++++ .../fediz/service/idp/domain/Application.java | 14 +- .../idp/samlsso/AuthnRequestValidator.java | 211 ----------------- .../idp/service/jpa/ApplicationDAOJPAImpl.java | 2 + .../idp/service/jpa/ApplicationEntity.java | 10 + .../WEB-INF/flows/saml-validate-request.xml | 10 +- .../apache/cxf/fediz/systests/idp/IdpTest.java | 2 +- systests/samlsso/src/test/resources/realma.cert | 15 ++ .../test/resources/realma/entities-realma.xml | 1 + 10 files changed, 283 insertions(+), 229 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/bb6d06d6/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/AuthnRequestParser.java ---------------------------------------------------------------------- diff --git a/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/AuthnRequestParser.java b/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/AuthnRequestParser.java index 410e8c1..565de41 100644 --- a/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/AuthnRequestParser.java +++ b/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/AuthnRequestParser.java @@ -25,10 +25,8 @@ import org.w3c.dom.Document; import org.apache.cxf.common.util.Base64Utility; import org.apache.cxf.fediz.core.exception.ProcessingException; -import org.apache.cxf.fediz.core.exception.ProcessingException.TYPE; import org.apache.cxf.fediz.service.idp.IdpConstants; import org.apache.cxf.fediz.service.idp.domain.Idp; -import org.apache.cxf.fediz.service.idp.samlsso.AuthnRequestValidator; import org.apache.cxf.fediz.service.idp.util.WebUtils; import org.apache.cxf.rs.security.saml.DeflateEncoderDecoder; import org.apache.cxf.staxutils.StaxUtils; @@ -48,8 +46,7 @@ public class AuthnRequestParser { private static final Logger LOG = LoggerFactory.getLogger(AuthnRequestParser.class); - public void parseSAMLRequest(RequestContext context, Idp idp, String signature, - String relayState, String samlRequest) throws ProcessingException { + public void parseSAMLRequest(RequestContext context, Idp idp, String samlRequest) throws ProcessingException { LOG.debug("Received SAML Request: {}", samlRequest); AuthnRequest parsedRequest = null; @@ -64,16 +61,6 @@ public class AuthnRequestParser { LOG.warn("Error parsing request: {}", ex.getMessage()); } } - - if (parsedRequest != null) { - try { - AuthnRequestValidator validator = new AuthnRequestValidator(); - validator.validateAuthnRequest(context, parsedRequest, idp, signature, relayState, samlRequest); - } catch (Exception ex) { - LOG.warn("Error validating request {}", ex.getMessage(), ex); - throw new ProcessingException(TYPE.BAD_REQUEST); - } - } } public String retrieveRealm(RequestContext context) { http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/bb6d06d6/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/AuthnRequestValidator.java ---------------------------------------------------------------------- diff --git a/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/AuthnRequestValidator.java b/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/AuthnRequestValidator.java new file mode 100644 index 0000000..26088d6 --- /dev/null +++ b/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/AuthnRequestValidator.java @@ -0,0 +1,232 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.cxf.fediz.service.idp.beans.samlsso; + +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.security.cert.X509Certificate; +import java.util.Collections; + +import org.w3c.dom.Document; + +import org.apache.cxf.fediz.core.exception.ProcessingException; +import org.apache.cxf.fediz.core.exception.ProcessingException.TYPE; +import org.apache.cxf.fediz.core.util.CertsUtils; +import org.apache.cxf.fediz.service.idp.IdpConstants; +import org.apache.cxf.fediz.service.idp.domain.Application; +import org.apache.cxf.fediz.service.idp.domain.Idp; +import org.apache.cxf.fediz.service.idp.util.WebUtils; +import org.apache.cxf.rs.security.saml.sso.SSOConstants; +import org.apache.wss4j.common.crypto.CertificateStore; +import org.apache.wss4j.common.crypto.Crypto; +import org.apache.wss4j.common.ext.WSSecurityException; +import org.apache.wss4j.common.saml.SAMLKeyInfo; +import org.apache.wss4j.common.saml.SAMLUtil; +import org.apache.wss4j.dom.WSDocInfo; +import org.apache.wss4j.dom.engine.WSSConfig; +import org.apache.wss4j.dom.handler.RequestData; +import org.apache.wss4j.dom.saml.WSSSAMLKeyInfoProcessor; +import org.apache.wss4j.dom.validate.Credential; +import org.apache.wss4j.dom.validate.SignatureTrustValidator; +import org.apache.wss4j.dom.validate.Validator; +import org.apache.xml.security.utils.Base64; +import org.opensaml.saml.saml2.core.AuthnRequest; +import org.opensaml.saml.security.impl.SAMLSignatureProfileValidator; +import org.opensaml.security.credential.BasicCredential; +import org.opensaml.security.x509.BasicX509Credential; +import org.opensaml.xmlsec.signature.KeyInfo; +import org.opensaml.xmlsec.signature.Signature; +import org.opensaml.xmlsec.signature.support.SignatureException; +import org.opensaml.xmlsec.signature.support.SignatureValidator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; +import org.springframework.webflow.execution.RequestContext; + +/** + * Validate the received AuthnRequest + */ +@Component +public class AuthnRequestValidator { + + private static final Logger LOG = LoggerFactory.getLogger(AuthnRequestValidator.class); + + public void validateAuthnRequest(RequestContext context, Idp idp, String signature, + String relayState, String samlRequest, String realm) + throws Exception { + AuthnRequest authnRequest = + (AuthnRequest)WebUtils.getAttributeFromFlowScope(context, IdpConstants.SAML_AUTHN_REQUEST); + if (authnRequest.isSigned()) { + // Check destination + checkDestination(context, authnRequest); + + // Check signature + X509Certificate validatingCert = getValidatingCertificate(idp, realm); + Crypto issuerCrypto = + new CertificateStore(Collections.singletonList(validatingCert).toArray(new X509Certificate[0])); + validateAuthnRequestSignature(authnRequest.getSignature(), issuerCrypto); + } else if (signature != null) { + // Check destination + checkDestination(context, authnRequest); + + // Check signature + X509Certificate validatingCert = getValidatingCertificate(idp, realm); + + java.security.Signature sig = java.security.Signature.getInstance("SHA1withRSA"); + sig.initVerify(validatingCert); + + // Recreate request to sign + String requestToSign = WebUtils.getHttpServletRequest(context).getRequestURL().toString() + "?"; + requestToSign += SSOConstants.RELAY_STATE + "=" + relayState; + requestToSign += "&" + SSOConstants.SAML_REQUEST + "=" + URLEncoder.encode(samlRequest, "UTF-8"); + requestToSign += "&" + SSOConstants.SIG_ALG + "=" + + URLEncoder.encode(SSOConstants.RSA_SHA1, StandardCharsets.UTF_8.name()); + + sig.update(requestToSign.getBytes(StandardCharsets.UTF_8)); + + if (!sig.verify(Base64.decode(signature))) { + LOG.debug("Signature validation failed"); + throw new ProcessingException(TYPE.BAD_REQUEST); + } + } else { + LOG.debug("No signature is present, therefore the request is rejected"); + throw new ProcessingException(TYPE.BAD_REQUEST); + } + + if (authnRequest.getIssuer() == null) { + LOG.debug("No Issuer is present in the AuthnRequest"); + throw new ProcessingException(TYPE.BAD_REQUEST); + } + + String format = authnRequest.getIssuer().getFormat(); + if (format != null + && !"urn:oasis:names:tc:SAML:2.0:nameid-format:entity".equals(format)) { + LOG.debug("An invalid Format attribute was received: {}", format); + throw new ProcessingException(TYPE.BAD_REQUEST); + } + } + + private X509Certificate getValidatingCertificate(Idp idp, String realm) + throws Exception { + Application serviceConfig = idp.findApplication(realm); + if (serviceConfig == null || serviceConfig.getValidatingCertificate() == null) { + LOG.debug("No validating certificate found for realm {}", realm); + throw new ProcessingException(TYPE.ISSUER_NOT_TRUSTED); + } + + return CertsUtils.parseX509Certificate(serviceConfig.getValidatingCertificate()); + } + + private void checkDestination(RequestContext context, AuthnRequest authnRequest) throws ProcessingException { + // Check destination + String destination = authnRequest.getDestination(); + LOG.debug("Validating destination: {}", destination); + + String localAddr = WebUtils.getHttpServletRequest(context).getRequestURL().toString(); + if (!localAddr.startsWith(destination)) { + LOG.debug("The destination {} does not match the local address {}", destination, localAddr); + throw new ProcessingException(TYPE.BAD_REQUEST); + } + } + + /** + * Validate the AuthnRequest signature + */ + private void validateAuthnRequestSignature( + Signature signature, + Crypto sigCrypto + ) throws WSSecurityException { + RequestData requestData = new RequestData(); + requestData.setSigVerCrypto(sigCrypto); + WSSConfig wssConfig = WSSConfig.getNewInstance(); + requestData.setWssConfig(wssConfig); + // requestData.setCallbackHandler(callbackHandler); + + SAMLKeyInfo samlKeyInfo = null; + + KeyInfo keyInfo = signature.getKeyInfo(); + if (keyInfo != null) { + try { + Document doc = signature.getDOM().getOwnerDocument(); + samlKeyInfo = + SAMLUtil.getCredentialFromKeyInfo( + keyInfo.getDOM(), new WSSSAMLKeyInfoProcessor(requestData, new WSDocInfo(doc)), sigCrypto + ); + } catch (WSSecurityException ex) { + LOG.debug("Error in getting KeyInfo from SAML AuthnRequest: {}", ex.getMessage(), ex); + throw ex; + } + } + + if (samlKeyInfo == null) { + LOG.debug("No KeyInfo supplied in the AuthnRequest signature"); + throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "invalidSAMLsecurity"); + } + + // Validate Signature against profiles + validateSignatureAgainstProfiles(signature, samlKeyInfo); + + // Now verify trust on the signature + Credential trustCredential = new Credential(); + trustCredential.setPublicKey(samlKeyInfo.getPublicKey()); + trustCredential.setCertificates(samlKeyInfo.getCerts()); + + try { + Validator signatureValidator = new SignatureTrustValidator(); + signatureValidator.validate(trustCredential, requestData); + } catch (WSSecurityException e) { + LOG.debug("Error in validating signature on SAML AuthnRequest: {}", e.getMessage(), e); + throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "invalidSAMLsecurity"); + } + } + + /** + * Validate a signature against the profiles + */ + private void validateSignatureAgainstProfiles( + Signature signature, + SAMLKeyInfo samlKeyInfo + ) throws WSSecurityException { + // Validate Signature against profiles + SAMLSignatureProfileValidator validator = new SAMLSignatureProfileValidator(); + try { + validator.validate(signature); + } catch (SignatureException ex) { + LOG.debug("Error in validating the SAML Signature: {}", ex.getMessage(), ex); + throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "invalidSAMLsecurity"); + } + + BasicCredential credential = null; + if (samlKeyInfo.getCerts() != null) { + credential = new BasicX509Credential(samlKeyInfo.getCerts()[0]); + } else if (samlKeyInfo.getPublicKey() != null) { + credential = new BasicCredential(samlKeyInfo.getPublicKey()); + } else { + LOG.debug("Can't get X509Certificate or PublicKey to verify signature"); + throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "invalidSAMLsecurity"); + } + try { + SignatureValidator.validate(signature, credential); + } catch (SignatureException ex) { + LOG.debug("Error in validating the SAML Signature: {}", ex.getMessage(), ex); + throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "invalidSAMLsecurity"); + } + } + +} http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/bb6d06d6/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/domain/Application.java ---------------------------------------------------------------------- diff --git a/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/domain/Application.java b/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/domain/Application.java index 47f2d9f..63d7a9d 100644 --- a/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/domain/Application.java +++ b/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/domain/Application.java @@ -33,7 +33,8 @@ import javax.xml.bind.annotation.XmlType; @XmlRootElement(name = "application", namespace = "http://org.apache.cxf.fediz/") @XmlType(propOrder = {"realm", "role", "serviceDisplayName", "serviceDescription", "protocol", "tokenType", "lifeTime", "encryptionCertificate", "requestedClaims", - "policyNamespace", "passiveRequestorEndpoint", "passiveRequestorEndpointConstraint", "id" }) + "policyNamespace", "passiveRequestorEndpoint", "passiveRequestorEndpointConstraint", "id", + "validatingCertificate"}) public class Application implements Serializable { private static final long serialVersionUID = 5644327504861846964L; @@ -57,6 +58,9 @@ public class Application implements Serializable { // Could be read from Metadata, md:KeyDescriptor, use="encryption" protected String encryptionCertificate; + // Certificate for Signature verification + protected String validatingCertificate; + // Could be read from Metadata, fed:ClaimTypesRequested protected List<RequestClaim> requestedClaims = new ArrayList<>(); @@ -216,4 +220,12 @@ public class Application implements Serializable { public Pattern getCompiledPassiveRequestorEndpointConstraint() { return compiledPassiveRequestorEndpointConstraint; } + + public String getValidatingCertificate() { + return validatingCertificate; + } + + public void setValidatingCertificate(String validatingCertificate) { + this.validatingCertificate = validatingCertificate; + } } http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/bb6d06d6/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/samlsso/AuthnRequestValidator.java ---------------------------------------------------------------------- diff --git a/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/samlsso/AuthnRequestValidator.java b/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/samlsso/AuthnRequestValidator.java deleted file mode 100644 index 1fa58c6..0000000 --- a/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/samlsso/AuthnRequestValidator.java +++ /dev/null @@ -1,211 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.cxf.fediz.service.idp.samlsso; - -import java.net.URLEncoder; -import java.nio.charset.StandardCharsets; -import java.security.cert.X509Certificate; - -import org.w3c.dom.Document; - -import org.apache.cxf.fediz.core.exception.ProcessingException; -import org.apache.cxf.fediz.core.exception.ProcessingException.TYPE; -import org.apache.cxf.fediz.core.util.CertsUtils; -import org.apache.cxf.fediz.service.idp.domain.Idp; -import org.apache.cxf.fediz.service.idp.util.WebUtils; -import org.apache.cxf.rs.security.saml.sso.SSOConstants; -import org.apache.wss4j.common.crypto.Crypto; -import org.apache.wss4j.common.ext.WSSecurityException; -import org.apache.wss4j.common.saml.SAMLKeyInfo; -import org.apache.wss4j.common.saml.SAMLUtil; -import org.apache.wss4j.dom.WSDocInfo; -import org.apache.wss4j.dom.engine.WSSConfig; -import org.apache.wss4j.dom.handler.RequestData; -import org.apache.wss4j.dom.saml.WSSSAMLKeyInfoProcessor; -import org.apache.wss4j.dom.validate.Credential; -import org.apache.wss4j.dom.validate.SignatureTrustValidator; -import org.apache.wss4j.dom.validate.Validator; -import org.apache.xml.security.utils.Base64; -import org.opensaml.saml.saml2.core.AuthnRequest; -import org.opensaml.saml.security.impl.SAMLSignatureProfileValidator; -import org.opensaml.security.credential.BasicCredential; -import org.opensaml.security.x509.BasicX509Credential; -import org.opensaml.xmlsec.signature.KeyInfo; -import org.opensaml.xmlsec.signature.Signature; -import org.opensaml.xmlsec.signature.support.SignatureException; -import org.opensaml.xmlsec.signature.support.SignatureValidator; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.webflow.execution.RequestContext; - -/** - * Validate the received AuthnRequest - */ -public class AuthnRequestValidator { - - private static final Logger LOG = LoggerFactory.getLogger(AuthnRequestValidator.class); - - public void validateAuthnRequest(RequestContext context, AuthnRequest authnRequest, Idp idp, String signature, - String relayState, String samlRequest) - throws Exception { - if (authnRequest.isSigned()) { - // Check destination - checkDestination(context, authnRequest); - - // Check signature - Crypto issuerCrypto = CertsUtils.getCryptoFromCertificate(idp.getCertificate()); - validateAuthnRequestSignature(authnRequest.getSignature(), issuerCrypto); - } else if (signature != null) { - // Check destination - checkDestination(context, authnRequest); - - // Check signature - X509Certificate validatingCert = CertsUtils.parseX509Certificate(idp.getCertificate()); - - java.security.Signature sig = java.security.Signature.getInstance("SHA1withRSA"); - sig.initVerify(validatingCert); - - // Recreate request to sign - String requestToSign = WebUtils.getHttpServletRequest(context).getRequestURL().toString() + "?"; - requestToSign += SSOConstants.RELAY_STATE + "=" + relayState; - requestToSign += "&" + SSOConstants.SAML_REQUEST + "=" + URLEncoder.encode(samlRequest, "UTF-8"); - requestToSign += "&" + SSOConstants.SIG_ALG + "=" - + URLEncoder.encode(SSOConstants.RSA_SHA1, StandardCharsets.UTF_8.name()); - - sig.update(requestToSign.getBytes(StandardCharsets.UTF_8)); - - if (!sig.verify(Base64.decode(signature))) { - LOG.debug("Signature validation failed"); - throw new ProcessingException(TYPE.BAD_REQUEST); - } - } else { - LOG.debug("No signature is present, therefore the request is rejected"); - throw new ProcessingException(TYPE.BAD_REQUEST); - } - - if (authnRequest.getIssuer() == null) { - LOG.debug("No Issuer is present in the AuthnRequest"); - throw new ProcessingException(TYPE.BAD_REQUEST); - } - - String format = authnRequest.getIssuer().getFormat(); - if (format != null - && !"urn:oasis:names:tc:SAML:2.0:nameid-format:entity".equals(format)) { - LOG.debug("An invalid Format attribute was received: {}", format); - throw new ProcessingException(TYPE.BAD_REQUEST); - } - } - - private void checkDestination(RequestContext context, AuthnRequest authnRequest) throws ProcessingException { - // Check destination - String destination = authnRequest.getDestination(); - LOG.debug("Validating destination: {}", destination); - - String localAddr = WebUtils.getHttpServletRequest(context).getRequestURL().toString(); - if (!localAddr.startsWith(destination)) { - LOG.debug("The destination {} does not match the local address {}", destination, localAddr); - throw new ProcessingException(TYPE.BAD_REQUEST); - } - } - - /** - * Validate the AuthnRequest signature - */ - private void validateAuthnRequestSignature( - Signature signature, - Crypto sigCrypto - ) throws WSSecurityException { - RequestData requestData = new RequestData(); - requestData.setSigVerCrypto(sigCrypto); - WSSConfig wssConfig = WSSConfig.getNewInstance(); - requestData.setWssConfig(wssConfig); - // requestData.setCallbackHandler(callbackHandler); - - SAMLKeyInfo samlKeyInfo = null; - - KeyInfo keyInfo = signature.getKeyInfo(); - if (keyInfo != null) { - try { - Document doc = signature.getDOM().getOwnerDocument(); - samlKeyInfo = - SAMLUtil.getCredentialFromKeyInfo( - keyInfo.getDOM(), new WSSSAMLKeyInfoProcessor(requestData, new WSDocInfo(doc)), sigCrypto - ); - } catch (WSSecurityException ex) { - LOG.debug("Error in getting KeyInfo from SAML AuthnRequest: {}", ex.getMessage(), ex); - throw ex; - } - } - - if (samlKeyInfo == null) { - LOG.debug("No KeyInfo supplied in the AuthnRequest signature"); - throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "invalidSAMLsecurity"); - } - - // Validate Signature against profiles - validateSignatureAgainstProfiles(signature, samlKeyInfo); - - // Now verify trust on the signature - Credential trustCredential = new Credential(); - trustCredential.setPublicKey(samlKeyInfo.getPublicKey()); - trustCredential.setCertificates(samlKeyInfo.getCerts()); - - try { - Validator signatureValidator = new SignatureTrustValidator(); - signatureValidator.validate(trustCredential, requestData); - } catch (WSSecurityException e) { - LOG.debug("Error in validating signature on SAML AuthnRequest: {}", e.getMessage(), e); - throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "invalidSAMLsecurity"); - } - } - - /** - * Validate a signature against the profiles - */ - private void validateSignatureAgainstProfiles( - Signature signature, - SAMLKeyInfo samlKeyInfo - ) throws WSSecurityException { - // Validate Signature against profiles - SAMLSignatureProfileValidator validator = new SAMLSignatureProfileValidator(); - try { - validator.validate(signature); - } catch (SignatureException ex) { - LOG.debug("Error in validating the SAML Signature: {}", ex.getMessage(), ex); - throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "invalidSAMLsecurity"); - } - - BasicCredential credential = null; - if (samlKeyInfo.getCerts() != null) { - credential = new BasicX509Credential(samlKeyInfo.getCerts()[0]); - } else if (samlKeyInfo.getPublicKey() != null) { - credential = new BasicCredential(samlKeyInfo.getPublicKey()); - } else { - LOG.debug("Can't get X509Certificate or PublicKey to verify signature"); - throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "invalidSAMLsecurity"); - } - try { - SignatureValidator.validate(signature, credential); - } catch (SignatureException ex) { - LOG.debug("Error in validating the SAML Signature: {}", ex.getMessage(), ex); - throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "invalidSAMLsecurity"); - } - } - -} http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/bb6d06d6/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/service/jpa/ApplicationDAOJPAImpl.java ---------------------------------------------------------------------- diff --git a/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/service/jpa/ApplicationDAOJPAImpl.java b/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/service/jpa/ApplicationDAOJPAImpl.java index b18dc90..aa3274f 100644 --- a/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/service/jpa/ApplicationDAOJPAImpl.java +++ b/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/service/jpa/ApplicationDAOJPAImpl.java @@ -191,6 +191,7 @@ public class ApplicationDAOJPAImpl implements ApplicationDAO { } entity.setEncryptionCertificate(application.getEncryptionCertificate()); + entity.setValidatingCertificate(application.getValidatingCertificate()); entity.setLifeTime(application.getLifeTime()); entity.setProtocol(application.getProtocol()); entity.setRealm(application.getRealm()); @@ -207,6 +208,7 @@ public class ApplicationDAOJPAImpl implements ApplicationDAO { Application application = new Application(); application.setId(entity.getId()); application.setEncryptionCertificate(entity.getEncryptionCertificate()); + application.setValidatingCertificate(entity.getValidatingCertificate()); application.setLifeTime(entity.getLifeTime()); application.setProtocol(entity.getProtocol()); application.setRealm(entity.getRealm()); http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/bb6d06d6/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/service/jpa/ApplicationEntity.java ---------------------------------------------------------------------- diff --git a/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/service/jpa/ApplicationEntity.java b/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/service/jpa/ApplicationEntity.java index 2a0e8de..7b64712 100644 --- a/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/service/jpa/ApplicationEntity.java +++ b/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/service/jpa/ApplicationEntity.java @@ -52,6 +52,9 @@ public class ApplicationEntity { // Could be read from Metadata, md:KeyDescriptor, use="encryption" private String encryptionCertificate; + // Certificate for Signature verification + private String validatingCertificate; + // Could be read from Metadata, fed:ClaimTypesRequested @OneToMany(mappedBy = "application", cascade = CascadeType.ALL, orphanRemoval = true) private List<ApplicationClaimEntity> requestedClaims = new ArrayList<>(); @@ -190,4 +193,11 @@ public class ApplicationEntity { this.passiveRequestorEndpointConstraint = passiveRequestorEndpointConstraint; } + public String getValidatingCertificate() { + return validatingCertificate; + } + + public void setValidatingCertificate(String validatingCertificate) { + this.validatingCertificate = validatingCertificate; + } } http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/bb6d06d6/services/idp/src/main/webapp/WEB-INF/flows/saml-validate-request.xml ---------------------------------------------------------------------- diff --git a/services/idp/src/main/webapp/WEB-INF/flows/saml-validate-request.xml b/services/idp/src/main/webapp/WEB-INF/flows/saml-validate-request.xml index 2578e98..4e008eb 100644 --- a/services/idp/src/main/webapp/WEB-INF/flows/saml-validate-request.xml +++ b/services/idp/src/main/webapp/WEB-INF/flows/saml-validate-request.xml @@ -55,9 +55,15 @@ </subflow-state> <action-state id="parseAndValidateSAMLRequest"> - <evaluate expression="authnRequestParser.parseSAMLRequest(flowRequestContext, flowScope.idpConfig, - flowScope.Signature, flowScope.RelayState, + <on-entry> + <evaluate expression="authnRequestParser.parseSAMLRequest(flowRequestContext, flowScope.idpConfig, flowScope.SAMLRequest)" /> + <evaluate expression="authnRequestParser.retrieveRealm(flowRequestContext)" + result="flowScope.realm"/> + </on-entry> + <evaluate expression="authnRequestValidator.validateAuthnRequest(flowRequestContext, flowScope.idpConfig, + flowScope.Signature, flowScope.RelayState, + flowScope.SAMLRequest, flowScope.realm)" /> <transition to="requestRpToken"/> <transition on-exception="org.apache.cxf.fediz.core.exception.ProcessingException" to="viewBadRequest" /> </action-state> http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/bb6d06d6/systests/samlsso/src/test/java/org/apache/cxf/fediz/systests/idp/IdpTest.java ---------------------------------------------------------------------- diff --git a/systests/samlsso/src/test/java/org/apache/cxf/fediz/systests/idp/IdpTest.java b/systests/samlsso/src/test/java/org/apache/cxf/fediz/systests/idp/IdpTest.java index 97137b1..bdf72d4 100644 --- a/systests/samlsso/src/test/java/org/apache/cxf/fediz/systests/idp/IdpTest.java +++ b/systests/samlsso/src/test/java/org/apache/cxf/fediz/systests/idp/IdpTest.java @@ -337,7 +337,7 @@ public class IdpTest { webClient.getPage(url); Assert.fail("Failure expected on no destination value"); } catch (FailingHttpStatusCodeException ex) { - Assert.assertEquals(ex.getStatusCode(), 400); + // expected } webClient.close(); http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/bb6d06d6/systests/samlsso/src/test/resources/realma.cert ---------------------------------------------------------------------- diff --git a/systests/samlsso/src/test/resources/realma.cert b/systests/samlsso/src/test/resources/realma.cert new file mode 100644 index 0000000..ff97f79 --- /dev/null +++ b/systests/samlsso/src/test/resources/realma.cert @@ -0,0 +1,15 @@ +-----BEGIN CERTIFICATE----- +MIICwTCCAamgAwIBAgIEINqJ9TANBgkqhkiG9w0BAQsFADARMQ8wDQYDVQQDEwZSRUFMTUEwHhcN +MTUwNjEwMTU0NDE3WhcNMjUwNDE4MTU0NDE3WjARMQ8wDQYDVQQDEwZSRUFMTUEwggEiMA0GCSqG +SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCJDSXn2lDR+JM+AsJarFG3/XGH7K+9AfAbQIz2IgB9MCpO +KVWTUPCvuo1I+Fp5nEGreuHYLEwgIiam3o+C9tvpLgtDDaDkmXjDzkWpk8z6+im72HZ/ODF93Rqw +jIiY5ZCzgDumFyPzdKiGwChThamidy+rd6oheSoi6qRVSMMcnwiEUmvkfFvV3izXRqeT5nGQwsin +y9mCEiGx8jkfxP++H0RQjVjhOwzfQ7epsR7dTQNf2ZhkBR3o6wKV9QnF2IBWHZpA9EK58rWU9H6j +G7b631rYvwsbOUF9HcZ8DI2BFh+4p18jDN/fnjNGSLr9rYOExpsIiF1cHBK7Tr7WwCmDAgMBAAGj +ITAfMB0GA1UdDgQWBBRHy0qYoLm9jx/1L6r61NznHKun2jANBgkqhkiG9w0BAQsFAAOCAQEAR9rU +5Sp1FsOErdvKNFqeaKl0oq6Fuz7BWcGm2kK6+1ZbWE8IOv6Vh+BlLuOe5hF7aLUbm8UIjhKsmg0M +Ey5MBwkBZktT1qhQteMuiKgYR7CxayCxO0f125RYvvwntJa5rI7bUrzOqX29VQD1qQ/Tb+08fULT +L7oURP+g88Ff99dn3IpO4VZxZdsbl4+KZRtqQvPAdXNYjOajJtPzS489+/DtfWJ6wPm/7YZ4did4 +1fYcrdwyEZ15L0/5i931z7sztNickm5WhO40qEVDKN6KrlV2Eyea0+933v2Pwe4resTlko9G2T5h +dEaSbvht2Q/JOMMmT91daeto2oS8HTKhTA== +-----END CERTIFICATE----- http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/bb6d06d6/systests/samlsso/src/test/resources/realma/entities-realma.xml ---------------------------------------------------------------------- diff --git a/systests/samlsso/src/test/resources/realma/entities-realma.xml b/systests/samlsso/src/test/resources/realma/entities-realma.xml index fec578c..9f3b9d1 100644 --- a/systests/samlsso/src/test/resources/realma/entities-realma.xml +++ b/systests/samlsso/src/test/resources/realma/entities-realma.xml @@ -152,6 +152,7 @@ <property name="lifeTime" value="3600" /> <property name="passiveRequestorEndpointConstraint" value="https://localhost:(\d)*/(\w)*helloworld(\w)*/secure/.*" /> + <property name="validatingCertificate" value="realma.cert" /> </bean> <bean class="org.apache.cxf.fediz.service.idp.service.jpa.ApplicationClaimEntity">
