From 7a31734a1f47219d2702766b5d5032a66229a791 Mon Sep 17 00:00:00 2001 From: Jerker Klang Date: Tue, 9 Feb 2016 13:14:05 +0100 Subject: [PATCH 1/6] Changed opensaml dependency --- ivy.xml | 2 +- pom.xml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ivy.xml b/ivy.xml index 4fc432d..ef31f6e 100644 --- a/ivy.xml +++ b/ivy.xml @@ -1,6 +1,6 @@ - + diff --git a/pom.xml b/pom.xml index d6b07d8..45e9f41 100644 --- a/pom.xml +++ b/pom.xml @@ -10,8 +10,8 @@ org.opensaml - opensaml - 2.6.4 + opensaml-saml-impl + 3.1.1 \ No newline at end of file From f20f049b54e067309282737cecc1eb0f8dc4b84b Mon Sep 17 00:00:00 2001 From: Jerker Klang Date: Tue, 9 Feb 2016 13:41:42 +0100 Subject: [PATCH 2/6] Converted implementation to OpenSAML V3 --- .../java/com/lastpass/saml/IdPConfig.java | 42 +++--- .../java/com/lastpass/saml/SAMLClient.java | 133 ++++++++---------- src/main/java/com/lastpass/saml/SAMLInit.java | 8 +- src/main/java/com/lastpass/saml/SPConfig.java | 28 ++-- 4 files changed, 92 insertions(+), 119 deletions(-) diff --git a/src/main/java/com/lastpass/saml/IdPConfig.java b/src/main/java/com/lastpass/saml/IdPConfig.java index 65d450b..4e72197 100644 --- a/src/main/java/com/lastpass/saml/IdPConfig.java +++ b/src/main/java/com/lastpass/saml/IdPConfig.java @@ -26,23 +26,21 @@ import java.io.FileInputStream; import java.io.InputStream; -import org.opensaml.Configuration; -import org.opensaml.xml.parse.BasicParserPool; -import org.opensaml.xml.io.UnmarshallerFactory; -import org.opensaml.saml2.metadata.EntityDescriptor; -import org.opensaml.saml2.metadata.IDPSSODescriptor; -import org.opensaml.saml2.metadata.SingleSignOnService; -import org.opensaml.saml2.metadata.KeyDescriptor; -import org.opensaml.xml.signature.KeyInfo; -import org.opensaml.xml.signature.X509Data; -import org.opensaml.xml.signature.X509Certificate; -import org.opensaml.xml.security.credential.UsageType; -import org.opensaml.common.xml.SAMLConstants; - import javax.xml.bind.DatatypeConverter; +import net.shibboleth.utilities.java.support.xml.BasicParserPool; +import net.shibboleth.utilities.java.support.xml.XMLParserException; +import org.opensaml.core.xml.io.UnmarshallingException; +import org.opensaml.core.xml.util.XMLObjectSupport; +import org.opensaml.saml.common.xml.SAMLConstants; +import org.opensaml.saml.saml2.metadata.EntityDescriptor; +import org.opensaml.saml.saml2.metadata.IDPSSODescriptor; +import org.opensaml.saml.saml2.metadata.KeyDescriptor; +import org.opensaml.saml.saml2.metadata.SingleSignOnService; +import org.opensaml.security.credential.UsageType; +import org.opensaml.xmlsec.signature.KeyInfo; +import org.opensaml.xmlsec.signature.X509Certificate; +import org.opensaml.xmlsec.signature.X509Data; -import org.w3c.dom.Document; -import org.w3c.dom.Element; /** @@ -114,20 +112,12 @@ private void init(InputStream inputStream) EntityDescriptor edesc; try { - Document doc = parsers.parse(inputStream); - Element root = doc.getDocumentElement(); - - UnmarshallerFactory unmarshallerFactory = - Configuration.getUnmarshallerFactory(); - - edesc = (EntityDescriptor) unmarshallerFactory - .getUnmarshaller(root) - .unmarshall(root); + edesc = (EntityDescriptor) XMLObjectSupport.unmarshallFromInputStream(parsers, inputStream); } - catch (org.opensaml.xml.parse.XMLParserException e) { + catch (XMLParserException e) { throw new SAMLException(e); } - catch (org.opensaml.xml.io.UnmarshallingException e) { + catch (UnmarshallingException e) { throw new SAMLException(e); } diff --git a/src/main/java/com/lastpass/saml/SAMLClient.java b/src/main/java/com/lastpass/saml/SAMLClient.java index 3991722..a628429 100644 --- a/src/main/java/com/lastpass/saml/SAMLClient.java +++ b/src/main/java/com/lastpass/saml/SAMLClient.java @@ -17,41 +17,11 @@ */ package com.lastpass.saml; -import org.opensaml.Configuration; -import org.opensaml.saml2.core.Response; -import org.opensaml.saml2.core.Subject; -import org.opensaml.saml2.core.Conditions; -import org.opensaml.saml2.core.AuthnStatement; -import org.opensaml.saml2.core.AuthnRequest; -import org.opensaml.saml2.core.Assertion; -import org.opensaml.saml2.core.Issuer; -import org.opensaml.saml2.core.Audience; -import org.opensaml.saml2.core.AudienceRestriction; -import org.opensaml.saml2.core.StatusCode; -import org.opensaml.saml2.core.SubjectConfirmation; -import org.opensaml.saml2.core.SubjectConfirmationData; - -import org.opensaml.saml2.core.AttributeStatement; -import org.opensaml.saml2.core.Attribute; - -import org.opensaml.common.SAMLObjectBuilder; - -import org.opensaml.xml.parse.BasicParserPool; -import org.opensaml.xml.io.MarshallingException; -import org.opensaml.xml.security.credential.BasicCredential; -import org.opensaml.xml.signature.SignatureValidator; -import org.opensaml.xml.signature.Signature; -import org.opensaml.xml.validation.ValidationException; -import org.opensaml.xml.XMLObjectBuilderFactory; -import org.opensaml.xml.XMLObject; + import org.joda.time.DateTime; -import org.w3c.dom.Document; import org.w3c.dom.Element; -import org.w3c.dom.ls.DOMImplementationLS; -import org.w3c.dom.ls.LSSerializer; -import org.xml.sax.InputSource; import java.io.StringReader; import java.io.ByteArrayOutputStream; import java.io.UnsupportedEncodingException; @@ -63,6 +33,33 @@ import java.util.zip.Deflater; import javax.xml.bind.DatatypeConverter; +import javax.xml.bind.ValidationException; +import net.shibboleth.utilities.java.support.xml.BasicParserPool; +import net.shibboleth.utilities.java.support.xml.SerializeSupport; +import net.shibboleth.utilities.java.support.xml.XMLParserException; +import org.opensaml.core.xml.XMLObject; +import org.opensaml.core.xml.io.MarshallingException; +import org.opensaml.core.xml.io.UnmarshallingException; +import org.opensaml.core.xml.util.XMLObjectSupport; +import org.opensaml.saml.common.SAMLObjectBuilder; +import org.opensaml.saml.saml2.core.Assertion; +import org.opensaml.saml.saml2.core.Attribute; +import org.opensaml.saml.saml2.core.AttributeStatement; +import org.opensaml.saml.saml2.core.Audience; +import org.opensaml.saml.saml2.core.AudienceRestriction; +import org.opensaml.saml.saml2.core.AuthnRequest; +import org.opensaml.saml.saml2.core.AuthnStatement; +import org.opensaml.saml.saml2.core.Conditions; +import org.opensaml.saml.saml2.core.Issuer; +import org.opensaml.saml.saml2.core.Response; +import org.opensaml.saml.saml2.core.StatusCode; +import org.opensaml.saml.saml2.core.Subject; +import org.opensaml.saml.saml2.core.SubjectConfirmation; +import org.opensaml.saml.saml2.core.SubjectConfirmationData; +import org.opensaml.security.credential.BasicCredential; +import org.opensaml.xmlsec.signature.Signature; +import org.opensaml.xmlsec.signature.support.SignatureException; +import org.opensaml.xmlsec.signature.support.SignatureValidator; /** @@ -90,12 +87,13 @@ public class SAMLClient { private SPConfig spConfig; private IdPConfig idpConfig; - private SignatureValidator sigValidator; private BasicParserPool parsers; + private final BasicCredential cred; /* do date comparisons +/- this many seconds */ private static final int slack = 300; + /** * Create a new SAMLClient, using the IdPConfig for * endpoints and validation. @@ -106,11 +104,8 @@ public SAMLClient(SPConfig spConfig, IdPConfig idpConfig) this.spConfig = spConfig; this.idpConfig = idpConfig; - BasicCredential cred = new BasicCredential(); - cred.setEntityId(idpConfig.getEntityId()); - cred.setPublicKey(idpConfig.getCert().getPublicKey()); - - sigValidator = new SignatureValidator(cred); + cred = new BasicCredential(idpConfig.getCert().getPublicKey()); + cred.setEntityId(idpConfig.getEntityId()); // create xml parsers parsers = new BasicParserPool(); @@ -141,26 +136,18 @@ private Response parseResponse(String authnResponse) throws SAMLException { try { - Document doc = parsers.getBuilder() - .parse(new InputSource(new StringReader(authnResponse))); + XMLObject obj + = XMLObjectSupport. + unmarshallFromReader(parsers, new StringReader(authnResponse)); - Element root = doc.getDocumentElement(); - return (Response) Configuration.getUnmarshallerFactory() - .getUnmarshaller(root) - .unmarshall(root); - } - catch (org.opensaml.xml.parse.XMLParserException e) { - throw new SAMLException(e); - } - catch (org.opensaml.xml.io.UnmarshallingException e) { - throw new SAMLException(e); + return (Response) obj; } - catch (org.xml.sax.SAXException e) { + catch (XMLParserException e) { throw new SAMLException(e); } - catch (java.io.IOException e) { + catch (UnmarshallingException e) { throw new SAMLException(e); - } + } } private void validate(Response response) @@ -168,13 +155,19 @@ private void validate(Response response) { // response signature must match IdP's key, if present Signature sig = response.getSignature(); - if (sig != null) - sigValidator.validate(sig); + if (sig != null) + { + try { + SignatureValidator.validate(sig, cred); + } catch (SignatureException ex) { + throw new ValidationException("Signature validation failed", ex); + } + } // response must be successful if (response.getStatus() == null || response.getStatus().getStatusCode() == null || - !(StatusCode.SUCCESS_URI + !(StatusCode.SUCCESS .equals(response.getStatus().getStatusCode().getValue()))) { throw new ValidationException( "Response has an unsuccessful status code"); @@ -208,7 +201,11 @@ private void validate(Response response) "Assertion must be signed"); sig = assertion.getSignature(); - sigValidator.validate(sig); + try { + SignatureValidator.validate(sig, cred); + } catch (SignatureException e) { + throw new ValidationException("Assertion signature validation failed", e); + } // Assertion must contain an authnstatement // with an unexpired session @@ -323,15 +320,12 @@ private void validate(Response response) private String createAuthnRequest(String requestId) throws SAMLException { - XMLObjectBuilderFactory builderFactory = Configuration.getBuilderFactory(); - SAMLObjectBuilder builder = - (SAMLObjectBuilder) builderFactory - .getBuilder(AuthnRequest.DEFAULT_ELEMENT_NAME); + (SAMLObjectBuilder) + XMLObjectSupport.getBuilder(AuthnRequest.DEFAULT_ELEMENT_NAME); SAMLObjectBuilder issuerBuilder = - (SAMLObjectBuilder) builderFactory - .getBuilder(Issuer.DEFAULT_ELEMENT_NAME); + (SAMLObjectBuilder) XMLObjectSupport.getBuilder(Issuer.DEFAULT_ELEMENT_NAME); AuthnRequest request = builder.buildObject(); request.setAssertionConsumerServiceURL(spConfig.getAcs().toString()); @@ -344,18 +338,9 @@ private String createAuthnRequest(String requestId) request.setIssuer(issuer); try { - // samlobject to xml dom object - Element elem = Configuration.getMarshallerFactory() - .getMarshaller(request) - .marshall(request); - - // and to a string... - Document document = elem.getOwnerDocument(); - DOMImplementationLS domImplLS = (DOMImplementationLS) document - .getImplementation(); - LSSerializer serializer = domImplLS.createLSSerializer(); - serializer.getDomConfig().setParameter("xml-declaration", false); - return serializer.writeToString(elem); + Element element = XMLObjectSupport.marshall(request); + + return SerializeSupport.nodeToString(element); } catch (MarshallingException e) { throw new SAMLException(e); diff --git a/src/main/java/com/lastpass/saml/SAMLInit.java b/src/main/java/com/lastpass/saml/SAMLInit.java index 518671f..54121f3 100644 --- a/src/main/java/com/lastpass/saml/SAMLInit.java +++ b/src/main/java/com/lastpass/saml/SAMLInit.java @@ -17,8 +17,8 @@ */ package com.lastpass.saml; -import org.opensaml.DefaultBootstrap; -import org.opensaml.xml.ConfigurationException; +import org.opensaml.core.config.InitializationException; +import org.opensaml.core.config.InitializationService; /** * Library initialization routines. @@ -35,8 +35,8 @@ public static void initialize() throws SAMLException { try { - DefaultBootstrap.bootstrap(); - } catch (ConfigurationException e) { + InitializationService.initialize(); + } catch (InitializationException e) { throw new SAMLException(e); } } diff --git a/src/main/java/com/lastpass/saml/SPConfig.java b/src/main/java/com/lastpass/saml/SPConfig.java index 35fabec..ab843d3 100644 --- a/src/main/java/com/lastpass/saml/SPConfig.java +++ b/src/main/java/com/lastpass/saml/SPConfig.java @@ -21,13 +21,16 @@ import java.io.FileInputStream; import java.io.InputStream; -import org.opensaml.Configuration; -import org.opensaml.xml.parse.BasicParserPool; -import org.opensaml.xml.io.UnmarshallerFactory; -import org.opensaml.saml2.metadata.EntityDescriptor; -import org.opensaml.saml2.metadata.SPSSODescriptor; -import org.opensaml.saml2.metadata.AssertionConsumerService; -import org.opensaml.common.xml.SAMLConstants; +import net.shibboleth.utilities.java.support.xml.BasicParserPool; +import net.shibboleth.utilities.java.support.xml.XMLParserException; +import org.opensaml.core.config.Configuration; +import org.opensaml.core.xml.io.UnmarshallerFactory; +import org.opensaml.core.xml.io.UnmarshallingException; +import org.opensaml.core.xml.util.XMLObjectSupport; +import org.opensaml.saml.common.xml.SAMLConstants; +import org.opensaml.saml.saml2.metadata.AssertionConsumerService; +import org.opensaml.saml.saml2.metadata.EntityDescriptor; +import org.opensaml.saml.saml2.metadata.SPSSODescriptor; import org.w3c.dom.Document; import org.w3c.dom.Element; @@ -109,17 +112,12 @@ private void init(InputStream inputStream) Document doc = parsers.parse(inputStream); Element root = doc.getDocumentElement(); - UnmarshallerFactory unmarshallerFactory = - Configuration.getUnmarshallerFactory(); - - edesc = (EntityDescriptor) unmarshallerFactory - .getUnmarshaller(root) - .unmarshall(root); + edesc = (EntityDescriptor) XMLObjectSupport.unmarshallFromInputStream(parsers, inputStream); } - catch (org.opensaml.xml.parse.XMLParserException e) { + catch (XMLParserException e) { throw new SAMLException(e); } - catch (org.opensaml.xml.io.UnmarshallingException e) { + catch (UnmarshallingException e) { throw new SAMLException(e); } From 5b2925aa8c67e37c286eb11890af6e94751cfb7f Mon Sep 17 00:00:00 2001 From: Jerker Klang Date: Tue, 9 Feb 2016 13:50:01 +0100 Subject: [PATCH 3/6] Use opensaml constant instead of hardcoded string --- src/main/java/com/lastpass/saml/IdPConfig.java | 3 ++- src/main/java/com/lastpass/saml/SPConfig.java | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/lastpass/saml/IdPConfig.java b/src/main/java/com/lastpass/saml/IdPConfig.java index 4e72197..816ae8f 100644 --- a/src/main/java/com/lastpass/saml/IdPConfig.java +++ b/src/main/java/com/lastpass/saml/IdPConfig.java @@ -32,6 +32,7 @@ import org.opensaml.core.xml.io.UnmarshallingException; import org.opensaml.core.xml.util.XMLObjectSupport; import org.opensaml.saml.common.xml.SAMLConstants; +import org.opensaml.saml.config.SAMLConfiguration; import org.opensaml.saml.saml2.metadata.EntityDescriptor; import org.opensaml.saml.saml2.metadata.IDPSSODescriptor; import org.opensaml.saml.saml2.metadata.KeyDescriptor; @@ -123,7 +124,7 @@ private void init(InputStream inputStream) // fetch idp information IDPSSODescriptor idpDesc = edesc.getIDPSSODescriptor( - "urn:oasis:names:tc:SAML:2.0:protocol"); + SAMLConstants.SAML20P_NS); if (idpDesc == null) throw new SAMLException("No IDP SSO descriptor found"); diff --git a/src/main/java/com/lastpass/saml/SPConfig.java b/src/main/java/com/lastpass/saml/SPConfig.java index ab843d3..7cde510 100644 --- a/src/main/java/com/lastpass/saml/SPConfig.java +++ b/src/main/java/com/lastpass/saml/SPConfig.java @@ -123,7 +123,7 @@ private void init(InputStream inputStream) // fetch sp information SPSSODescriptor spDesc = edesc.getSPSSODescriptor( - "urn:oasis:names:tc:SAML:2.0:protocol"); + SAMLConstants.SAML20P_NS); if (spDesc == null) throw new SAMLException("No SP SSO descriptor found"); From eb237ab4df2120e92bf8ac7f9943b568e51b5eb2 Mon Sep 17 00:00:00 2001 From: Jerker Klang Date: Tue, 9 Feb 2016 15:40:07 +0100 Subject: [PATCH 4/6] ParserPools must be initialized --- src/main/java/com/lastpass/saml/IdPConfig.java | 6 ++++++ src/main/java/com/lastpass/saml/SPConfig.java | 7 ++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/lastpass/saml/IdPConfig.java b/src/main/java/com/lastpass/saml/IdPConfig.java index 816ae8f..764b637 100644 --- a/src/main/java/com/lastpass/saml/IdPConfig.java +++ b/src/main/java/com/lastpass/saml/IdPConfig.java @@ -27,6 +27,7 @@ import java.io.InputStream; import javax.xml.bind.DatatypeConverter; +import net.shibboleth.utilities.java.support.component.ComponentInitializationException; import net.shibboleth.utilities.java.support.xml.BasicParserPool; import net.shibboleth.utilities.java.support.xml.XMLParserException; import org.opensaml.core.xml.io.UnmarshallingException; @@ -109,6 +110,11 @@ private void init(InputStream inputStream) { BasicParserPool parsers = new BasicParserPool(); parsers.setNamespaceAware(true); + try { + parsers.initialize(); + } catch (ComponentInitializationException e) { + throw new SAMLException("Failed to initialize BasicParserPool", e); + } EntityDescriptor edesc; diff --git a/src/main/java/com/lastpass/saml/SPConfig.java b/src/main/java/com/lastpass/saml/SPConfig.java index 7cde510..9e5d832 100644 --- a/src/main/java/com/lastpass/saml/SPConfig.java +++ b/src/main/java/com/lastpass/saml/SPConfig.java @@ -20,7 +20,7 @@ import java.io.File; import java.io.FileInputStream; import java.io.InputStream; - +import net.shibboleth.utilities.java.support.component.ComponentInitializationException; import net.shibboleth.utilities.java.support.xml.BasicParserPool; import net.shibboleth.utilities.java.support.xml.XMLParserException; import org.opensaml.core.config.Configuration; @@ -105,6 +105,11 @@ private void init(InputStream inputStream) { BasicParserPool parsers = new BasicParserPool(); parsers.setNamespaceAware(true); + try { + parsers.initialize(); + } catch (ComponentInitializationException e) { + throw new SAMLException("Failed to initialize BasicParserPool", e); + } EntityDescriptor edesc; From 8d911ba84a5a3181318136af11b75bd539c0fb99 Mon Sep 17 00:00:00 2001 From: Jerker Klang Date: Thu, 11 Feb 2016 08:32:39 +0100 Subject: [PATCH 5/6] Removed code that isn't used anymore and causes issues --- src/main/java/com/lastpass/saml/SPConfig.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/main/java/com/lastpass/saml/SPConfig.java b/src/main/java/com/lastpass/saml/SPConfig.java index 9e5d832..5921db3 100644 --- a/src/main/java/com/lastpass/saml/SPConfig.java +++ b/src/main/java/com/lastpass/saml/SPConfig.java @@ -114,9 +114,6 @@ private void init(InputStream inputStream) EntityDescriptor edesc; try { - Document doc = parsers.parse(inputStream); - Element root = doc.getDocumentElement(); - edesc = (EntityDescriptor) XMLObjectSupport.unmarshallFromInputStream(parsers, inputStream); } catch (XMLParserException e) { From 5c2d181f2e0afcce5e304786c51f1501bdba011d Mon Sep 17 00:00:00 2001 From: Jerker Klang Date: Thu, 11 Feb 2016 08:33:42 +0100 Subject: [PATCH 6/6] Initialize buffer pool before use --- src/main/java/com/lastpass/saml/SAMLClient.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/java/com/lastpass/saml/SAMLClient.java b/src/main/java/com/lastpass/saml/SAMLClient.java index a628429..be6d1a0 100644 --- a/src/main/java/com/lastpass/saml/SAMLClient.java +++ b/src/main/java/com/lastpass/saml/SAMLClient.java @@ -34,6 +34,7 @@ import javax.xml.bind.DatatypeConverter; import javax.xml.bind.ValidationException; +import net.shibboleth.utilities.java.support.component.ComponentInitializationException; import net.shibboleth.utilities.java.support.xml.BasicParserPool; import net.shibboleth.utilities.java.support.xml.SerializeSupport; import net.shibboleth.utilities.java.support.xml.XMLParserException; @@ -110,6 +111,11 @@ public SAMLClient(SPConfig spConfig, IdPConfig idpConfig) // create xml parsers parsers = new BasicParserPool(); parsers.setNamespaceAware(true); + try { + parsers.initialize(); + } catch (ComponentInitializationException e) { + throw new SAMLException("Failed to initialize BasicParserPool", e); + } } /**