First public commit

This commit is contained in:
srasmusson
2014-09-02 19:01:03 +02:00
parent ea8397e999
commit ded8127e1f
16 changed files with 1175 additions and 0 deletions

2
.gitignore vendored
View File

@@ -8,5 +8,7 @@
*.war
*.ear
# Maven target #
target/
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*

52
pom.xml Normal file
View File

@@ -0,0 +1,52 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>no.steras.opensamlbook</groupId>
<artifactId>webprofile-ref-project</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<dependencies>
<dependency>
<groupId>org.opensaml</groupId>
<artifactId>opensaml</artifactId>
<version>2.6.0</version>
<exclusions>
<exclusion>
<groupId>org.apache.xerces</groupId>
<artifactId>xercesImpl</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>xerces</groupId>
<artifactId>xercesImpl</artifactId>
<version>2.10.0</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.0.13</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.0.13</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
</dependencies>
<repositories>
<repository>
<id>Shibboleth repo</id>
<url>https://build.shibboleth.net/nexus/content/repositories/releases</url>
</repository>
</repositories>
</project>

View File

@@ -0,0 +1,101 @@
package no.steras.opensamlbook;
import org.opensaml.common.impl.SecureRandomIdentifierGenerator;
import org.opensaml.ws.soap.soap11.Body;
import org.opensaml.ws.soap.soap11.Envelope;
import org.opensaml.xml.Configuration;
import org.opensaml.xml.XMLObject;
import org.opensaml.xml.XMLObjectBuilderFactory;
import org.opensaml.xml.io.Marshaller;
import org.opensaml.xml.io.MarshallingException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.StringWriter;
import java.security.NoSuchAlgorithmException;
/**
* Created by Privat on 4/6/14.
*/
public class OpenSAMLUtils {
private static Logger logger = LoggerFactory.getLogger(OpenSAMLUtils.class);
private static SecureRandomIdentifierGenerator secureRandomIdGenerator;
static {
try {
secureRandomIdGenerator = new SecureRandomIdentifierGenerator();
} catch (NoSuchAlgorithmException e) {
logger.error(e.getMessage(), e);
}
}
public static <T> T buildSAMLObject(final Class<T> clazz) {
T object = null;
try {
XMLObjectBuilderFactory builderFactory = Configuration.getBuilderFactory();
QName defaultElementName = (QName)clazz.getDeclaredField("DEFAULT_ELEMENT_NAME").get(null);
object = (T)builderFactory.getBuilder(defaultElementName).buildObject(defaultElementName);
} catch (IllegalAccessException e) {
throw new IllegalArgumentException("Could not create SAML object");
} catch (NoSuchFieldException e) {
throw new IllegalArgumentException("Could not create SAML object");
}
return object;
}
public static String generateSecureRandomId() {
return secureRandomIdGenerator.generateIdentifier();
}
public static void logSAMLObject(final XMLObject object) {
try {
DocumentBuilder builder;
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
builder = factory.newDocumentBuilder();
Document document = builder.newDocument();
Marshaller out = Configuration.getMarshallerFactory().getMarshaller(object);
out.marshall(object, document);
Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
StreamResult result = new StreamResult(new StringWriter());
DOMSource source = new DOMSource(document);
transformer.transform(source, result);
String xmlString = result.getWriter().toString();
logger.info(xmlString);
} catch (ParserConfigurationException e) {
logger.error(e.getMessage(), e);
} catch (MarshallingException e) {
logger.error(e.getMessage(), e);
} catch (TransformerException e) {
logger.error(e.getMessage(), e);
}
}
public static Envelope wrapInSOAPEnvelope(final XMLObject xmlObject) throws IllegalAccessException {
Envelope envelope = OpenSAMLUtils.buildSAMLObject(Envelope.class);
Body body = OpenSAMLUtils.buildSAMLObject(Body.class);
body.getUnknownXMLObjects().add(xmlObject);
envelope.setBody(body);
return envelope;
}
}

View File

@@ -0,0 +1,19 @@
package no.steras.opensamlbook.app;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* This servlet acts as the resource that the access filter is protecting
*/
public class ApplicationServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html");
resp.getWriter().append("<h1>You are now at the requested resource</h1>");
resp.getWriter().append("This is the protected resource. You are authenticated");
}
}

View File

@@ -0,0 +1,331 @@
package no.steras.opensamlbook.idp;
import no.steras.opensamlbook.OpenSAMLUtils;
import no.steras.opensamlbook.sp.SPConstants;
import no.steras.opensamlbook.sp.SPCredentials;
import org.apache.xml.security.utils.EncryptionConstants;
import org.joda.time.DateTime;
import org.opensaml.common.SAMLObject;
import org.opensaml.common.impl.SecureRandomIdentifierGenerator;
import org.opensaml.saml2.core.*;
import org.opensaml.saml2.encryption.Encrypter;
import org.opensaml.saml2.metadata.provider.MetadataProviderException;
import org.opensaml.security.SAMLSignatureProfileValidator;
import org.opensaml.ws.soap.soap11.Body;
import org.opensaml.ws.soap.soap11.Envelope;
import org.opensaml.xml.Configuration;
import org.opensaml.xml.XMLObject;
import org.opensaml.xml.encryption.EncryptionException;
import org.opensaml.xml.encryption.EncryptionParameters;
import org.opensaml.xml.encryption.KeyEncryptionParameters;
import org.opensaml.xml.io.*;
import org.opensaml.xml.parse.BasicParserPool;
import org.opensaml.xml.parse.XMLParserException;
import org.opensaml.xml.schema.XSString;
import org.opensaml.xml.schema.impl.XSStringBuilder;
import org.opensaml.xml.security.keyinfo.KeyInfoGeneratorFactory;
import org.opensaml.xml.signature.*;
import org.opensaml.xml.validation.ValidationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.SAXException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.Writer;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.spec.ECField;
/**
* Created by Privat on 4/6/14.
*/
public class ArtifactResolutionServlet extends HttpServlet {
private static Logger logger = LoggerFactory.getLogger(ArtifactResolutionServlet.class);
@Override
protected void doPost(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {
ArtifactResponse artifactResponse = buildArtifactResponse();
artifactResponse.setInResponseTo("Made up ID");
printSAMLObject(wrapInSOAPEnvelope(artifactResponse), resp.getWriter());
}
public static ArtifactResolve unmarshallArtifactResolve(final InputStream input) {
try {
BasicParserPool ppMgr = new BasicParserPool();
ppMgr.setNamespaceAware(true);
Document soap = ppMgr.parse(input);
Element soapRoot = soap.getDocumentElement();
UnmarshallerFactory unmarshallerFactory = Configuration.getUnmarshallerFactory();
Unmarshaller unmarshaller = unmarshallerFactory.getUnmarshaller(soapRoot);
Envelope soapEnvelope = (Envelope)unmarshaller.unmarshall(soapRoot);
return (ArtifactResolve)soapEnvelope.getBody().getUnknownXMLObjects().get(0);
} catch (XMLParserException e) {
throw new RuntimeException(e);
} catch (UnmarshallingException e) {
throw new RuntimeException(e);
}
}
public static org.w3c.dom.Element marshallSAMLObject(final SAMLObject object) {
org.w3c.dom.Element element = null;
try {
MarshallerFactory unMarshallerFactory = Configuration.getMarshallerFactory();
Marshaller marshaller = unMarshallerFactory.getMarshaller(object);
element = marshaller.marshall(object);
} catch (ClassCastException e) {
throw new IllegalArgumentException("The class does not implement the interface XMLObject", e);
} catch (MarshallingException e) {
throw new RuntimeException(e);
}
return element;
}
private ArtifactResponse buildArtifactResponse() {
ArtifactResponse artifactResponse = OpenSAMLUtils.buildSAMLObject(ArtifactResponse.class);
Issuer issuer = OpenSAMLUtils.buildSAMLObject(Issuer.class);
issuer.setValue(IDPConstants.IDP_ENTITY_ID);
artifactResponse.setIssuer(issuer);
artifactResponse.setIssueInstant(new DateTime());
artifactResponse.setDestination(SPConstants.ASSERTION_CONSUMER_SERVICE);
artifactResponse.setID(OpenSAMLUtils.generateSecureRandomId());
Status status = OpenSAMLUtils.buildSAMLObject(Status.class);
StatusCode statusCode = OpenSAMLUtils.buildSAMLObject(StatusCode.class);
statusCode.setValue(StatusCode.SUCCESS_URI);
status.setStatusCode(statusCode);
artifactResponse.setStatus(status);
Response response = OpenSAMLUtils.buildSAMLObject(Response.class);
response.setDestination(SPConstants.ASSERTION_CONSUMER_SERVICE);
response.setIssueInstant(new DateTime());
response.setID(OpenSAMLUtils.generateSecureRandomId());
Issuer issuer2 = OpenSAMLUtils.buildSAMLObject(Issuer.class);
issuer2.setValue(IDPConstants.IDP_ENTITY_ID);
response.setIssuer(issuer2);
Status status2 = OpenSAMLUtils.buildSAMLObject(Status.class);
StatusCode statusCode2 = OpenSAMLUtils.buildSAMLObject(StatusCode.class);
statusCode2.setValue(StatusCode.SUCCESS_URI);
status2.setStatusCode(statusCode2);
response.setStatus(status2);
artifactResponse.setMessage(response);
Assertion assertion = buildAssertion();
signAssertion(assertion);
EncryptedAssertion encryptedAssertion = encryptAssertion(assertion);
response.getEncryptedAssertions().add(encryptedAssertion);
return artifactResponse;
}
private EncryptedAssertion encryptAssertion(Assertion assertion) {
EncryptionParameters encryptionParameters = new EncryptionParameters();
encryptionParameters.setAlgorithm(EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES128);
KeyEncryptionParameters keyEncryptionParameters = new KeyEncryptionParameters();
keyEncryptionParameters.setEncryptionCredential(SPCredentials.getCredential());
keyEncryptionParameters.setAlgorithm(EncryptionConstants.ALGO_ID_KEYTRANSPORT_RSAOAEP);
Encrypter encrypter = new Encrypter(encryptionParameters, keyEncryptionParameters);
encrypter.setKeyPlacement(Encrypter.KeyPlacement.INLINE);
try {
EncryptedAssertion encryptedAssertion = encrypter.encrypt(assertion);
return encryptedAssertion;
} catch (EncryptionException e) {
throw new RuntimeException(e);
}
}
private void signAssertion(Assertion assertion) {
Signature signature = OpenSAMLUtils.buildSAMLObject(Signature.class);
signature.setSigningCredential(IDPCredentials.getCredential());
signature.setSignatureAlgorithm(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA1);
signature.setCanonicalizationAlgorithm(SignatureConstants.ALGO_ID_C14N_EXCL_OMIT_COMMENTS);
assertion.setSignature(signature);
try {
Configuration.getMarshallerFactory().getMarshaller(assertion).marshall(assertion);
} catch (MarshallingException e) {
throw new RuntimeException(e);
}
try {
Signer.signObject(signature);
} catch (SignatureException e) {
throw new RuntimeException(e);
}
}
private Assertion buildAssertion() {
Assertion assertion = OpenSAMLUtils.buildSAMLObject(Assertion.class);
Issuer issuer = OpenSAMLUtils.buildSAMLObject(Issuer.class);
issuer.setValue(IDPConstants.IDP_ENTITY_ID);
assertion.setIssuer(issuer);
assertion.setIssueInstant(new DateTime());
assertion.setID(OpenSAMLUtils.generateSecureRandomId());
Subject subject = OpenSAMLUtils.buildSAMLObject(Subject.class);
assertion.setSubject(subject);
NameID nameID = OpenSAMLUtils.buildSAMLObject(NameID.class);
nameID.setFormat(NameIDType.TRANSIENT);
nameID.setValue("Some NameID value");
nameID.setSPNameQualifier("SP name qualifier");
nameID.setNameQualifier("Name qualifier");
subject.setNameID(nameID);
subject.getSubjectConfirmations().add(buildSubjectConfirmation());
assertion.setConditions(buildConditions());
assertion.getAttributeStatements().add(buildAttributeStatement());
assertion.getAuthnStatements().add(buildAuthnStatement());
return assertion;
}
private SubjectConfirmation buildSubjectConfirmation() {
SubjectConfirmation subjectConfirmation = OpenSAMLUtils.buildSAMLObject(SubjectConfirmation.class);
subjectConfirmation.setMethod(SubjectConfirmation.METHOD_BEARER);
SubjectConfirmationData subjectConfirmationData = OpenSAMLUtils.buildSAMLObject(SubjectConfirmationData.class);
subjectConfirmationData.setInResponseTo("Made up ID");
subjectConfirmationData.setNotBefore(new DateTime().minusDays(2));
subjectConfirmationData.setNotOnOrAfter(new DateTime().plusDays(2));
subjectConfirmationData.setRecipient(SPConstants.ASSERTION_CONSUMER_SERVICE);
subjectConfirmation.setSubjectConfirmationData(subjectConfirmationData);
return subjectConfirmation;
}
private AuthnStatement buildAuthnStatement() {
AuthnStatement authnStatement = OpenSAMLUtils.buildSAMLObject(AuthnStatement.class);
AuthnContext authnContext = OpenSAMLUtils.buildSAMLObject(AuthnContext.class);
AuthnContextClassRef authnContextClassRef = OpenSAMLUtils.buildSAMLObject(AuthnContextClassRef.class);
authnContextClassRef.setAuthnContextClassRef(AuthnContext.SMARTCARD_AUTHN_CTX);
authnContext.setAuthnContextClassRef(authnContextClassRef);
authnStatement.setAuthnContext(authnContext);
authnStatement.setAuthnInstant(new DateTime());
return authnStatement;
}
private Conditions buildConditions() {
Conditions conditions = OpenSAMLUtils.buildSAMLObject(Conditions.class);
conditions.setNotBefore(new DateTime().minusDays(2));
conditions.setNotOnOrAfter(new DateTime().plusDays(2));
AudienceRestriction audienceRestriction = OpenSAMLUtils.buildSAMLObject(AudienceRestriction.class);
Audience audience = OpenSAMLUtils.buildSAMLObject(Audience.class);
audience.setAudienceURI(SPConstants.ASSERTION_CONSUMER_SERVICE);
audienceRestriction.getAudiences().add(audience);
conditions.getAudienceRestrictions().add(audienceRestriction);
return conditions;
}
private AttributeStatement buildAttributeStatement() {
AttributeStatement attributeStatement = OpenSAMLUtils.buildSAMLObject(AttributeStatement.class);
Attribute attributeUserName = OpenSAMLUtils.buildSAMLObject(Attribute.class);
XSStringBuilder stringBuilder = (XSStringBuilder)Configuration.getBuilderFactory().getBuilder(XSString.TYPE_NAME);
XSString userNameValue = stringBuilder.buildObject(AttributeValue.DEFAULT_ELEMENT_NAME, XSString.TYPE_NAME);
userNameValue.setValue("bob");
attributeUserName.getAttributeValues().add(userNameValue);
attributeUserName.setName("username");
attributeStatement.getAttributes().add(attributeUserName);
Attribute attributeLevel = OpenSAMLUtils.buildSAMLObject(Attribute.class);
XSString levelValue = stringBuilder.buildObject(AttributeValue.DEFAULT_ELEMENT_NAME, XSString.TYPE_NAME);
levelValue.setValue("999999999");
attributeLevel.getAttributeValues().add(levelValue);
attributeLevel.setName("telephone");
attributeStatement.getAttributes().add(attributeLevel);
return attributeStatement;
}
public static Envelope wrapInSOAPEnvelope(final XMLObject xmlObject) {
Envelope envelope = OpenSAMLUtils.buildSAMLObject(Envelope.class);
Body body = OpenSAMLUtils.buildSAMLObject(Body.class);
body.getUnknownXMLObjects().add(xmlObject);
envelope.setBody(body);
return envelope;
}
public static void printSAMLObject(final XMLObject object, final PrintWriter writer) {
try {
DocumentBuilder builder;
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
builder = factory.newDocumentBuilder();
org.w3c.dom.Document document = builder.newDocument();
Marshaller out = Configuration.getMarshallerFactory().getMarshaller(object);
out.marshall(object, document);
Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
StreamResult result = new StreamResult(writer);
DOMSource source = new DOMSource(document);
transformer.transform(source, result);
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (MarshallingException e) {
e.printStackTrace();
} catch (TransformerException e) {
e.printStackTrace();
}
}
}

View File

@@ -0,0 +1,10 @@
package no.steras.opensamlbook.idp;
/**
* Created by Privat on 4/7/14.
*/
public class IDPConstants {
public static final String IDP_ENTITY_ID = "TestIDP";
public static final String SSO_SERVICE = "http://localhost:8080/webprofile-ref-project/idp/singleSignOnService";
public static final String ARTIFACT_RESOLUTION_SERVICE = "http://localhost:8080/webprofile-ref-project/idp/artifactResolutionService";
}

View File

@@ -0,0 +1,45 @@
package no.steras.opensamlbook.idp;
import org.opensaml.xml.security.*;
import org.opensaml.xml.security.credential.BasicCredential;
import org.opensaml.xml.security.credential.Credential;
import org.opensaml.xml.security.credential.KeyStoreCredentialResolver;
import org.opensaml.xml.security.credential.UsageType;
import org.opensaml.xml.security.criteria.EntityIDCriteria;
import org.opensaml.xml.security.x509.X509Credential;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.FileInputStream;
import java.io.InputStream;
import java.net.URL;
import java.security.*;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
/**
* Created by Privat on 13/05/14.
*/
public class IDPCredentials {
private static final Credential credential;
static {
credential = generateCredential();
}
private static Credential generateCredential() {
try {
KeyPair keyPair = SecurityHelper.generateKeyPair("RSA", 1024, null);
return SecurityHelper.getSimpleCredential(keyPair.getPublic(), keyPair.getPrivate());
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
} catch (NoSuchProviderException e) {
throw new RuntimeException(e);
}
}
public static Credential getCredential() {
return credential;
}
}

View File

@@ -0,0 +1,37 @@
package no.steras.opensamlbook.idp;
import no.steras.opensamlbook.OpenSAMLUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.Writer;
/**
* Created by Privat on 4/6/14.
*/
public class SingleSignOnServlet extends HttpServlet {
private static Logger logger = LoggerFactory.getLogger(SingleSignOnServlet.class);
private static final String ASSERTION_CONSUMER_SERVICE = "http://localhost:8080/webprofile-ref-project/sp/consumer";
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
logger.info("AuthnRequest recieved");
Writer w = resp.getWriter();
resp.setContentType("text/html");
w.append("<html>" + "<head></head>" + "<body><h1>You are now at IDP, click the button to authenticate</h1> <form method=\"POST\">"
+ "<input type=\"submit\" value=\"Authenticate\"/>" + "</form>" + "</body>" + "</html>");
}
@Override
protected void doPost(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {
resp.sendRedirect(ASSERTION_CONSUMER_SERVICE + "?SAMLart=AAQAAMFbLinlXaCM%2BFIxiDwGOLAy2T71gbpO7ZhNzAgEANlB90ECfpNEVLg%3D");
}
}

View File

@@ -0,0 +1,165 @@
package no.steras.opensamlbook.sp;
import no.steras.opensamlbook.OpenSAMLUtils;
import no.steras.opensamlbook.idp.IDPConstants;
import org.joda.time.DateTime;
import org.opensaml.Configuration;
import org.opensaml.DefaultBootstrap;
import org.opensaml.common.SAMLObject;
import org.opensaml.common.binding.BasicSAMLMessageContext;
import org.opensaml.common.xml.SAMLConstants;
import org.opensaml.saml2.binding.encoding.HTTPRedirectDeflateEncoder;
import org.opensaml.saml2.core.*;
import org.opensaml.saml2.metadata.Endpoint;
import org.opensaml.saml2.metadata.SingleSignOnService;
import org.opensaml.ws.message.encoder.MessageEncodingException;
import org.opensaml.ws.transport.http.HttpServletResponseAdapter;
import org.opensaml.xml.ConfigurationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.security.Provider;
import java.security.Security;
/**
* The filter intercepts the user and start the SAML authentication if it is not authenticated
*/
public class AccessFilter implements Filter {
private static Logger logger = LoggerFactory.getLogger(AccessFilter.class);
@Override
public void init(FilterConfig filterConfig) throws ServletException {
Configuration.validateJCEProviders();
Configuration.validateNonSunJAXP();
for (Provider jceProvider : Security.getProviders()) {
logger.info(jceProvider.getInfo());
}
try {
logger.info("Bootstrapping");
DefaultBootstrap.bootstrap();
} catch (ConfigurationException e) {
throw new RuntimeException("Bootstrapping failed");
}
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest)request;
HttpServletResponse httpServletResponse = (HttpServletResponse)response;
if (httpServletRequest.getSession().getAttribute(SPConstants.AUTHENTICATED_SESSION_ATTRIBUTE) != null) {
chain.doFilter(request, response);
} else {
setGotoURLOnSession(httpServletRequest);
redirectUserForAuthentication(httpServletResponse);
}
}
private void setGotoURLOnSession(HttpServletRequest request) {
request.getSession().setAttribute(SPConstants.GOTO_URL_SESSION_ATTRIBUTE, request.getRequestURL().toString());
}
private void redirectUserForAuthentication(HttpServletResponse httpServletResponse) {
AuthnRequest authnRequest = buildAuthnRequest();
redirectUserWithRequest(httpServletResponse, authnRequest);
}
private void redirectUserWithRequest(HttpServletResponse httpServletResponse, AuthnRequest authnRequest) {
HttpServletResponseAdapter responseAdapter = new HttpServletResponseAdapter(httpServletResponse, true);
BasicSAMLMessageContext<SAMLObject, AuthnRequest, SAMLObject> context = new BasicSAMLMessageContext<SAMLObject, AuthnRequest, SAMLObject>();
context.setPeerEntityEndpoint(getIPDEndpoint());
context.setOutboundSAMLMessage(authnRequest);
context.setOutboundMessageTransport(responseAdapter);
context.setOutboundSAMLMessageSigningCredential(SPCredentials.getCredential());
HTTPRedirectDeflateEncoder encoder = new HTTPRedirectDeflateEncoder();
logger.info("AuthnRequest: ");
OpenSAMLUtils.logSAMLObject(authnRequest);
logger.info("Redirecting to IDP");
try {
encoder.encode(context);
} catch (MessageEncodingException e) {
throw new RuntimeException(e);
}
}
private AuthnRequest buildAuthnRequest() {
AuthnRequest authnRequest = OpenSAMLUtils.buildSAMLObject(AuthnRequest.class);
authnRequest.setIssueInstant(new DateTime());
authnRequest.setDestination(getIPDSSODestination());
authnRequest.setProtocolBinding(SAMLConstants.SAML2_ARTIFACT_BINDING_URI);
authnRequest.setAssertionConsumerServiceURL(getAssertionConsumerEndpoint());
authnRequest.setID(OpenSAMLUtils.generateSecureRandomId());
authnRequest.setIssuer(buildIssuer());
authnRequest.setNameIDPolicy(buildNameIdPolicy());
authnRequest.setRequestedAuthnContext(buildRequestedAuthnContext());
return authnRequest;
}
private RequestedAuthnContext buildRequestedAuthnContext() {
RequestedAuthnContext requestedAuthnContext = OpenSAMLUtils.buildSAMLObject(RequestedAuthnContext.class);
requestedAuthnContext.setComparison(AuthnContextComparisonTypeEnumeration.MINIMUM);
AuthnContextClassRef passwordAuthnContextClassRef = OpenSAMLUtils.buildSAMLObject(AuthnContextClassRef.class);
passwordAuthnContextClassRef.setAuthnContextClassRef(AuthnContext.PASSWORD_AUTHN_CTX);
requestedAuthnContext.getAuthnContextClassRefs().add(passwordAuthnContextClassRef);
return requestedAuthnContext;
}
private NameIDPolicy buildNameIdPolicy() {
NameIDPolicy nameIDPolicy = OpenSAMLUtils.buildSAMLObject(NameIDPolicy.class);
nameIDPolicy.setAllowCreate(true);
nameIDPolicy.setFormat(NameIDType.TRANSIENT);
return nameIDPolicy;
}
private Issuer buildIssuer() {
Issuer issuer = OpenSAMLUtils.buildSAMLObject(Issuer.class);
issuer.setValue(getSPIssuerValue());
return issuer;
}
private String getSPIssuerValue() {
return SPConstants.SP_ENTITY_ID;
}
private String getSPNameQualifier() {
return SPConstants.SP_ENTITY_ID;
}
private String getAssertionConsumerEndpoint() {
return SPConstants.ASSERTION_CONSUMER_SERVICE;
}
private String getIPDSSODestination() {
return IDPConstants.SSO_SERVICE;
}
private Endpoint getIPDEndpoint() {
SingleSignOnService endpoint = OpenSAMLUtils.buildSAMLObject(SingleSignOnService.class);
endpoint.setBinding(SAMLConstants.SAML2_REDIRECT_BINDING_URI);
endpoint.setLocation(getIPDSSODestination());
return endpoint;
}
@Override
public void destroy() {
}
}

View File

@@ -0,0 +1,210 @@
package no.steras.opensamlbook.sp;
import no.steras.opensamlbook.OpenSAMLUtils;
import no.steras.opensamlbook.idp.IDPConstants;
import no.steras.opensamlbook.idp.IDPCredentials;
import org.joda.time.DateTime;
import org.opensaml.saml2.core.*;
import org.opensaml.saml2.encryption.Decrypter;
import org.opensaml.security.SAMLSignatureProfileValidator;
import org.opensaml.ws.soap.client.BasicSOAPMessageContext;
import org.opensaml.ws.soap.client.http.HttpClientBuilder;
import org.opensaml.ws.soap.client.http.HttpSOAPClient;
import org.opensaml.ws.soap.common.SOAPException;
import org.opensaml.ws.soap.soap11.Envelope;
import org.opensaml.xml.Configuration;
import org.opensaml.xml.XMLObject;
import org.opensaml.xml.encryption.DecryptionException;
import org.opensaml.xml.encryption.InlineEncryptedKeyResolver;
import org.opensaml.xml.io.MarshallingException;
import org.opensaml.xml.parse.BasicParserPool;
import org.opensaml.xml.schema.XSString;
import org.opensaml.xml.security.SecurityException;
import org.opensaml.xml.security.keyinfo.StaticKeyInfoCredentialResolver;
import org.opensaml.xml.signature.*;
import org.opensaml.xml.validation.ValidationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* Created by Privat on 4/6/14.
*/
public class ConsumerServlet extends HttpServlet {
private static Logger logger = LoggerFactory.getLogger(ConsumerServlet.class);
@Override
protected void doGet(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {
logger.info("Artifact received");
Artifact artifact = buildArtifactFromRequest(req);
logger.info("Artifact: " + artifact.getArtifact());
ArtifactResolve artifactResolve = buildArtifactResolve(artifact);
signArtifactResolve(artifactResolve);
logger.info("Sending ArtifactResolve");
logger.info("ArtifactResolve: ");
OpenSAMLUtils.logSAMLObject(artifactResolve);
ArtifactResponse artifactResponse = sendAndReceiveArtifactResolve(artifactResolve);
logger.info("ArtifactResponse received");
logger.info("ArtifactResponse: ");
OpenSAMLUtils.logSAMLObject(artifactResponse);
EncryptedAssertion encryptedAssertion = getEncryptedAssertion(artifactResponse);
Assertion assertion = decryptAssertion(encryptedAssertion);
verifyAssertionSignature(assertion);
logger.info("Decrypted Assertion: ");
OpenSAMLUtils.logSAMLObject(assertion);
logAssertionAttributes(assertion);
logAuthenticationInstant(assertion);
logAuthenticationMethod(assertion);
setAuthenticatedSession(req);
redirectToGotoURL(req, resp);
}
private Assertion decryptAssertion(EncryptedAssertion encryptedAssertion) {
StaticKeyInfoCredentialResolver keyInfoCredentialResolver = new StaticKeyInfoCredentialResolver(SPCredentials.getCredential());
Decrypter decrypter = new Decrypter(null, keyInfoCredentialResolver, new InlineEncryptedKeyResolver());
decrypter.setRootInNewDocument(true);
try {
return decrypter.decrypt(encryptedAssertion);
} catch (DecryptionException e) {
throw new RuntimeException(e);
}
}
private void verifyAssertionSignature(Assertion assertion) {
if (!assertion.isSigned()) {
throw new RuntimeException("The SAML Assertion was not signed");
}
try {
SAMLSignatureProfileValidator profileValidator = new SAMLSignatureProfileValidator();
profileValidator.validate(assertion.getSignature());
SignatureValidator sigValidator = new SignatureValidator(IDPCredentials.getCredential());
sigValidator.validate(assertion.getSignature());
logger.info("SAML Assertion signature verified");
} catch (ValidationException e) {
throw new RuntimeException(e);
}
}
private void signArtifactResolve(ArtifactResolve artifactResolve) {
Signature signature = OpenSAMLUtils.buildSAMLObject(Signature.class);
signature.setSigningCredential(SPCredentials.getCredential());
signature.setSignatureAlgorithm(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA1);
signature.setCanonicalizationAlgorithm(SignatureConstants.ALGO_ID_C14N_EXCL_OMIT_COMMENTS);
artifactResolve.setSignature(signature);
try {
Configuration.getMarshallerFactory().getMarshaller(artifactResolve).marshall(artifactResolve);
} catch (MarshallingException e) {
throw new RuntimeException(e);
}
try {
Signer.signObject(signature);
} catch (SignatureException e) {
throw new RuntimeException(e);
}
}
private void setAuthenticatedSession(HttpServletRequest req) {
req.getSession().setAttribute(SPConstants.AUTHENTICATED_SESSION_ATTRIBUTE, true);
}
private void redirectToGotoURL(HttpServletRequest req, HttpServletResponse resp) {
String gotoURL = (String)req.getSession().getAttribute(SPConstants.GOTO_URL_SESSION_ATTRIBUTE);
logger.info("Redirecting to requested URL: " + gotoURL);
try {
resp.sendRedirect(gotoURL);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private void logAuthenticationMethod(Assertion assertion) {
logger.info("Authentication method: " + assertion.getAuthnStatements().get(0)
.getAuthnContext().getAuthnContextClassRef().getAuthnContextClassRef());
}
private void logAuthenticationInstant(Assertion assertion) {
logger.info("Authentication instant: " + assertion.getAuthnStatements().get(0).getAuthnInstant());
}
private void logAssertionAttributes(Assertion assertion) {
for (Attribute attribute : assertion.getAttributeStatements().get(0).getAttributes()) {
logger.info("Attribute name: " + attribute.getName());
for (XMLObject attributeValue : attribute.getAttributeValues()) {
logger.info("Attribute value: " + ((XSString) attributeValue).getValue());
}
}
}
private EncryptedAssertion getEncryptedAssertion(ArtifactResponse artifactResponse) {
Response response = (Response)artifactResponse.getMessage();
return response.getEncryptedAssertions().get(0);
}
private ArtifactResponse sendAndReceiveArtifactResolve(final ArtifactResolve artifactResolve) {
try {
Envelope envelope = OpenSAMLUtils.wrapInSOAPEnvelope(artifactResolve);
HttpClientBuilder clientBuilder = new HttpClientBuilder();
HttpSOAPClient soapClient = new HttpSOAPClient(clientBuilder.buildClient(), new BasicParserPool());
BasicSOAPMessageContext soapContext = new BasicSOAPMessageContext();
soapContext.setOutboundMessage(envelope);
soapClient.send(IDPConstants.ARTIFACT_RESOLUTION_SERVICE, soapContext);
Envelope soapResponse = (Envelope)soapContext.getInboundMessage();
return (ArtifactResponse)soapResponse.getBody().getUnknownXMLObjects().get(0);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (SecurityException e) {
throw new RuntimeException(e);
} catch (SOAPException e) {
throw new RuntimeException(e);
}
}
private Artifact buildArtifactFromRequest(final HttpServletRequest req) {
Artifact artifact = OpenSAMLUtils.buildSAMLObject(Artifact.class);
artifact.setArtifact(req.getParameter("SAMLart"));
return artifact;
}
private ArtifactResolve buildArtifactResolve(final Artifact artifact) {
ArtifactResolve artifactResolve = OpenSAMLUtils.buildSAMLObject(ArtifactResolve.class);
Issuer issuer = OpenSAMLUtils.buildSAMLObject(Issuer.class);
issuer.setValue(SPConstants.SP_ENTITY_ID);
artifactResolve.setIssuer(issuer);
artifactResolve.setIssueInstant(new DateTime());
artifactResolve.setID(OpenSAMLUtils.generateSecureRandomId());
artifactResolve.setDestination(IDPConstants.ARTIFACT_RESOLUTION_SERVICE);
artifactResolve.setArtifact(artifact);
return artifactResolve;
}
}

View File

@@ -0,0 +1,15 @@
package no.steras.opensamlbook.sp;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Created by Privat on 4/7/14.
*/
public class SPConstants {
public static final String SP_ENTITY_ID = "TestSP";
public static final String AUTHENTICATED_SESSION_ATTRIBUTE = "authenticated";
public static final String GOTO_URL_SESSION_ATTRIBUTE = "gotoURL";
public static final String ASSERTION_CONSUMER_SERVICE = "http://localhost:8080/webprofile-ref-project/sp/consumer";
}

View File

@@ -0,0 +1,63 @@
package no.steras.opensamlbook.sp;
import org.opensaml.xml.security.*;
import org.opensaml.xml.security.credential.BasicCredential;
import org.opensaml.xml.security.credential.Credential;
import org.opensaml.xml.security.credential.KeyStoreCredentialResolver;
import org.opensaml.xml.security.credential.UsageType;
import org.opensaml.xml.security.criteria.EntityIDCriteria;
import org.opensaml.xml.security.x509.X509Credential;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.FileInputStream;
import java.io.InputStream;
import java.net.URL;
import java.security.*;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
/**
* Created by Privat on 13/05/14.
*/
public class SPCredentials {
private static final String KEY_STORE_PASSWORD = "password";
private static final String KEY_STORE_ENTRY_PASSWORD = "password";
private static final String KEY_STORE_PATH = "/SPKeystore.jks";
private static final String KEY_ENTRY_ID = "SPKey";
private static final Credential credential;
static {
try {
KeyStore keystore = readKeystoreFromFile(KEY_STORE_PATH, KEY_STORE_PASSWORD);
Map<String, String> passwordMap = new HashMap<String, String>();
passwordMap.put(KEY_ENTRY_ID, KEY_STORE_ENTRY_PASSWORD);
KeyStoreCredentialResolver resolver = new KeyStoreCredentialResolver(keystore, passwordMap);
Criteria criteria = new EntityIDCriteria(KEY_ENTRY_ID);
CriteriaSet criteriaSet = new CriteriaSet(criteria);
credential = resolver.resolveSingle(criteriaSet);
} catch (org.opensaml.xml.security.SecurityException e) {
throw new RuntimeException("Something went wrong reading credentials", e);
}
}
private static KeyStore readKeystoreFromFile(String pathToKeyStore, String keyStorePassword) {
try {
KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
InputStream inputStream = SPCredentials.class.getResourceAsStream(pathToKeyStore);
keystore.load(inputStream, keyStorePassword.toCharArray());
inputStream.close();
return keystore;
} catch (Exception e) {
throw new RuntimeException("Something went wrong reading keystore", e);
}
}
public static Credential getCredential() {
return credential;
}
}

Binary file not shown.

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{5} - %msg%n</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="STDOUT" />
</root>
</configuration>

View File

@@ -0,0 +1,52 @@
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
<servlet>
<servlet-name>ApplicationServlet</servlet-name>
<servlet-class>no.steras.opensamlbook.app.ApplicationServlet</servlet-class>
</servlet>
<servlet>
<servlet-name>SingleSignOnService</servlet-name>
<servlet-class>no.steras.opensamlbook.idp.SingleSignOnServlet</servlet-class>
</servlet>
<servlet>
<servlet-name>ConsumerServlet</servlet-name>
<servlet-class>no.steras.opensamlbook.sp.ConsumerServlet</servlet-class>
</servlet>
<servlet>
<servlet-name>ArtifactResolutionServlet</servlet-name>
<servlet-class>no.steras.opensamlbook.idp.ArtifactResolutionServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ApplicationServlet</servlet-name>
<url-pattern>/app/appservlet</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>SingleSignOnService</servlet-name>
<url-pattern>/idp/singleSignOnService</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>ConsumerServlet</servlet-name>
<url-pattern>/sp/consumer</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>ArtifactResolutionServlet</servlet-name>
<url-pattern>/idp/artifactResolutionService</url-pattern>
</servlet-mapping>
<filter>
<filter-name>AccessFilter</filter-name>
<filter-class>no.steras.opensamlbook.sp.AccessFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>AccessFilter</filter-name>
<url-pattern>/app/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
</filter-mapping>
</web-app>

View File

@@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8"?>
<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
<component name="FacetManager">
<facet type="web" name="Web">
<configuration>
<descriptors>
<deploymentDescriptor name="web.xml" url="file://$MODULE_DIR$/src/main/webapp/WEB-INF/web.xml" />
</descriptors>
<webroots>
<root url="file://$MODULE_DIR$/src/main/webapp" relative="/" />
</webroots>
<sourceRoots>
<root url="file://$MODULE_DIR$/src/main/java" />
<root url="file://$MODULE_DIR$/src/main/resources" />
</sourceRoots>
</configuration>
</facet>
</component>
<component name="NewModuleRootManager" inherit-compiler-output="false">
<output url="file://$MODULE_DIR$/target/classes" />
<output-test url="file://$MODULE_DIR$/target/test-classes" />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
<excludeFolder url="file://$MODULE_DIR$/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Maven: org.opensaml:opensaml:2.6.0" level="project" />
<orderEntry type="library" name="Maven: org.opensaml:openws:1.5.0" level="project" />
<orderEntry type="library" name="Maven: org.opensaml:xmltooling:1.4.0" level="project" />
<orderEntry type="library" name="Maven: org.bouncycastle:bcprov-jdk15:1.46" level="project" />
<orderEntry type="library" name="Maven: commons-codec:commons-codec:1.7" level="project" />
<orderEntry type="library" name="Maven: joda-time:joda-time:2.2" level="project" />
<orderEntry type="library" name="Maven: ca.juliusdavies:not-yet-commons-ssl:0.3.9" level="project" />
<orderEntry type="library" name="Maven: commons-httpclient:commons-httpclient:3.1" level="project" />
<orderEntry type="library" name="Maven: org.apache.santuario:xmlsec:1.5.4" level="project" />
<orderEntry type="library" scope="RUNTIME" name="Maven: org.apache.xerces:xml-apis:2.10.0" level="project" />
<orderEntry type="library" scope="RUNTIME" name="Maven: org.apache.xerces:serializer:2.10.0" level="project" />
<orderEntry type="library" scope="RUNTIME" name="Maven: xml-resolver:xml-resolver:1.2" level="project" />
<orderEntry type="library" scope="RUNTIME" name="Maven: xalan:xalan:2.7.1" level="project" />
<orderEntry type="library" name="Maven: org.slf4j:jcl-over-slf4j:1.7.5" level="project" />
<orderEntry type="library" name="Maven: org.slf4j:slf4j-api:1.7.5" level="project" />
<orderEntry type="library" name="Maven: org.slf4j:jul-to-slf4j:1.7.5" level="project" />
<orderEntry type="library" name="Maven: org.slf4j:log4j-over-slf4j:1.7.5" level="project" />
<orderEntry type="library" name="Maven: commons-collections:commons-collections:3.2.1" level="project" />
<orderEntry type="library" name="Maven: commons-lang:commons-lang:2.6" level="project" />
<orderEntry type="library" name="Maven: org.apache.velocity:velocity:1.7" level="project" />
<orderEntry type="library" name="Maven: org.owasp.esapi:esapi:2.0.1" level="project" />
<orderEntry type="library" name="Maven: xerces:xercesImpl:2.10.0" level="project" />
<orderEntry type="library" name="Maven: xml-apis:xml-apis:1.4.01" level="project" />
<orderEntry type="library" name="Maven: ch.qos.logback:logback-core:1.0.13" level="project" />
<orderEntry type="library" name="Maven: ch.qos.logback:logback-classic:1.0.13" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: javax.servlet:servlet-api:2.5" level="project" />
</component>
</module>