Repository: cxf-fediz Updated Branches: refs/heads/master 49b028e3e -> a700f7ae2
[FEDIZ-153] - Got an initial test-case working with a lot of hacks Project: http://git-wip-us.apache.org/repos/asf/cxf-fediz/repo Commit: http://git-wip-us.apache.org/repos/asf/cxf-fediz/commit/a700f7ae Tree: http://git-wip-us.apache.org/repos/asf/cxf-fediz/tree/a700f7ae Diff: http://git-wip-us.apache.org/repos/asf/cxf-fediz/diff/a700f7ae Branch: refs/heads/master Commit: a700f7ae2dd0eba786af620f5c671eb5cf62fc4f Parents: 49b028e Author: Colm O hEigeartaigh <[email protected]> Authored: Tue Feb 23 15:39:41 2016 +0000 Committer: Colm O hEigeartaigh <[email protected]> Committed: Tue Feb 23 15:39:41 2016 +0000 ---------------------------------------------------------------------- .../TrustedIdpOIDCProtocolHandler.java | 382 ++++++++++++------- .../fediz/service/oidc/FedizSubjectCreator.java | 2 +- systests/federation/oidc/pom.xml | 1 + .../oidc/src/test/resources/realmb.cert | 3 + 4 files changed, 257 insertions(+), 131 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/a700f7ae/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/protocols/TrustedIdpOIDCProtocolHandler.java ---------------------------------------------------------------------- diff --git a/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/protocols/TrustedIdpOIDCProtocolHandler.java b/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/protocols/TrustedIdpOIDCProtocolHandler.java index f4ffe40..c6ebeba 100644 --- a/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/protocols/TrustedIdpOIDCProtocolHandler.java +++ b/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/protocols/TrustedIdpOIDCProtocolHandler.java @@ -30,34 +30,23 @@ import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.util.ArrayList; -import java.util.Collections; +import java.util.Date; import java.util.List; +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.UnsupportedCallbackException; import javax.servlet.http.HttpServletRequest; import javax.ws.rs.core.Form; import javax.ws.rs.core.Response; +import org.w3c.dom.Document; import org.w3c.dom.Element; -import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider; - import org.apache.cxf.fediz.core.FederationConstants; -import org.apache.cxf.fediz.core.config.FedizContext; -import org.apache.cxf.fediz.core.config.TrustManager; -import org.apache.cxf.fediz.core.config.jaxb.AudienceUris; -import org.apache.cxf.fediz.core.config.jaxb.CertificateStores; -import org.apache.cxf.fediz.core.config.jaxb.ContextConfig; -import org.apache.cxf.fediz.core.config.jaxb.FederationProtocolType; -import org.apache.cxf.fediz.core.config.jaxb.KeyStoreType; -import org.apache.cxf.fediz.core.config.jaxb.TrustManagersType; -import org.apache.cxf.fediz.core.config.jaxb.TrustedIssuerType; -import org.apache.cxf.fediz.core.config.jaxb.TrustedIssuers; -import org.apache.cxf.fediz.core.config.jaxb.ValidationType; import org.apache.cxf.fediz.core.exception.ProcessingException; -import org.apache.cxf.fediz.core.processor.FederationProcessorImpl; -import org.apache.cxf.fediz.core.processor.FedizProcessor; -import org.apache.cxf.fediz.core.processor.FedizRequest; -import org.apache.cxf.fediz.core.processor.FedizResponse; +import org.apache.cxf.fediz.core.util.CertsUtils; +import org.apache.cxf.fediz.core.util.DOMUtils; import org.apache.cxf.fediz.service.idp.domain.Idp; import org.apache.cxf.fediz.service.idp.domain.TrustedIdp; import org.apache.cxf.fediz.service.idp.spi.TrustedIdpProtocolHandler; @@ -66,13 +55,29 @@ import org.apache.cxf.interceptor.LoggingInInterceptor; import org.apache.cxf.interceptor.LoggingOutInterceptor; import org.apache.cxf.jaxrs.client.ClientConfiguration; import org.apache.cxf.jaxrs.client.WebClient; +import org.apache.cxf.rs.security.jose.jwa.SignatureAlgorithm; +import org.apache.cxf.rs.security.jose.jws.JwsJwtCompactConsumer; +import org.apache.cxf.rs.security.jose.jwt.JwtConstants; +import org.apache.cxf.rs.security.jose.jwt.JwtToken; import org.apache.cxf.rs.security.oauth2.common.ClientAccessToken; +import org.apache.cxf.rs.security.oauth2.provider.OAuthJSONProvider; import org.apache.cxf.rs.security.oauth2.utils.OAuthConstants; import org.apache.cxf.ws.security.tokenstore.SecurityToken; import org.apache.wss4j.common.crypto.CertificateStore; +import org.apache.wss4j.common.crypto.Crypto; +import org.apache.wss4j.common.crypto.Merlin; +import org.apache.wss4j.common.ext.WSSecurityException; +import org.apache.wss4j.common.saml.SAMLCallback; +import org.apache.wss4j.common.saml.SAMLUtil; +import org.apache.wss4j.common.saml.SamlAssertionWrapper; +import org.apache.wss4j.common.saml.bean.ConditionsBean; +import org.apache.wss4j.common.saml.bean.SubjectBean; +import org.apache.wss4j.common.saml.bean.Version; +import org.apache.wss4j.common.saml.builder.SAML2Constants; +import org.apache.wss4j.common.util.DOM2Writer; import org.apache.xml.security.exceptions.Base64DecodingException; -import org.apache.xml.security.stax.impl.util.IDGenerator; import org.apache.xml.security.utils.Base64; +import org.joda.time.DateTime; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; @@ -192,10 +197,10 @@ public class TrustedIdpOIDCProtocolHandler implements TrustedIdpProtocolHandler String address = "http://localhost:8080/auth/realms/realmb/protocol/openid-connect/token"; List<Object> providers = new ArrayList<Object>(); - providers.add(new JacksonJsonProvider()); + providers.add(new OAuthJSONProvider()); WebClient client = - WebClient.create(address, providers, "consumer-id", "90d5da25-e900-443f-a5d5-feb3bb060800", null); + WebClient.create(address, providers, "consumer-id", "7c220ee6-77e2-43d3-b531-6ede8a581698", null); ClientConfiguration config = WebClient.getConfig(client); @@ -212,123 +217,121 @@ public class TrustedIdpOIDCProtocolHandler implements TrustedIdpProtocolHandler Response response = client.post(form); ClientAccessToken accessToken = response.readEntity(ClientAccessToken.class); - System.out.println("AT: " + accessToken.getTokenKey()); - - } - - try { - String whr = (String) WebUtils.getAttributeFromFlowScope(context, - FederationConstants.PARAM_HOME_REALM); - - if (whr == null) { - LOG.warn("Home realm is null"); - throw new IllegalStateException("Home realm is null"); - } - - String wresult = (String) WebUtils.getAttributeFromFlowScope(context, - FederationConstants.PARAM_RESULT); - - if (wresult == null) { - LOG.warn("Parameter wresult not found"); - throw new IllegalStateException("No security token issued"); + String idToken = accessToken.getParameters().get("id_token"); + if (idToken == null) { + LOG.warn("No IdToken received from the OIDC IdP"); + return null; } - - FedizContext fedContext = getFedizContext(idp, trustedIdp); - - FedizRequest wfReq = new FedizRequest(); - wfReq.setAction(FederationConstants.ACTION_SIGNIN); - wfReq.setResponseToken(wresult); - - FedizProcessor wfProc = new FederationProcessorImpl(); - FedizResponse wfResp = wfProc.processRequest(wfReq, fedContext); - - fedContext.close(); - - Element e = wfResp.getToken(); - - // Create new Security token with new id. - // Parameters for freshness computation are copied from original IDP_TOKEN - String id = IDGenerator.generateID("_"); - SecurityToken idpToken = new SecurityToken(id, - wfResp.getTokenCreated(), wfResp.getTokenExpires()); - - idpToken.setToken(e); - LOG.info("[IDP_TOKEN={}] for user '{}' created from [RP_TOKEN={}] issued by home realm [{}/{}]", - id, wfResp.getUsername(), wfResp.getUniqueTokenId(), whr, wfResp.getIssuer()); - LOG.debug("Created date={}", wfResp.getTokenCreated()); - LOG.debug("Expired date={}", wfResp.getTokenExpires()); - if (LOG.isDebugEnabled()) { - LOG.debug("Validated 'wresult' : " - + System.getProperty("line.separator") + wresult); + + try { + X509Certificate validatingCert = getCertificate(trustedIdp); + if (validatingCert == null) { + LOG.warn("No X.509 Certificate configured for signature validation"); + return null; + } + + /*String whr = (String) WebUtils.getAttributeFromFlowScope(context, + FederationConstants.PARAM_HOME_REALM); + if (whr == null) { + LOG.warn("Home realm is null"); + throw new IllegalStateException("Home realm is null"); + } + + String wresult = (String) WebUtils.getAttributeFromFlowScope(context, + FederationConstants.PARAM_RESULT); + if (wresult == null) { + LOG.warn("Parameter wresult not found"); + throw new IllegalStateException("No security token issued"); + }*/ + + // Parse the received Token + JwsJwtCompactConsumer jwtConsumer = new JwsJwtCompactConsumer(idToken); + JwtToken jwt = jwtConsumer.getJwtToken(); + + for (String claim : jwt.getClaims().asMap().keySet()) { + System.out.println("CLAIM: " + claim + " " + jwt.getClaim(claim)); + } + + if (!jwtConsumer.verifySignatureWith(validatingCert, SignatureAlgorithm.RS256)) { + LOG.warn("Signature does not validate"); + return null; + } + + Date created = new Date(); + if (jwt.getClaim(JwtConstants.CLAIM_ISSUED_AT) != null) { + created = new Date((long)jwt.getClaim(JwtConstants.CLAIM_ISSUED_AT) * 1000L); + } + Date expires = new Date((long)jwt.getClaim(JwtConstants.CLAIM_EXPIRY) * 1000L); + System.out.println("IAT: " + created); + System.out.println("EXP: " + expires); + + // Convert into a SAML Token + SamlAssertionWrapper assertion = createSamlAssertion(idp, jwt, created, expires); + Document doc = DOMUtils.createDocument(); + Element token = assertion.toDOM(doc); + System.out.println("TOK: " + DOM2Writer.nodeToString(token)); + + // Create new Security token with new id. + // Parameters for freshness computation are copied from original IDP_TOKEN + SecurityToken idpToken = new SecurityToken(assertion.getId(), created, expires); + idpToken.setToken(token); + + // idpToken.setToken(e); + // LOG.info("[IDP_TOKEN={}] for user '{}' created from [RP_TOKEN={}] issued by home realm [{}/{}]", + // id, wfResp.getUsername(), wfResp.getUniqueTokenId(), whr, wfResp.getIssuer()); + LOG.debug("Created date={}", created); + LOG.debug("Expired date={}", expires); + if (LOG.isDebugEnabled()) { + //LOG.debug("Validated 'wresult' : " + // + System.getProperty("line.separator") + wresult); + } + return idpToken; + } catch (IllegalStateException ex) { + throw ex; + } catch (Exception ex) { + LOG.warn("Unexpected exception occured", ex); + throw new IllegalStateException("Unexpected exception occured: " + ex.getMessage()); } - return idpToken; - } catch (IllegalStateException ex) { - throw ex; - } catch (Exception ex) { - LOG.warn("Unexpected exception occured", ex); - throw new IllegalStateException("Unexpected exception occured: " + ex.getMessage()); } + return null; } - private FedizContext getFedizContext(Idp idpConfig, - TrustedIdp trustedIdpConfig) throws ProcessingException { - - ContextConfig config = new ContextConfig(); - - config.setName("whatever"); - - // Configure certificate store - String certificate = trustedIdpConfig.getCertificate(); - boolean isCertificateLocation = !certificate.startsWith("-----BEGIN CERTIFICATE"); - if (isCertificateLocation) { - CertificateStores certStores = new CertificateStores(); - TrustManagersType tm0 = new TrustManagersType(); - KeyStoreType ks0 = new KeyStoreType(); - ks0.setType("PEM"); - // ks0.setType("JKS"); - // ks0.setPassword("changeit"); - ks0.setFile(trustedIdpConfig.getCertificate()); - tm0.setKeyStore(ks0); - certStores.getTrustManager().add(tm0); - config.setCertificateStores(certStores); - } - - // Configure trusted IDP - TrustedIssuers trustedIssuers = new TrustedIssuers(); - TrustedIssuerType ti0 = new TrustedIssuerType(); - ti0.setCertificateValidation(ValidationType.PEER_TRUST); - ti0.setName(trustedIdpConfig.getName()); - // ti0.setSubject(".*CN=www.sts.com.*"); - trustedIssuers.getIssuer().add(ti0); - config.setTrustedIssuers(trustedIssuers); - - FederationProtocolType protocol = new FederationProtocolType(); - config.setProtocol(protocol); - - AudienceUris audienceUris = new AudienceUris(); - audienceUris.getAudienceItem().add(idpConfig.getRealm()); - config.setAudienceUris(audienceUris); - - FedizContext fedContext = new FedizContext(config); - if (!isCertificateLocation) { - CertificateStore cs = null; - - X509Certificate cert; - try { - cert = parseCertificate(trustedIdpConfig.getCertificate()); - } catch (Exception ex) { - LOG.error("Failed to parse trusted certificate", ex); - throw new ProcessingException("Failed to parse trusted certificate"); + private X509Certificate getCertificate(TrustedIdp trustedIdp) + throws CertificateException, Base64DecodingException, IOException { + String certificate = trustedIdp.getCertificate(); + if (certificate != null) { + boolean isCertificateLocation = !certificate.startsWith("-----BEGIN CERTIFICATE"); + if (isCertificateLocation) { + InputStream is = null; + try { + is = Merlin.loadInputStream(Thread.currentThread().getContextClassLoader(), certificate); + + CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); + return (X509Certificate) certFactory.generateCertificate(is); + } catch (WSSecurityException ex) { + LOG.error("Failed to load keystore " + certificate, ex); + throw new RuntimeException("Failed to load keystore " + certificate); + } catch (IOException ex) { + LOG.error("Failed to read keystore", ex); + throw new RuntimeException("Failed to read keystore"); + } catch (CertificateException ex) { + // This is ok as it could be a WSS4J properties file + } finally { + if (is != null) { + try { + is.close(); + } catch (IOException e) { + // Do nothing + } + } + } + } else { + return parseCertificate(certificate); } - cs = new CertificateStore(Collections.singletonList(cert).toArray(new X509Certificate[0])); - - TrustManager tm = new TrustManager(cs); - fedContext.getCertificateStores().add(tm); - } + } - fedContext.init(); - return fedContext; + return null; } private X509Certificate parseCertificate(String certificate) @@ -343,5 +346,124 @@ public class TrustedIdpOIDCProtocolHandler implements TrustedIdpProtocolHandler } } + private SamlAssertionWrapper createSamlAssertion(Idp idp, JwtToken token, + Date created, + Date expires) throws Exception { + SamlCallbackHandler callbackHandler = new SamlCallbackHandler(); + callbackHandler.setIssuer(idp.getServiceDisplayName()); + + // Subject + // TODO + SubjectBean subjectBean = + new SubjectBean((String)token.getClaim("preferred_username"), + SAML2Constants.NAMEID_FORMAT_UNSPECIFIED, + SAML2Constants.CONF_BEARER); + callbackHandler.setSubjectBean(subjectBean); + + // Conditions + ConditionsBean conditionsBean = new ConditionsBean(); + conditionsBean.setNotAfter(new DateTime(expires)); + if (token.getClaim(JwtConstants.CLAIM_NOT_BEFORE) != null) { + DateTime notBefore = new DateTime((long)token.getClaim(JwtConstants.CLAIM_NOT_BEFORE) * 1000L); + conditionsBean.setNotBefore(notBefore); + } else { + conditionsBean.setNotBefore(new DateTime()); + } + callbackHandler.setConditionsBean(conditionsBean); + + SAMLCallback samlCallback = new SAMLCallback(); + SAMLUtil.doSAMLCallback(callbackHandler, samlCallback); + + SamlAssertionWrapper assertion = new SamlAssertionWrapper(samlCallback); + + Crypto crypto = getCrypto(idp.getCertificate()); + assertion.signAssertion(crypto.getDefaultX509Identifier(), idp.getCertificatePassword(), + crypto, false); + + return assertion; + } + + private Crypto getCrypto(String certificate) throws ProcessingException { + if (certificate == null) { + return null; + } + + // First see if it's a certificate file + InputStream is = null; + try { + is = Merlin.loadInputStream(Thread.currentThread().getContextClassLoader(), certificate); + + CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); + X509Certificate cert = (X509Certificate) certFactory.generateCertificate(is); + return new CertificateStore(new X509Certificate[]{cert}); + } catch (WSSecurityException ex) { + LOG.error("Failed to load keystore " + certificate, ex); + throw new RuntimeException("Failed to load keystore " + certificate); + } catch (IOException ex) { + LOG.error("Failed to read keystore", ex); + throw new RuntimeException("Failed to read keystore"); + } catch (CertificateException ex) { + // This is ok as it could be a WSS4J properties file + } finally { + if (is != null) { + try { + is.close(); + } catch (IOException e) { + // Do nothing + } + } + } + + // Maybe it's a WSS4J properties file... + return CertsUtils.createCrypto(certificate); + } + + private static class SamlCallbackHandler implements CallbackHandler { + private ConditionsBean conditionsBean; + private SubjectBean subjectBean; + private String issuer; + + /** + * Set the SubjectBean + */ + public void setSubjectBean(SubjectBean subjectBean) { + this.subjectBean = subjectBean; + } + + /** + * Set the ConditionsBean + */ + public void setConditionsBean(ConditionsBean conditionsBean) { + this.conditionsBean = conditionsBean; + } + + /** + * Set the issuer name + */ + public void setIssuer(String issuerName) { + this.issuer = issuerName; + } + + public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { + for (Callback callback : callbacks) { + if (callback instanceof SAMLCallback) { + SAMLCallback samlCallback = (SAMLCallback) callback; + + // Set the Subject + if (subjectBean != null) { + samlCallback.setSubject(subjectBean); + } + samlCallback.setSamlVersion(Version.SAML_20); + + // Set the issuer + samlCallback.setIssuer(issuer); + + // Set the conditions + samlCallback.setConditions(conditionsBean); + } + } + } + + } } http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/a700f7ae/services/oidc/src/main/java/org/apache/cxf/fediz/service/oidc/FedizSubjectCreator.java ---------------------------------------------------------------------- diff --git a/services/oidc/src/main/java/org/apache/cxf/fediz/service/oidc/FedizSubjectCreator.java b/services/oidc/src/main/java/org/apache/cxf/fediz/service/oidc/FedizSubjectCreator.java index f134039..ac1aff6 100644 --- a/services/oidc/src/main/java/org/apache/cxf/fediz/service/oidc/FedizSubjectCreator.java +++ b/services/oidc/src/main/java/org/apache/cxf/fediz/service/oidc/FedizSubjectCreator.java @@ -75,7 +75,7 @@ public class FedizSubjectCreator implements SubjectCreator { public IdToken convertToIdToken(Element samlToken, String subjectName, ClaimCollection claims) { - // The current SAML Assertion represents anauthentication record. + // The current SAML Assertion represents an authentication record. // It has to be translated into IdToken (JWT) so that it can be returned // to client applications participating in various OIDC flows. http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/a700f7ae/systests/federation/oidc/pom.xml ---------------------------------------------------------------------- diff --git a/systests/federation/oidc/pom.xml b/systests/federation/oidc/pom.xml index 4df592a..7d11584 100644 --- a/systests/federation/oidc/pom.xml +++ b/systests/federation/oidc/pom.xml @@ -229,6 +229,7 @@ <directory>${basedir}/src/test/resources</directory> <includes> <include>entities-realma.xml</include> + <include>realmb.cert</include> </includes> <filtering>true</filtering> </resource> http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/a700f7ae/systests/federation/oidc/src/test/resources/realmb.cert ---------------------------------------------------------------------- diff --git a/systests/federation/oidc/src/test/resources/realmb.cert b/systests/federation/oidc/src/test/resources/realmb.cert new file mode 100644 index 0000000..de19105 --- /dev/null +++ b/systests/federation/oidc/src/test/resources/realmb.cert @@ -0,0 +1,3 @@ +-----BEGIN CERTIFICATE----- +MIICmzCCAYMCBgFTDfCUaDANBgkqhkiG9w0BAQsFADARMQ8wDQYDVQQDDAZyZWFsbWIwHhcNMTYwMjIzMTE0MTIzWhcNMjYwMjIzMTE0MzAzWjARMQ8wDQYDVQQDDAZyZWFsbWIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCqFm0GlXs+y+Xhc6xcSPPUwRHXF5WxYIJWcBGS2ooMKHOTnq+js2/t3GBsKuUZiEudflu3q/O5GGHoFQXrib4JGw+y+QMOJhQ8OBg+Hj7iQPNI0xOy8lxUnEblt7ksPaZE1c4dOhqWZMDMt7TSsa90g2j2B+dOtdm1EJKMT9VVF/NpFQSYu6c15VDvvVm1tz0R+/BCkfWAtvU6MDrxWbDEcVTHqpCsrzZ2+n1+IgVuULtKyLgJS5fIRiquu0B51QehMVfko15zLY6UaNvlrq5FcVBHWJrM4pZdgXujfgXOxLSbIw1bzjM0yRnlMvJ05qTTdIq/8uLcUgjZ+VGoiKLLAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAEB0UAoWnUOwGFtjyWYs00WjMpfCf00jbZRzBakqfVmJwqFzCuP1D5hArL2L3floKsp50fspzJpWytoARcT+FYr/Xju/0CDTtbMoul6p6Lc+y+YFghd7tOD0rw26lRwsuZZwycc7gqRoqycy6NzUEnHee514UgSQM8nILyZT58CUYKYOhnNdKHcthjwE7IRWGvEMWJ09Pe8OvQMKUWnMU9jpxAk6jbmInapEGoiOT8HQtnsVGruWnIpHwwl1kYJXDRfrVtSi64DtbGEOTfC/+jbWsmH4LwX1LQRsD4HrDoAZUmSn7gI3jmUxckYAuX6gv9Ve4fZbGe9s09tirGQZ5lg= +-----END CERTIFICATE-----
