You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by co...@apache.org on 2017/08/11 12:39:52 UTC
[1/5] syncope git commit: SYNCOPE-1194 - Sign the SAML SSO Service
Provider Metadata
Repository: syncope
Updated Branches:
refs/heads/2_0_X 5160df7ba -> 6b3ace024
SYNCOPE-1194 - Sign the SAML SSO Service Provider Metadata
Project: http://git-wip-us.apache.org/repos/asf/syncope/repo
Commit: http://git-wip-us.apache.org/repos/asf/syncope/commit/919584f3
Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/919584f3
Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/919584f3
Branch: refs/heads/2_0_X
Commit: 919584f3f780a54b3447dd4f397a29eea438af94
Parents: 5160df7
Author: Colm O hEigeartaigh <co...@apache.org>
Authored: Fri Aug 11 11:59:08 2017 +0100
Committer: Colm O hEigeartaigh <co...@apache.org>
Committed: Fri Aug 11 13:15:33 2017 +0100
----------------------------------------------------------------------
.../apache/syncope/core/logic/SAML2SPLogic.java | 1 +
.../core/logic/saml2/SAML2ReaderWriter.java | 3 +--
.../org/apache/syncope/fit/core/SAML2ITCase.java | 18 ++++++++++++++++++
3 files changed, 20 insertions(+), 2 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/syncope/blob/919584f3/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/SAML2SPLogic.java
----------------------------------------------------------------------
diff --git a/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/SAML2SPLogic.java b/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/SAML2SPLogic.java
index 87b7eb6..31ef8c4 100644
--- a/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/SAML2SPLogic.java
+++ b/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/SAML2SPLogic.java
@@ -200,6 +200,7 @@ public class SAML2SPLogic extends AbstractSAML2Logic<AbstractBaseBean> {
}
spEntityDescriptor.getRoleDescriptors().add(spSSODescriptor);
+ saml2rw.sign(spEntityDescriptor);
saml2rw.write(new OutputStreamWriter(os), spEntityDescriptor, true);
} catch (Exception e) {
http://git-wip-us.apache.org/repos/asf/syncope/blob/919584f3/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/saml2/SAML2ReaderWriter.java
----------------------------------------------------------------------
diff --git a/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/saml2/SAML2ReaderWriter.java b/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/saml2/SAML2ReaderWriter.java
index 62e90e7..22b0fd1 100644
--- a/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/saml2/SAML2ReaderWriter.java
+++ b/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/saml2/SAML2ReaderWriter.java
@@ -151,14 +151,13 @@ public class SAML2ReaderWriter {
return responseObject;
}
- public void sign(final RequestAbstractType request) throws SecurityException {
+ public void sign(final SignableSAMLObject signableObject) throws SecurityException {
org.opensaml.xmlsec.signature.Signature signature = OpenSAMLUtil.buildSignature();
signature.setCanonicalizationAlgorithm(SignatureConstants.ALGO_ID_C14N_EXCL_OMIT_COMMENTS);
signature.setSignatureAlgorithm(sigAlgo);
signature.setSigningCredential(loader.getCredential());
signature.setKeyInfo(keyInfoGenerator.generate(loader.getCredential()));
- SignableSAMLObject signableObject = (SignableSAMLObject) request;
signableObject.setSignature(signature);
signableObject.releaseDOM();
signableObject.releaseChildrenDOM(true);
http://git-wip-us.apache.org/repos/asf/syncope/blob/919584f3/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SAML2ITCase.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SAML2ITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SAML2ITCase.java
index 6967e73..e8a5add 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SAML2ITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SAML2ITCase.java
@@ -30,9 +30,12 @@ import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.security.KeyStore;
+import java.security.cert.X509Certificate;
import java.util.Collections;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
+import javax.xml.namespace.QName;
+
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.collections4.IterableUtils;
import org.apache.commons.collections4.Predicate;
@@ -68,6 +71,7 @@ import org.apache.wss4j.common.util.DOM2Writer;
import org.apache.wss4j.common.util.Loader;
import org.apache.wss4j.dom.WSConstants;
import org.apache.wss4j.dom.engine.WSSConfig;
+import org.apache.xml.security.signature.XMLSignature;
import org.joda.time.DateTime;
import org.junit.AfterClass;
import org.junit.Assume;
@@ -75,6 +79,7 @@ import org.junit.BeforeClass;
import org.junit.Test;
import org.opensaml.saml.common.xml.SAMLConstants;
import org.opensaml.saml.saml2.core.Status;
+import org.opensaml.xmlsec.signature.support.SignatureConstants;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
@@ -143,6 +148,19 @@ public class SAML2ITCase extends AbstractITCase {
new InputStreamReader((InputStream) response.getEntity(), StandardCharsets.UTF_8));
assertEquals("EntityDescriptor", responseDoc.getDocumentElement().getLocalName());
assertEquals("urn:oasis:names:tc:SAML:2.0:metadata", responseDoc.getDocumentElement().getNamespaceURI());
+
+ // Get the signature
+ QName signatureQName = new QName(SignatureConstants.XMLSIG_NS, "Signature");
+ Element signatureElement =
+ DOMUtils.getFirstChildWithName(responseDoc.getDocumentElement(), signatureQName);
+ assertNotNull(signatureElement);
+
+ // Validate the signature
+ XMLSignature signature = new XMLSignature(signatureElement, null);
+ KeyStore keystore = KeyStore.getInstance("JKS");
+ keystore.load(Loader.getResourceAsStream("keystore"), "changeit".toCharArray());
+ assertTrue(signature.checkSignatureValue((X509Certificate)keystore.getCertificate("sp")));
+
} catch (Exception e) {
LOG.error("During SAML 2.0 SP metadata parsing", e);
fail(e.getMessage());
[5/5] syncope git commit: SYNCOPE-1195 - Remove copy of OpenSAMLUtil
when WSS4J 2.1.11 is out
Posted by co...@apache.org.
SYNCOPE-1195 - Remove copy of OpenSAMLUtil when WSS4J 2.1.11 is out
Project: http://git-wip-us.apache.org/repos/asf/syncope/repo
Commit: http://git-wip-us.apache.org/repos/asf/syncope/commit/6b3ace02
Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/6b3ace02
Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/6b3ace02
Branch: refs/heads/2_0_X
Commit: 6b3ace024498e4d86bff1e12c782e6c55c036511
Parents: c8748ba
Author: Colm O hEigeartaigh <co...@apache.org>
Authored: Fri Aug 11 13:39:21 2017 +0100
Committer: Colm O hEigeartaigh <co...@apache.org>
Committed: Fri Aug 11 13:39:21 2017 +0100
----------------------------------------------------------------------
.../syncope/core/logic/saml2/OpenSAMLUtil.java | 141 +++++++++++++++++++
.../core/logic/saml2/SAML2ReaderWriter.java | 5 +-
2 files changed, 143 insertions(+), 3 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/syncope/blob/6b3ace02/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/saml2/OpenSAMLUtil.java
----------------------------------------------------------------------
diff --git a/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/saml2/OpenSAMLUtil.java b/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/saml2/OpenSAMLUtil.java
new file mode 100644
index 0000000..ff197d4
--- /dev/null
+++ b/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/saml2/OpenSAMLUtil.java
@@ -0,0 +1,141 @@
+/**
+ * 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.syncope.core.logic.saml2;
+
+import org.apache.wss4j.common.ext.WSSecurityException;
+import org.opensaml.core.xml.XMLObject;
+import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;
+import org.opensaml.core.xml.io.Marshaller;
+import org.opensaml.core.xml.io.MarshallerFactory;
+import org.opensaml.core.xml.io.MarshallingException;
+import org.opensaml.saml.common.SignableSAMLObject;
+import org.opensaml.xmlsec.signature.Signature;
+import org.opensaml.xmlsec.signature.support.SignatureException;
+import org.opensaml.xmlsec.signature.support.Signer;
+import org.opensaml.xmlsec.signature.support.SignerProvider;
+import org.w3c.dom.Document;
+import org.w3c.dom.DocumentFragment;
+import org.w3c.dom.Element;
+
+/**
+ * Class OpenSAMLUtil provides static helper methods for the OpenSaml library.
+ * TODO Remove once we pick up WSS4J 2.1.11 - See https://issues.apache.org/jira/browse/WSS-613
+ */
+final class OpenSAMLUtil {
+
+ private OpenSAMLUtil() {
+ // Complete
+ }
+
+ /**
+ * Convert a SAML Assertion from a XMLObject to a DOM Element
+ *
+ * @param xmlObject of type XMLObject
+ * @param doc of type Document
+ * @param signObject whether to sign the XMLObject during marshalling
+ * @return Element
+ * @throws WSSecurityException
+ */
+ public static Element toDom(
+ final XMLObject xmlObject,
+ final Document doc,
+ final boolean signObject
+ ) throws WSSecurityException {
+ MarshallerFactory marshallerFactory = XMLObjectProviderRegistrySupport.getMarshallerFactory();
+ Marshaller marshaller = marshallerFactory.getMarshaller(xmlObject);
+ Element element = null;
+ DocumentFragment frag = doc == null ? null : doc.createDocumentFragment();
+ try {
+ if (frag != null) {
+ while (doc.getFirstChild() != null) {
+ frag.appendChild(doc.removeChild(doc.getFirstChild()));
+ }
+ }
+ try {
+ if (doc == null) {
+ element = marshaller.marshall(xmlObject);
+ } else {
+ element = marshaller.marshall(xmlObject, doc);
+ }
+ } catch (MarshallingException ex) {
+ throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, ex, "empty",
+ new Object[] {"Error marshalling a SAML assertion"});
+ }
+
+ if (signObject) {
+ signXMLObject(xmlObject);
+ }
+ } finally {
+ if (frag != null) {
+ while (doc.getFirstChild() != null) {
+ doc.removeChild(doc.getFirstChild());
+ }
+ doc.appendChild(frag);
+ }
+ }
+ return element;
+ }
+
+ private static void signXMLObject(final XMLObject xmlObject) throws WSSecurityException {
+ if (xmlObject instanceof org.opensaml.saml.saml1.core.Response) {
+ org.opensaml.saml.saml1.core.Response response =
+ (org.opensaml.saml.saml1.core.Response) xmlObject;
+
+ // Sign any Assertions
+ if (response.getAssertions() != null) {
+ for (org.opensaml.saml.saml1.core.Assertion assertion : response.getAssertions()) {
+ signObject(assertion.getSignature());
+ }
+ }
+
+ signObject(response.getSignature());
+ } else if (xmlObject instanceof org.opensaml.saml.saml2.core.Response) {
+ org.opensaml.saml.saml2.core.Response response =
+ (org.opensaml.saml.saml2.core.Response) xmlObject;
+
+ // Sign any Assertions
+ if (response.getAssertions() != null) {
+ for (org.opensaml.saml.saml2.core.Assertion assertion : response.getAssertions()) {
+ signObject(assertion.getSignature());
+ }
+ }
+
+ signObject(response.getSignature());
+ } else if (xmlObject instanceof SignableSAMLObject) {
+ signObject(((SignableSAMLObject) xmlObject).getSignature());
+ }
+ }
+
+ private static void signObject(final Signature signature) throws WSSecurityException {
+ if (signature != null) {
+ ClassLoader loader = Thread.currentThread().getContextClassLoader();
+ try {
+ Thread.currentThread().setContextClassLoader(SignerProvider.class.getClassLoader());
+ Signer.signObject(signature);
+ } catch (SignatureException ex) {
+ throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, ex, "empty",
+ new Object[] {"Error signing a SAML assertion"});
+ } finally {
+ Thread.currentThread().setContextClassLoader(loader);
+ }
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/6b3ace02/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/saml2/SAML2ReaderWriter.java
----------------------------------------------------------------------
diff --git a/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/saml2/SAML2ReaderWriter.java b/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/saml2/SAML2ReaderWriter.java
index f530afb..9c1bcb2 100644
--- a/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/saml2/SAML2ReaderWriter.java
+++ b/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/saml2/SAML2ReaderWriter.java
@@ -49,7 +49,6 @@ import org.apache.syncope.common.lib.types.SAML2BindingType;
import org.apache.syncope.core.logic.init.SAML2SPLoader;
import org.apache.wss4j.common.crypto.Merlin;
import org.apache.wss4j.common.ext.WSSecurityException;
-import org.apache.wss4j.common.saml.OpenSAMLUtil;
import org.opensaml.core.xml.XMLObject;
import org.opensaml.saml.common.SignableSAMLObject;
import org.opensaml.saml.saml2.core.RequestAbstractType;
@@ -134,7 +133,7 @@ public class SAML2ReaderWriter {
// parse the provided SAML response
Document responseDoc = StaxUtils.read(new InputStreamReader(tokenStream, StandardCharsets.UTF_8));
- XMLObject responseObject = OpenSAMLUtil.fromDom(responseDoc.getDocumentElement());
+ XMLObject responseObject = org.apache.wss4j.common.saml.OpenSAMLUtil.fromDom(responseDoc.getDocumentElement());
if (LOG.isDebugEnabled()) {
try {
@@ -152,7 +151,7 @@ public class SAML2ReaderWriter {
}
public void sign(final SignableSAMLObject signableObject) throws SecurityException {
- org.opensaml.xmlsec.signature.Signature signature = OpenSAMLUtil.buildSignature();
+ org.opensaml.xmlsec.signature.Signature signature = org.apache.wss4j.common.saml.OpenSAMLUtil.buildSignature();
signature.setCanonicalizationAlgorithm(SignatureConstants.ALGO_ID_C14N_EXCL_OMIT_COMMENTS);
signature.setSignatureAlgorithm(sigAlgo);
signature.setSigningCredential(loader.getCredential());
[3/5] syncope git commit: Take the valid SAML Assertion from the
validator response instead.
Posted by co...@apache.org.
Take the valid SAML Assertion from the validator response instead.
Project: http://git-wip-us.apache.org/repos/asf/syncope/repo
Commit: http://git-wip-us.apache.org/repos/asf/syncope/commit/fecfc6ff
Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/fecfc6ff
Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/fecfc6ff
Branch: refs/heads/2_0_X
Commit: fecfc6ff37e71b5eedf7f86be429205d9dd1715e
Parents: 1d8b6c6
Author: Colm O hEigeartaigh <co...@apache.org>
Authored: Fri Aug 11 12:51:22 2017 +0100
Committer: Colm O hEigeartaigh <co...@apache.org>
Committed: Fri Aug 11 13:16:36 2017 +0100
----------------------------------------------------------------------
.../apache/syncope/core/logic/SAML2SPLogic.java | 65 ++++++++++----------
.../core/logic/saml2/SAML2ReaderWriter.java | 8 ++-
2 files changed, 39 insertions(+), 34 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/syncope/blob/fecfc6ff/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/SAML2SPLogic.java
----------------------------------------------------------------------
diff --git a/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/SAML2SPLogic.java b/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/SAML2SPLogic.java
index 31ef8c4..03576ab 100644
--- a/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/SAML2SPLogic.java
+++ b/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/SAML2SPLogic.java
@@ -37,6 +37,7 @@ import org.apache.commons.lang3.tuple.Pair;
import org.apache.commons.lang3.tuple.Triple;
import org.apache.cxf.rs.security.jose.jws.JwsJwtCompactConsumer;
import org.apache.cxf.rs.security.jose.jws.JwsSignatureVerifier;
+import org.apache.cxf.rs.security.saml.sso.SSOValidatorResponse;
import org.apache.syncope.common.lib.AbstractBaseBean;
import org.apache.syncope.common.lib.SyncopeClientException;
import org.apache.syncope.common.lib.to.AttrTO;
@@ -371,8 +372,10 @@ public class SAML2SPLogic extends AbstractSAML2Logic<AbstractBaseBean> {
if (idp.getConnObjectKeyItem() == null) {
throw new IllegalArgumentException("No mapping provided for SAML 2.0 IdP '" + idp.getId() + "'");
}
+
+ SSOValidatorResponse validatorResponse = null;
try {
- saml2rw.validate(
+ validatorResponse = saml2rw.validate(
samlResponse,
idp,
getAssertionConsumerURL(response.getSpEntityID(), response.getUrlContext()),
@@ -390,47 +393,45 @@ public class SAML2SPLogic extends AbstractSAML2Logic<AbstractBaseBean> {
responseTO.setIdp(idp.getId());
responseTO.setSloSupported(idp.getSLOLocation(idp.getBindingType()) != null);
- NameID nameID = null;
+ Assertion assertion = validatorResponse.getOpensamlAssertion();
+ NameID nameID = assertion.getSubject().getNameID();
String keyValue = null;
- for (Assertion assertion : samlResponse.getAssertions()) {
- nameID = assertion.getSubject().getNameID();
- if (StringUtils.isNotBlank(nameID.getValue())
- && idp.getConnObjectKeyItem().getExtAttrName().equals("NameID")) {
+ if (StringUtils.isNotBlank(nameID.getValue())
+ && idp.getConnObjectKeyItem().getExtAttrName().equals("NameID")) {
- keyValue = nameID.getValue();
- }
+ keyValue = nameID.getValue();
+ }
- if (assertion.getConditions().getNotOnOrAfter() != null) {
- responseTO.setNotOnOrAfter(assertion.getConditions().getNotOnOrAfter().toDate());
- }
- for (AuthnStatement authnStmt : assertion.getAuthnStatements()) {
- responseTO.setSessionIndex(authnStmt.getSessionIndex());
+ if (assertion.getConditions().getNotOnOrAfter() != null) {
+ responseTO.setNotOnOrAfter(assertion.getConditions().getNotOnOrAfter().toDate());
+ }
+ for (AuthnStatement authnStmt : assertion.getAuthnStatements()) {
+ responseTO.setSessionIndex(authnStmt.getSessionIndex());
- responseTO.setAuthInstant(authnStmt.getAuthnInstant().toDate());
- if (authnStmt.getSessionNotOnOrAfter() != null) {
- responseTO.setNotOnOrAfter(authnStmt.getSessionNotOnOrAfter().toDate());
- }
+ responseTO.setAuthInstant(authnStmt.getAuthnInstant().toDate());
+ if (authnStmt.getSessionNotOnOrAfter() != null) {
+ responseTO.setNotOnOrAfter(authnStmt.getSessionNotOnOrAfter().toDate());
}
+ }
- for (AttributeStatement attrStmt : assertion.getAttributeStatements()) {
- for (Attribute attr : attrStmt.getAttributes()) {
- if (!attr.getAttributeValues().isEmpty()) {
- String attrName = attr.getFriendlyName() == null ? attr.getName() : attr.getFriendlyName();
- if (attrName.equals(idp.getConnObjectKeyItem().getExtAttrName())
- && attr.getAttributeValues().get(0) instanceof XSString) {
+ for (AttributeStatement attrStmt : assertion.getAttributeStatements()) {
+ for (Attribute attr : attrStmt.getAttributes()) {
+ if (!attr.getAttributeValues().isEmpty()) {
+ String attrName = attr.getFriendlyName() == null ? attr.getName() : attr.getFriendlyName();
+ if (attrName.equals(idp.getConnObjectKeyItem().getExtAttrName())
+ && attr.getAttributeValues().get(0) instanceof XSString) {
- keyValue = ((XSString) attr.getAttributeValues().get(0)).getValue();
- }
+ keyValue = ((XSString) attr.getAttributeValues().get(0)).getValue();
+ }
- AttrTO attrTO = new AttrTO();
- attrTO.setSchema(attrName);
- for (XMLObject value : attr.getAttributeValues()) {
- if (value.getDOM() != null) {
- attrTO.getValues().add(value.getDOM().getTextContent());
- }
+ AttrTO attrTO = new AttrTO();
+ attrTO.setSchema(attrName);
+ for (XMLObject value : attr.getAttributeValues()) {
+ if (value.getDOM() != null) {
+ attrTO.getValues().add(value.getDOM().getTextContent());
}
- responseTO.getAttrs().add(attrTO);
}
+ responseTO.getAttrs().add(attrTO);
}
}
}
http://git-wip-us.apache.org/repos/asf/syncope/blob/fecfc6ff/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/saml2/SAML2ReaderWriter.java
----------------------------------------------------------------------
diff --git a/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/saml2/SAML2ReaderWriter.java b/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/saml2/SAML2ReaderWriter.java
index 22b0fd1..dba63cc 100644
--- a/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/saml2/SAML2ReaderWriter.java
+++ b/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/saml2/SAML2ReaderWriter.java
@@ -43,6 +43,7 @@ import javax.xml.transform.stream.StreamResult;
import org.apache.commons.codec.binary.Base64;
import org.apache.cxf.rs.security.saml.DeflateEncoderDecoder;
import org.apache.cxf.rs.security.saml.sso.SAMLProtocolResponseValidator;
+import org.apache.cxf.rs.security.saml.sso.SSOValidatorResponse;
import org.apache.cxf.staxutils.StaxUtils;
import org.apache.syncope.common.lib.SSOConstants;
import org.apache.syncope.common.lib.types.SAML2BindingType;
@@ -202,7 +203,7 @@ public class SAML2ReaderWriter {
return Base64.encodeBase64String(deflatedBytes);
}
- public void validate(
+ public SSOValidatorResponse validate(
final Response samlResponse,
final SAML2IdPEntity idp,
final String assertionConsumerURL,
@@ -224,7 +225,8 @@ public class SAML2ReaderWriter {
ssoResponseValidator.setIssuerIDP(idp.getId());
ssoResponseValidator.setRequestId(requestId);
ssoResponseValidator.setSpIdentifier(spEntityID);
- ssoResponseValidator.validateSamlResponse(samlResponse, idp.getBindingType() == SAML2BindingType.POST);
+ SSOValidatorResponse validatorResponse =
+ ssoResponseValidator.validateSamlResponse(samlResponse, idp.getBindingType() == SAML2BindingType.POST);
if (LOG.isDebugEnabled()) {
try {
@@ -237,5 +239,7 @@ public class SAML2ReaderWriter {
LOG.error("Could not log the SAML response with decrypted assertions", e);
}
}
+
+ return validatorResponse;
}
}
[4/5] syncope git commit: Temporarily adding SSOValidatorResponse
until we pick up CXF 3.1.13
Posted by co...@apache.org.
Temporarily adding SSOValidatorResponse until we pick up CXF 3.1.13
Project: http://git-wip-us.apache.org/repos/asf/syncope/repo
Commit: http://git-wip-us.apache.org/repos/asf/syncope/commit/c8748ba1
Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/c8748ba1
Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/c8748ba1
Branch: refs/heads/2_0_X
Commit: c8748ba107bdda6ae4e8a3aec6dcf4cf9e25a3f6
Parents: fecfc6f
Author: Colm O hEigeartaigh <co...@apache.org>
Authored: Fri Aug 11 13:25:57 2017 +0100
Committer: Colm O hEigeartaigh <co...@apache.org>
Committed: Fri Aug 11 13:35:24 2017 +0100
----------------------------------------------------------------------
.../apache/syncope/core/logic/SAML2SPLogic.java | 3 +-
.../core/logic/saml2/SAML2ReaderWriter.java | 1 -
.../logic/saml2/SAMLSSOResponseValidator.java | 78 +++++++++---------
.../core/logic/saml2/SSOValidatorResponse.java | 84 ++++++++++++++++++++
4 files changed, 125 insertions(+), 41 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/syncope/blob/c8748ba1/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/SAML2SPLogic.java
----------------------------------------------------------------------
diff --git a/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/SAML2SPLogic.java b/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/SAML2SPLogic.java
index 03576ab..0891f59 100644
--- a/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/SAML2SPLogic.java
+++ b/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/SAML2SPLogic.java
@@ -19,6 +19,8 @@
package org.apache.syncope.core.logic;
import org.apache.syncope.core.logic.saml2.SAML2UserManager;
+import org.apache.syncope.core.logic.saml2.SSOValidatorResponse;
+
import com.fasterxml.uuid.Generators;
import com.fasterxml.uuid.impl.RandomBasedGenerator;
import java.io.OutputStream;
@@ -37,7 +39,6 @@ import org.apache.commons.lang3.tuple.Pair;
import org.apache.commons.lang3.tuple.Triple;
import org.apache.cxf.rs.security.jose.jws.JwsJwtCompactConsumer;
import org.apache.cxf.rs.security.jose.jws.JwsSignatureVerifier;
-import org.apache.cxf.rs.security.saml.sso.SSOValidatorResponse;
import org.apache.syncope.common.lib.AbstractBaseBean;
import org.apache.syncope.common.lib.SyncopeClientException;
import org.apache.syncope.common.lib.to.AttrTO;
http://git-wip-us.apache.org/repos/asf/syncope/blob/c8748ba1/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/saml2/SAML2ReaderWriter.java
----------------------------------------------------------------------
diff --git a/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/saml2/SAML2ReaderWriter.java b/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/saml2/SAML2ReaderWriter.java
index dba63cc..f530afb 100644
--- a/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/saml2/SAML2ReaderWriter.java
+++ b/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/saml2/SAML2ReaderWriter.java
@@ -43,7 +43,6 @@ import javax.xml.transform.stream.StreamResult;
import org.apache.commons.codec.binary.Base64;
import org.apache.cxf.rs.security.saml.DeflateEncoderDecoder;
import org.apache.cxf.rs.security.saml.sso.SAMLProtocolResponseValidator;
-import org.apache.cxf.rs.security.saml.sso.SSOValidatorResponse;
import org.apache.cxf.staxutils.StaxUtils;
import org.apache.syncope.common.lib.SSOConstants;
import org.apache.syncope.common.lib.types.SAML2BindingType;
http://git-wip-us.apache.org/repos/asf/syncope/blob/c8748ba1/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/saml2/SAMLSSOResponseValidator.java
----------------------------------------------------------------------
diff --git a/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/saml2/SAMLSSOResponseValidator.java b/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/saml2/SAMLSSOResponseValidator.java
index a730140..a32ed09 100644
--- a/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/saml2/SAMLSSOResponseValidator.java
+++ b/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/saml2/SAMLSSOResponseValidator.java
@@ -25,7 +25,6 @@ import java.util.logging.Logger;
import org.w3c.dom.Element;
import org.apache.cxf.common.logging.LogUtils;
-import org.apache.cxf.rs.security.saml.sso.SSOValidatorResponse;
import org.apache.cxf.rs.security.saml.sso.TokenReplayCache;
import org.apache.wss4j.common.ext.WSSecurityException;
import org.apache.wss4j.common.saml.builder.SAML2Constants;
@@ -39,9 +38,9 @@ import org.opensaml.saml.saml2.core.AuthnStatement;
*/
//CHECKSTYLE:OFF
public class SAMLSSOResponseValidator {
-
+
private static final Logger LOG = LogUtils.getL7dLogger(SAMLSSOResponseValidator.class);
-
+
private String issuerIDP;
private String assertionConsumerURL;
private String clientAddress;
@@ -51,7 +50,7 @@ public class SAMLSSOResponseValidator {
private boolean enforceAssertionsSigned = true;
private boolean enforceKnownIssuer = true;
private TokenReplayCache<String> replayCache;
-
+
/**
* Enforce that Assertions contained in the Response must be signed (if the Response itself is not
* signed). The default is true.
@@ -59,14 +58,14 @@ public class SAMLSSOResponseValidator {
public void setEnforceAssertionsSigned(boolean enforceAssertionsSigned) {
this.enforceAssertionsSigned = enforceAssertionsSigned;
}
-
+
/**
* Enforce that the Issuer of the received Response/Assertion is known. The default is true.
*/
public void setEnforceKnownIssuer(boolean enforceKnownIssuer) {
this.enforceKnownIssuer = enforceKnownIssuer;
}
-
+
/**
* Validate a SAML 2 Protocol Response
* @param samlResponse
@@ -86,7 +85,7 @@ public class SAMLSSOResponseValidator {
LOG.fine("The Response must contain at least one Assertion");
throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "invalidSAMLsecurity");
}
-
+
// The Response must contain a Destination that matches the assertionConsumerURL if it is
// signed
String destination = samlResponse.getDestination();
@@ -95,12 +94,12 @@ public class SAMLSSOResponseValidator {
LOG.fine("The Response must contain a destination that matches the assertion consumer URL");
throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "invalidSAMLsecurity");
}
-
+
if (enforceResponseSigned && !samlResponse.isSigned()) {
LOG.fine("The Response must be signed!");
throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "invalidSAMLsecurity");
}
-
+
// Validate Assertions
org.opensaml.saml.saml2.core.Assertion validAssertion = null;
Date sessionNotOnOrAfter = null;
@@ -111,17 +110,17 @@ public class SAMLSSOResponseValidator {
throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "invalidSAMLsecurity");
}
validateIssuer(assertion.getIssuer());
-
+
if (!samlResponse.isSigned() && enforceAssertionsSigned && assertion.getSignature() == null) {
LOG.fine("The enclosed assertions in the SAML Response must be signed");
throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "invalidSAMLsecurity");
}
-
+
// Check for AuthnStatements and validate the Subject accordingly
if (assertion.getAuthnStatements() != null
&& !assertion.getAuthnStatements().isEmpty()) {
org.opensaml.saml.saml2.core.Subject subject = assertion.getSubject();
- org.opensaml.saml.saml2.core.SubjectConfirmation subjectConf =
+ org.opensaml.saml.saml2.core.SubjectConfirmation subjectConf =
validateAuthenticationSubject(subject, assertion.getID(), postBinding);
if (subjectConf != null) {
validateAudienceRestrictionCondition(assertion.getConditions());
@@ -139,28 +138,29 @@ public class SAMLSSOResponseValidator {
}
}
}
-
+
if (validAssertion == null) {
LOG.fine("The Response did not contain any Authentication Statement that matched "
+ "the Subject Confirmation criteria");
throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "invalidSAMLsecurity");
}
-
+
SSOValidatorResponse validatorResponse = new SSOValidatorResponse();
validatorResponse.setResponseId(samlResponse.getID());
validatorResponse.setSessionNotOnOrAfter(sessionNotOnOrAfter);
+ validatorResponse.setOpensamlAssertion(validAssertion);
if (samlResponse.getIssueInstant() != null) {
validatorResponse.setCreated(samlResponse.getIssueInstant().toDate());
}
-
+
Element assertionElement = validAssertion.getDOM();
Element clonedAssertionElement = (Element)assertionElement.cloneNode(true);
validatorResponse.setAssertionElement(clonedAssertionElement);
validatorResponse.setAssertion(DOM2Writer.nodeToString(clonedAssertionElement));
-
+
return validatorResponse;
}
-
+
/**
* Validate the Issuer (if it exists)
*/
@@ -168,23 +168,23 @@ public class SAMLSSOResponseValidator {
if (issuer == null) {
return;
}
-
+
// Issuer value must match (be contained in) Issuer IDP
if (enforceKnownIssuer && !issuerIDP.startsWith(issuer.getValue())) {
- LOG.fine("Issuer value: " + issuer.getValue() + " does not match issuer IDP: "
+ LOG.fine("Issuer value: " + issuer.getValue() + " does not match issuer IDP: "
+ issuerIDP);
throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "invalidSAMLsecurity");
}
-
+
// Format must be nameid-format-entity
if (issuer.getFormat() != null
&& !SAML2Constants.NAMEID_FORMAT_ENTITY.equals(issuer.getFormat())) {
- LOG.fine("Issuer format is not null and does not equal: "
+ LOG.fine("Issuer format is not null and does not equal: "
+ SAML2Constants.NAMEID_FORMAT_ENTITY);
throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "invalidSAMLsecurity");
}
}
-
+
/**
* Validate the Subject (of an Authentication Statement).
*/
@@ -194,20 +194,20 @@ public class SAMLSSOResponseValidator {
if (subject.getSubjectConfirmations() == null) {
return null;
}
-
+
org.opensaml.saml.saml2.core.SubjectConfirmation validSubjectConf = null;
// We need to find a Bearer Subject Confirmation method
- for (org.opensaml.saml.saml2.core.SubjectConfirmation subjectConf
+ for (org.opensaml.saml.saml2.core.SubjectConfirmation subjectConf
: subject.getSubjectConfirmations()) {
if (SAML2Constants.CONF_BEARER.equals(subjectConf.getMethod())) {
validateSubjectConfirmation(subjectConf.getSubjectConfirmationData(), id, postBinding);
validSubjectConf = subjectConf;
}
}
-
+
return validSubjectConf;
}
-
+
/**
* Validate a (Bearer) Subject Confirmation
*/
@@ -218,7 +218,7 @@ public class SAMLSSOResponseValidator {
LOG.fine("Subject Confirmation Data of a Bearer Subject Confirmation is null");
throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "invalidSAMLsecurity");
}
-
+
// Recipient must match assertion consumer URL
String recipient = subjectConfData.getRecipient();
if (recipient == null || !recipient.equals(assertionConsumerURL)) {
@@ -226,14 +226,14 @@ public class SAMLSSOResponseValidator {
+ assertionConsumerURL);
throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "invalidSAMLsecurity");
}
-
+
// We must have a NotOnOrAfter timestamp
if (subjectConfData.getNotOnOrAfter() == null
|| subjectConfData.getNotOnOrAfter().isBeforeNow()) {
LOG.fine("Subject Conf Data does not contain NotOnOrAfter or it has expired");
throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "invalidSAMLsecurity");
}
-
+
// Need to keep bearer assertion IDs based on NotOnOrAfter to detect replay attacks
if (postBinding && replayCache != null) {
if (replayCache.getId(id) == null) {
@@ -246,7 +246,7 @@ public class SAMLSSOResponseValidator {
throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "invalidSAMLsecurity");
}
}
-
+
// Check address
if (subjectConfData.getAddress() != null && clientAddress != null
&& !subjectConfData.getAddress().equals(clientAddress)) {
@@ -254,13 +254,13 @@ public class SAMLSSOResponseValidator {
+ " client address " + clientAddress);
throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "invalidSAMLsecurity");
}
-
+
// It must not contain a NotBefore timestamp
if (subjectConfData.getNotBefore() != null) {
LOG.fine("The Subject Conf Data must not contain a NotBefore timestamp");
throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "invalidSAMLsecurity");
}
-
+
// InResponseTo must match the AuthnRequest request Id
if (requestId != null && !requestId.equals(subjectConfData.getInResponseTo())) {
LOG.fine("The InResponseTo String does match the original request id " + requestId);
@@ -269,9 +269,9 @@ public class SAMLSSOResponseValidator {
LOG.fine("No InResponseTo String is allowed for the unsolicted case");
throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "invalidSAMLsecurity");
}
-
+
}
-
+
private void validateAudienceRestrictionCondition(
org.opensaml.saml.saml2.core.Conditions conditions
) throws WSSecurityException {
@@ -281,13 +281,13 @@ public class SAMLSSOResponseValidator {
}
List<AudienceRestriction> audienceRestrs = conditions.getAudienceRestrictions();
if (!matchSaml2AudienceRestriction(spIdentifier, audienceRestrs)) {
- LOG.fine("Assertion does not contain unique subject provider identifier "
+ LOG.fine("Assertion does not contain unique subject provider identifier "
+ spIdentifier + " in the audience restriction conditions");
throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "invalidSAMLsecurity");
}
}
-
-
+
+
private boolean matchSaml2AudienceRestriction(
String appliesTo, List<AudienceRestriction> audienceRestrictions
) {
@@ -352,7 +352,7 @@ public class SAMLSSOResponseValidator {
public void setSpIdentifier(String spIdentifier) {
this.spIdentifier = spIdentifier;
}
-
+
public void setReplayCache(TokenReplayCache<String> replayCache) {
this.replayCache = replayCache;
}
@@ -367,5 +367,5 @@ public class SAMLSSOResponseValidator {
public void setEnforceResponseSigned(boolean enforceResponseSigned) {
this.enforceResponseSigned = enforceResponseSigned;
}
-
+
}
http://git-wip-us.apache.org/repos/asf/syncope/blob/c8748ba1/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/saml2/SSOValidatorResponse.java
----------------------------------------------------------------------
diff --git a/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/saml2/SSOValidatorResponse.java b/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/saml2/SSOValidatorResponse.java
new file mode 100644
index 0000000..5eadaef
--- /dev/null
+++ b/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/saml2/SSOValidatorResponse.java
@@ -0,0 +1,84 @@
+/**
+ * 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.syncope.core.logic.saml2;
+
+import java.util.Date;
+
+import org.w3c.dom.Element;
+import org.opensaml.saml.saml2.core.Assertion;
+
+/**
+ * Some information that encapsulates a successful validation by the SAMLSSOResponseValidator
+ */
+public class SSOValidatorResponse {
+ private Date sessionNotOnOrAfter;
+ private Date created;
+ private String responseId;
+ private String assertion;
+ private Element assertionElement;
+ private Assertion opensamlAssertion;
+
+ public String getAssertion() {
+ return assertion;
+ }
+
+ public void setAssertion(final String assertion) {
+ this.assertion = assertion;
+ }
+
+ public Date getSessionNotOnOrAfter() {
+ return sessionNotOnOrAfter;
+ }
+
+ public void setSessionNotOnOrAfter(final Date sessionNotOnOrAfter) {
+ this.sessionNotOnOrAfter = sessionNotOnOrAfter;
+ }
+
+ public String getResponseId() {
+ return responseId;
+ }
+
+ public void setResponseId(final String responseId) {
+ this.responseId = responseId;
+ }
+
+ public Element getAssertionElement() {
+ return assertionElement;
+ }
+
+ public void setAssertionElement(final Element assertionElement) {
+ this.assertionElement = assertionElement;
+ }
+
+ public Date getCreated() {
+ return created;
+ }
+
+ public void setCreated(final Date created) {
+ this.created = created;
+ }
+
+ public Assertion getOpensamlAssertion() {
+ return opensamlAssertion;
+ }
+
+ public void setOpensamlAssertion(final Assertion opensamlAssertion) {
+ this.opensamlAssertion = opensamlAssertion;
+ }
+}
[2/5] syncope git commit: Dynamically generate a keypair for use in
the SAML signing tests
Posted by co...@apache.org.
Dynamically generate a keypair for use in the SAML signing tests
Project: http://git-wip-us.apache.org/repos/asf/syncope/repo
Commit: http://git-wip-us.apache.org/repos/asf/syncope/commit/1d8b6c62
Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/1d8b6c62
Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/1d8b6c62
Branch: refs/heads/2_0_X
Commit: 1d8b6c62110564b57eb615b405346f1c978ee65e
Parents: 919584f
Author: Colm O hEigeartaigh <co...@apache.org>
Authored: Fri Aug 11 12:38:06 2017 +0100
Committer: Colm O hEigeartaigh <co...@apache.org>
Committed: Fri Aug 11 13:16:06 2017 +0100
----------------------------------------------------------------------
fit/core-reference/pom.xml | 7 ++
.../apache/syncope/fit/core/SAML2ITCase.java | 104 +++++++++++++++++--
fit/core-reference/src/test/resources/fediz.xml | 14 +--
pom.xml | 2 +
4 files changed, 108 insertions(+), 19 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/syncope/blob/1d8b6c62/fit/core-reference/pom.xml
----------------------------------------------------------------------
diff --git a/fit/core-reference/pom.xml b/fit/core-reference/pom.xml
index de491a3..d28eb06 100644
--- a/fit/core-reference/pom.xml
+++ b/fit/core-reference/pom.xml
@@ -176,6 +176,13 @@ under the License.
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcpkix-jdk15on</artifactId>
+ <version>${bouncycastle.version}</version>
+ <scope>test</scope>
+ </dependency>
+
</dependencies>
<build>
http://git-wip-us.apache.org/repos/asf/syncope/blob/1d8b6c62/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SAML2ITCase.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SAML2ITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SAML2ITCase.java
index e8a5add..4ae8c8f 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SAML2ITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SAML2ITCase.java
@@ -26,12 +26,23 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import java.io.File;
import java.io.InputStream;
import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
import java.security.KeyStore;
+import java.security.SecureRandom;
+import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.Collections;
+import java.util.Date;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.xml.namespace.QName;
@@ -72,6 +83,13 @@ import org.apache.wss4j.common.util.Loader;
import org.apache.wss4j.dom.WSConstants;
import org.apache.wss4j.dom.engine.WSSConfig;
import org.apache.xml.security.signature.XMLSignature;
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.asn1.x500.style.RFC4519Style;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.cert.X509v3CertificateBuilder;
+import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
+import org.bouncycastle.operator.ContentSigner;
+import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.joda.time.DateTime;
import org.junit.AfterClass;
import org.junit.Assume;
@@ -86,6 +104,8 @@ import org.w3c.dom.Element;
public class SAML2ITCase extends AbstractITCase {
private static SyncopeClient anonymous;
+ private static Path keystorePath;
+ private static Path truststorePath;
@BeforeClass
public static void setup() {
@@ -98,13 +118,17 @@ public class SAML2ITCase extends AbstractITCase {
}
@BeforeClass
- public static void importFromIdPMetadata() {
+ public static void importFromIdPMetadata() throws Exception {
if (!SAML2SPDetector.isSAML2SPAvailable()) {
return;
}
assertTrue(saml2IdPService.list().isEmpty());
+ createKeystores();
+
+ updateMetadataWithCert();
+
WebClient.client(saml2IdPService).
accept(MediaType.APPLICATION_XML_TYPE).
type(MediaType.APPLICATION_XML_TYPE);
@@ -124,7 +148,7 @@ public class SAML2ITCase extends AbstractITCase {
}
@AfterClass
- public static void clearIdPs() {
+ public static void clearIdPs() throws Exception {
if (!SAML2SPDetector.isSAML2SPAvailable()) {
return;
}
@@ -132,6 +156,9 @@ public class SAML2ITCase extends AbstractITCase {
for (SAML2IdPTO idp : saml2IdPService.list()) {
saml2IdPService.delete(idp.getKey());
}
+
+ Files.delete(keystorePath);
+ Files.delete(truststorePath);
}
@Test
@@ -411,16 +438,81 @@ public class SAML2ITCase extends AbstractITCase {
if (signAssertion) {
Crypto issuerCrypto = new Merlin();
KeyStore keyStore = KeyStore.getInstance("JKS");
- ClassLoader loader = Loader.getClassLoader(getClass());
- InputStream input = Merlin.loadInputStream(loader, "keystore");
- keyStore.load(input, "changeit".toCharArray());
+ InputStream input = Files.newInputStream(keystorePath);
+ keyStore.load(input, "security".toCharArray());
((Merlin) issuerCrypto).setKeyStore(keyStore);
- assertion.signAssertion("sp", "changeit", issuerCrypto, false);
+ assertion.signAssertion("subject", "security", issuerCrypto, false);
}
response.getAssertions().add(assertion.getSaml2());
return response;
}
+
+ private static void createKeystores() throws Exception {
+ // Create KeyPair
+ KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
+ keyPairGenerator.initialize(1024, new SecureRandom());
+ KeyPair keyPair = keyPairGenerator.generateKeyPair();
+
+ Date currentDate = new Date();
+ Date expiryDate = new Date(currentDate.getTime() + 365L * 24L * 60L * 60L * 1000L);
+
+ // Create X509Certificate
+ String issuerName = "CN=Issuer";
+ String subjectName = "CN=Subject";
+ BigInteger serial = new BigInteger("123456");
+ X509v3CertificateBuilder certBuilder =
+ new X509v3CertificateBuilder(new X500Name(RFC4519Style.INSTANCE, issuerName), serial, currentDate, expiryDate,
+ new X500Name(RFC4519Style.INSTANCE, subjectName),
+ SubjectPublicKeyInfo.getInstance(keyPair.getPublic().getEncoded()));
+ ContentSigner contentSigner = new JcaContentSignerBuilder("SHA256WithRSAEncryption").build(keyPair.getPrivate());
+ X509Certificate certificate = new JcaX509CertificateConverter().getCertificate(certBuilder.build(contentSigner));
+
+ // Store Private Key + Certificate in Keystore
+ KeyStore keystore = KeyStore.getInstance("JKS");
+ keystore.load(null, "security".toCharArray());
+ keystore.setKeyEntry("subject", keyPair.getPrivate(), "security".toCharArray(), new Certificate[] {certificate});
+
+ File keystoreFile = File.createTempFile("samlkeystore", ".jks");
+ try (OutputStream output = Files.newOutputStream(keystoreFile.toPath())) {
+ keystore.store(output, "security".toCharArray());
+ }
+ keystorePath = keystoreFile.toPath();
+
+ // Now store the Certificate in the truststore
+ KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
+ trustStore.load(null, "security".toCharArray());
+
+ trustStore.setCertificateEntry("subject", certificate);
+
+ File truststoreFile = File.createTempFile("samltruststore", ".jks");
+ try (OutputStream output = Files.newOutputStream(truststoreFile.toPath())) {
+ trustStore.store(output, "security".toCharArray());
+ }
+ truststorePath = truststoreFile.toPath();
+ }
+
+ private static void updateMetadataWithCert() throws Exception {
+ // Get encoded truststore cert
+ KeyStore keyStore = KeyStore.getInstance("JKS");
+ InputStream input = Files.newInputStream(truststorePath);
+ keyStore.load(input, "security".toCharArray());
+ X509Certificate cert = (X509Certificate)keyStore.getCertificate("subject");
+ String certEncoded = java.util.Base64.getMimeEncoder().encodeToString(cert.getEncoded());
+
+ // Replace the "cert-placeholder" string in the metadata with the actual cert
+ String basedir = System.getProperty("basedir");
+ if (basedir == null) {
+ basedir = new File(".").getCanonicalPath();
+ }
+ Path path = FileSystems.getDefault().getPath(basedir, "/src/test/resources/fediz.xml");
+ String content = new String(Files.readAllBytes(path), StandardCharsets.UTF_8);
+ content = content.replaceAll("cert-placeholder", certEncoded);
+
+ Path path2 = FileSystems.getDefault().getPath(basedir, "/target/test-classes/fediz.xml");
+ Files.write(path2, content.getBytes());
+ }
+
}
http://git-wip-us.apache.org/repos/asf/syncope/blob/1d8b6c62/fit/core-reference/src/test/resources/fediz.xml
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/resources/fediz.xml b/fit/core-reference/src/test/resources/fediz.xml
index cbc8faa..b8cbda0 100644
--- a/fit/core-reference/src/test/resources/fediz.xml
+++ b/fit/core-reference/src/test/resources/fediz.xml
@@ -23,19 +23,7 @@ under the License.
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:X509Data>
<ds:X509Certificate>
-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==
+cert-placeholder
</ds:X509Certificate>
</ds:X509Data>
</ds:KeyInfo>
http://git-wip-us.apache.org/repos/asf/syncope/blob/1d8b6c62/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index e29b8a4..004a8b5 100644
--- a/pom.xml
+++ b/pom.xml
@@ -356,6 +356,8 @@ under the License.
<properties>
<syncope.version>${project.version}</syncope.version>
+ <bouncycastle.version>1.57</bouncycastle.version>
+
<connid.version>1.4.3.0</connid.version>
<connid.soap.version>1.4.1</connid.soap.version>
<connid.rest.version>1.0.1</connid.rest.version>