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 diff --git a/src/main/java/com/lastpass/saml/IdPConfig.java b/src/main/java/com/lastpass/saml/IdPConfig.java index 65d450b..764b637 100644 --- a/src/main/java/com/lastpass/saml/IdPConfig.java +++ b/src/main/java/com/lastpass/saml/IdPConfig.java @@ -26,23 +26,23 @@ 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.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; +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; +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; /** @@ -110,30 +110,27 @@ 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; 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); } // 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/SAMLClient.java b/src/main/java/com/lastpass/saml/SAMLClient.java index 3991722..be6d1a0 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,34 @@ import java.util.zip.Deflater; 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; +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 +88,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,15 +105,17 @@ 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(); parsers.setNamespaceAware(true); + try { + parsers.initialize(); + } catch (ComponentInitializationException e) { + throw new SAMLException("Failed to initialize BasicParserPool", e); + } } /** @@ -141,26 +142,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); + return (Response) obj; } - catch (org.opensaml.xml.io.UnmarshallingException e) { + catch (XMLParserException e) { throw new SAMLException(e); } - catch (org.xml.sax.SAXException e) { + catch (UnmarshallingException e) { throw new SAMLException(e); - } - catch (java.io.IOException e) { - throw new SAMLException(e); - } + } } private void validate(Response response) @@ -168,13 +161,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 +207,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 +326,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 +344,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..5921db3 100644 --- a/src/main/java/com/lastpass/saml/SPConfig.java +++ b/src/main/java/com/lastpass/saml/SPConfig.java @@ -20,14 +20,17 @@ import java.io.File; 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.component.ComponentInitializationException; +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; @@ -102,30 +105,27 @@ 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; 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); } // 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");