From caab9110c126c3115b5d3384df3eb06bcc23c194 Mon Sep 17 00:00:00 2001 From: Matt Kovacs Date: Mon, 28 Jul 2014 12:38:54 -0500 Subject: [PATCH] Initial Setup --- .gitignore | 4 + LICENSE | 176 ++++++++++++ README.md | 109 ++++++- pom.xml | 111 ++++++++ .../java/com/rackspace/saml/CertManager.java | 52 ++++ src/main/java/com/rackspace/saml/Main.java | 84 ++++++ .../rackspace/saml/SamlAssertionProducer.java | 267 ++++++++++++++++++ 7 files changed, 801 insertions(+), 2 deletions(-) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 pom.xml create mode 100644 src/main/java/com/rackspace/saml/CertManager.java create mode 100644 src/main/java/com/rackspace/saml/Main.java create mode 100644 src/main/java/com/rackspace/saml/SamlAssertionProducer.java diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bd8df66 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.idea/* +*.iml +dependency-reduced-pom.xml +target/* diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..68c771a --- /dev/null +++ b/LICENSE @@ -0,0 +1,176 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + diff --git a/README.md b/README.md index b93f5b6..4e714a2 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,107 @@ -saml-generator -============== +SAML Response Generator +======================= + +This is a small utility program that makes it easy to generate SAML responses for testing. + +Creating Private and Public Keys for Testing +-------------------------------------------- + +You will need to generate a private and public key to use for generating saml assertions. The following steps are used for creating the keys: +``` +#create the keypair +openssl req -new -x509 -days 3652 -nodes -out saml.crt -keyout saml.pem + +#convert the private key to pkcs8 format +openssl pkcs8 -topk8 -inform PEM -outform DER -in saml.pem -out saml.pkcs8 -nocrypt +``` + +Command line tool +----------------- + +You will need to create the jar file in order to use the command line tool. cd to saml-tutorial then run 'mvn package' to create a jar file called 'saml-generator-1.0.jar'. This jar file will be used to create saml assertions. + +Usage +----- + +java -jar saml-generator-1.0.jar [-domain ] [-issuer ] [-privateKey ] [-publicKey ] [-roles ] [-email ] [-samlAssertionExpirationDays ] [-subject ] + +``` +-issuer +The URI of the issuer for the saml assertion. + +-subject +The username of the federated user. + +-domain +The domain ID for the federated user. + +-roles +A comma separated list of role names for the federated user. + +-email +The email address of the federated user. + +-publicKey +THe path to the location of the public key to decrypt assertions + +-privateKey +The path to the location of the private key to use to sign assertions + +-samlAssertionExpirationDays +How long before the assertion is no longer valid +``` + +Example +------- + +java -jar saml-generator-1.0.jar -domain 7719 -issuer 'http://some.compnay.com' -privateKey saml.pkcs8 -publicKey saml.crt -roles 'role1' -samlAssertionExpirationDays 5 -subject samlUser1 + +Output: +``` + + + http://test.rackspace.com + + + + + + + + + + + + + fufQ5g8YHPZVT4tX6Xx4LfYO588= + + + LlYniaVX8EXAZDvKP396IDpDm31mJf3T8HKh4NroTSPWqEjmcN2Wj32QBjSCpzXtE7bhVoRIQQRDRWzAbMjR0gjuy6NK0z1vBQDi4iwuRM6Y+sgsDAqB9wT4h4yi6J7cjnUdNi83VRVYF3F7zVjCq//mDQVkyp+rkhC0Lkxe2kM= + + + + + + http://some.compnay.com + + samlUser + + + + + + + urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport + + + + + role1 + + + 14309 + + + + +``` diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..3e04a29 --- /dev/null +++ b/pom.xml @@ -0,0 +1,111 @@ + + 4.0.0 + com.rackspace.saml + saml-generator + 1.0 + jar + + + + + + org.apache.maven.plugins + maven-jar-plugin + 2.4 + + + + true + lib/ + com.rackspace.saml.Main + + + + + default-package + package + + jar + + + + + + + + + org.apache.maven.plugins + maven-shade-plugin + 2.1 + + + package + + shade + + + + + com.rackspace.saml.Main + + + + + + + + + *:* + + META-INF/*.SF + META-INF/*.DSA + META-INF/*.RSA + + + + + + + + + + + + junit + junit + 4.8.2 + test + + + + + org.hamcrest + hamcrest-all + 1.1 + test + + + + + javax.xml + jaxb-api + 2.1 + + + + + org.opensaml + opensaml + 2.5.3 + + + + + commons-cli + commons-cli + 1.2 + + + + \ No newline at end of file diff --git a/src/main/java/com/rackspace/saml/CertManager.java b/src/main/java/com/rackspace/saml/CertManager.java new file mode 100644 index 0000000..c0f3257 --- /dev/null +++ b/src/main/java/com/rackspace/saml/CertManager.java @@ -0,0 +1,52 @@ +package com.rackspace.saml; + +import java.io.FileInputStream; +import java.io.InputStream; +import java.io.RandomAccessFile; +import java.security.KeyFactory; +import java.security.PrivateKey; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.security.spec.PKCS8EncodedKeySpec; + +import org.opensaml.xml.security.credential.Credential; +import org.opensaml.xml.security.x509.BasicX509Credential; + +public class CertManager { + + /** + * gets credential used to sign saml assertionts that are produced. This method + * assumes the cert and pkcs formatted primary key are on file system. this data + * could be stored elsewhere e.g keystore + * + * a credential is used to sign saml response, and includes the private key + * as well as a cert for the public key + * + * @return + * @throws Throwable + */ + public Credential getSigningCredential(String publicKeyLocation, String privateKeyLocation) throws Throwable { + // create public key (cert) portion of credential + InputStream inStream = new FileInputStream(publicKeyLocation); + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + X509Certificate publicKey = (X509Certificate)cf.generateCertificate(inStream); + inStream.close(); + + // create private key + RandomAccessFile raf = new RandomAccessFile(privateKeyLocation, "r"); + byte[] buf = new byte[(int)raf.length()]; + raf.readFully(buf); + raf.close(); + + PKCS8EncodedKeySpec kspec = new PKCS8EncodedKeySpec(buf); + KeyFactory kf = KeyFactory.getInstance("RSA"); + PrivateKey privateKey = kf.generatePrivate(kspec); + + // create credential and initialize + BasicX509Credential credential = new BasicX509Credential(); + credential.setEntityCertificate(publicKey); + credential.setPrivateKey(privateKey); + + return credential; + } +} diff --git a/src/main/java/com/rackspace/saml/Main.java b/src/main/java/com/rackspace/saml/Main.java new file mode 100644 index 0000000..ae72012 --- /dev/null +++ b/src/main/java/com/rackspace/saml/Main.java @@ -0,0 +1,84 @@ +package com.rackspace.saml; + +import java.io.ByteArrayOutputStream; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; + +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.CommandLineParser; +import org.apache.commons.cli.GnuParser; +import org.apache.commons.cli.HelpFormatter; +import org.apache.commons.cli.Options; +import org.joda.time.DateTime; +import org.opensaml.saml2.core.Response; +import org.opensaml.saml2.core.impl.ResponseMarshaller; +import org.opensaml.xml.util.XMLHelper; +import org.w3c.dom.Element; + +public class Main { + + public static void main(String[] args) { + try { + HashMap> attributes = new HashMap>(); + String issuer = null; + String subject = null; + String privateKey = null; + String publicKey = null; + Integer samlAssertionExpirationDays = null; + + Options options = new Options(); + options.addOption("issuer", true, "Issuer for saml assertion"); + options.addOption("subject", true, "Subject of saml assertion"); + options.addOption("email", true, "Email associated with the subject"); + options.addOption("domain", true, "Domain attribute"); + options.addOption("roles", true, "Comma separated list of roles"); + options.addOption("publicKey", true, "Location of public key to decrypt assertion"); + options.addOption("privateKey", true, "Location or private key use to sign assertion"); + options.addOption("samlAssertionExpirationDays", true, "How long before assertion is no longer valid. Can be negative."); + + CommandLineParser parser = new GnuParser(); + CommandLine cmd = parser.parse(options, args); + + if (args.length == 0) { + HelpFormatter formatter = new HelpFormatter(); + formatter.printHelp( "saml-util-1.0", options, true); + System.exit(1); + } + + issuer = cmd.getOptionValue("issuer"); + subject = cmd.getOptionValue("subject"); + privateKey = cmd.getOptionValue("privateKey"); + publicKey = cmd.getOptionValue("publicKey"); + + samlAssertionExpirationDays = cmd.getOptionValue("samlAssertionExpirationDays") != null ? Integer.valueOf(cmd.getOptionValue("samlAssertionExpirationDays")) : null; + + if (cmd.getOptionValue("domain") != null) + attributes.put("domain", Arrays.asList(cmd.getOptionValue("domain"))); + + if (cmd.getOptionValue("roles") != null) + attributes.put("roles", Arrays.asList(cmd.getOptionValue("roles").split(","))); + + if (cmd.getOptionValue("email") != null) + attributes.put("email", Arrays.asList(cmd.getOptionValue("email"))); + + SamlAssertionProducer producer = new SamlAssertionProducer(); + producer.setPrivateKeyLocation(privateKey); + producer.setPublicKeyLocation(publicKey); + + Response responseInitial = producer.createSAMLResponse(subject, new DateTime(), "password", attributes, issuer, samlAssertionExpirationDays); + + ResponseMarshaller marshaller = new ResponseMarshaller(); + Element element = marshaller.marshall(responseInitial); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + XMLHelper.writeNode(element, baos); + String responseStr = new String(baos.toByteArray()); + + System.out.println(responseStr); + + } catch (Throwable t) { + t.printStackTrace(); + } + } +} diff --git a/src/main/java/com/rackspace/saml/SamlAssertionProducer.java b/src/main/java/com/rackspace/saml/SamlAssertionProducer.java new file mode 100644 index 0000000..a6350c0 --- /dev/null +++ b/src/main/java/com/rackspace/saml/SamlAssertionProducer.java @@ -0,0 +1,267 @@ +package com.rackspace.saml; + +import java.io.ByteArrayOutputStream; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; + +import org.joda.time.DateTime; +import org.opensaml.DefaultBootstrap; +import org.opensaml.common.SAMLVersion; +import org.opensaml.saml2.core.Assertion; +import org.opensaml.saml2.core.Attribute; +import org.opensaml.saml2.core.AttributeStatement; +import org.opensaml.saml2.core.AttributeValue; +import org.opensaml.saml2.core.AuthnContext; +import org.opensaml.saml2.core.AuthnContextClassRef; +import org.opensaml.saml2.core.AuthnRequest; +import org.opensaml.saml2.core.AuthnStatement; +import org.opensaml.saml2.core.Issuer; +import org.opensaml.saml2.core.NameID; +import org.opensaml.saml2.core.Response; +import org.opensaml.saml2.core.Status; +import org.opensaml.saml2.core.StatusCode; +import org.opensaml.saml2.core.Subject; +import org.opensaml.saml2.core.SubjectConfirmation; +import org.opensaml.saml2.core.SubjectConfirmationData; +import org.opensaml.saml2.core.impl.AssertionBuilder; +import org.opensaml.saml2.core.impl.AttributeBuilder; +import org.opensaml.saml2.core.impl.AttributeStatementBuilder; +import org.opensaml.saml2.core.impl.AuthnContextBuilder; +import org.opensaml.saml2.core.impl.AuthnContextClassRefBuilder; +import org.opensaml.saml2.core.impl.AuthnStatementBuilder; +import org.opensaml.saml2.core.impl.IssuerBuilder; +import org.opensaml.saml2.core.impl.NameIDBuilder; +import org.opensaml.saml2.core.impl.ResponseBuilder; +import org.opensaml.saml2.core.impl.ResponseMarshaller; +import org.opensaml.saml2.core.impl.StatusBuilder; +import org.opensaml.saml2.core.impl.StatusCodeBuilder; +import org.opensaml.saml2.core.impl.SubjectBuilder; +import org.opensaml.saml2.core.impl.SubjectConfirmationBuilder; +import org.opensaml.saml2.core.impl.SubjectConfirmationDataBuilder; +import org.opensaml.xml.schema.XSString; +import org.opensaml.xml.schema.impl.XSStringBuilder; +import org.opensaml.xml.signature.Signature; +import org.opensaml.xml.signature.SignatureConstants; +import org.opensaml.xml.signature.Signer; +import org.opensaml.xml.signature.impl.SignatureBuilder; +import org.opensaml.xml.util.XMLHelper; +import org.w3c.dom.Element; + + +public class SamlAssertionProducer { + + private String privateKeyLocation; + private String publicKeyLocation; + private CertManager certManager = new CertManager(); + + public Response createSAMLResponse(final String subjectId, final DateTime authenticationTime, + final String credentialType, final HashMap> attributes, String issuer, Integer samlAssertionDays) { + + try { + DefaultBootstrap.bootstrap(); + + Signature signature = createSignature(); + Status status = createStatus(); + Issuer responseIssuer = null; + Issuer assertionIssuer = null; + Subject subject = null; + AttributeStatement attributeStatement = null; + + if (issuer != null) { + responseIssuer = createIssuer(issuer); + assertionIssuer = createIssuer(issuer); + } + + if (subjectId != null) { + subject = createSubject(subjectId, samlAssertionDays); + } + + if (attributes != null && attributes.size() != 0) { + attributeStatement = createAttributeStatement(attributes); + } + + AuthnStatement authnStatement = createAuthnStatement(authenticationTime); + + Assertion assertion = createAssertion(new DateTime(), subject, assertionIssuer, authnStatement, attributeStatement); + + Response response = createResponse(new DateTime(), responseIssuer, status, assertion); + response.setSignature(signature); + + ResponseMarshaller marshaller = new ResponseMarshaller(); + Element element = marshaller.marshall(response); + + if (signature != null) { + Signer.signObject(signature); + } + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + XMLHelper.writeNode(element, baos); + + return response; + + } catch (Throwable t) { + t.printStackTrace(); + return null; + } + } + + public String getPrivateKeyLocation() { + return privateKeyLocation; + } + + public void setPrivateKeyLocation(String privateKeyLocation) { + this.privateKeyLocation = privateKeyLocation; + } + + public String getPublicKeyLocation() { + return publicKeyLocation; + } + + public void setPublicKeyLocation(String publicKeyLocation) { + this.publicKeyLocation = publicKeyLocation; + } + + private Response createResponse(final DateTime issueDate, Issuer issuer, Status status, Assertion assertion) { + ResponseBuilder responseBuilder = new ResponseBuilder(); + Response response = responseBuilder.buildObject(); + response.setID(UUID.randomUUID().toString()); + response.setIssueInstant(issueDate); + response.setVersion(SAMLVersion.VERSION_20); + response.setIssuer(issuer); + response.setStatus(status); + response.getAssertions().add(assertion); + return response; + } + + private Assertion createAssertion(final DateTime issueDate, Subject subject, Issuer issuer, AuthnStatement authnStatement, + AttributeStatement attributeStatement) { + AssertionBuilder assertionBuilder = new AssertionBuilder(); + Assertion assertion = assertionBuilder.buildObject(); + assertion.setID(UUID.randomUUID().toString()); + assertion.setIssueInstant(issueDate); + assertion.setSubject(subject); + assertion.setIssuer(issuer); + + if (authnStatement != null) + assertion.getAuthnStatements().add(authnStatement); + + if (attributeStatement != null) + assertion.getAttributeStatements().add(attributeStatement); + + return assertion; + } + + private Issuer createIssuer(final String issuerName) { + // create Issuer object + IssuerBuilder issuerBuilder = new IssuerBuilder(); + Issuer issuer = issuerBuilder.buildObject(); + issuer.setValue(issuerName); + return issuer; + } + + private Subject createSubject(final String subjectId, final Integer samlAssertionDays) { + DateTime currentDate = new DateTime(); + if (samlAssertionDays != null) + currentDate = currentDate.plusDays(samlAssertionDays); + + // create name element + NameIDBuilder nameIdBuilder = new NameIDBuilder(); + NameID nameId = nameIdBuilder.buildObject(); + nameId.setValue(subjectId); + nameId.setFormat("urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"); + + SubjectConfirmationDataBuilder dataBuilder = new SubjectConfirmationDataBuilder(); + SubjectConfirmationData subjectConfirmationData = dataBuilder.buildObject(); + subjectConfirmationData.setNotOnOrAfter(currentDate); + + SubjectConfirmationBuilder subjectConfirmationBuilder = new SubjectConfirmationBuilder(); + SubjectConfirmation subjectConfirmation = subjectConfirmationBuilder.buildObject(); + subjectConfirmation.setMethod("urn:oasis:names:tc:SAML:2.0:cm:bearer"); + subjectConfirmation.setSubjectConfirmationData(subjectConfirmationData); + + // create subject element + SubjectBuilder subjectBuilder = new SubjectBuilder(); + Subject subject = subjectBuilder.buildObject(); + subject.setNameID(nameId); + subject.getSubjectConfirmations().add(subjectConfirmation); + + return subject; + } + + private AuthnStatement createAuthnStatement(final DateTime issueDate) { + // create authcontextclassref object + AuthnContextClassRefBuilder classRefBuilder = new AuthnContextClassRefBuilder(); + AuthnContextClassRef classRef = classRefBuilder.buildObject(); + classRef.setAuthnContextClassRef("urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport"); + + // create authcontext object + AuthnContextBuilder authContextBuilder = new AuthnContextBuilder(); + AuthnContext authnContext = authContextBuilder.buildObject(); + authnContext.setAuthnContextClassRef(classRef); + + // create authenticationstatement object + AuthnStatementBuilder authStatementBuilder = new AuthnStatementBuilder(); + AuthnStatement authnStatement = authStatementBuilder.buildObject(); + authnStatement.setAuthnInstant(issueDate); + authnStatement.setAuthnContext(authnContext); + + return authnStatement; + } + + private AttributeStatement createAttributeStatement(HashMap> attributes) { + // create authenticationstatement object + AttributeStatementBuilder attributeStatementBuilder = new AttributeStatementBuilder(); + AttributeStatement attributeStatement = attributeStatementBuilder.buildObject(); + + AttributeBuilder attributeBuilder = new AttributeBuilder(); + if (attributes != null) { + for (Map.Entry> entry : attributes.entrySet()) { + Attribute attribute = attributeBuilder.buildObject(); + attribute.setName(entry.getKey()); + + for (String value : entry.getValue()) { + XSStringBuilder stringBuilder = new XSStringBuilder(); + XSString attributeValue = stringBuilder.buildObject(AttributeValue.DEFAULT_ELEMENT_NAME, XSString.TYPE_NAME); + attributeValue.setValue(value); + attribute.getAttributeValues().add(attributeValue); + } + + attributeStatement.getAttributes().add(attribute); + } + } + + return attributeStatement; + } + + private Status createStatus() { + StatusCodeBuilder statusCodeBuilder = new StatusCodeBuilder(); + StatusCode statusCode = statusCodeBuilder.buildObject(); + statusCode.setValue(StatusCode.SUCCESS_URI); + + StatusBuilder statusBuilder = new StatusBuilder(); + Status status = statusBuilder.buildObject(); + status.setStatusCode(statusCode); + + return status; + } + + private Signature createSignature() throws Throwable { + if (publicKeyLocation != null && privateKeyLocation != null) { + SignatureBuilder builder = new SignatureBuilder(); + Signature signature = builder.buildObject(); + signature.setSigningCredential(certManager.getSigningCredential(publicKeyLocation, privateKeyLocation)); + signature.setSignatureAlgorithm(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA1); + signature.setCanonicalizationAlgorithm(SignatureConstants.ALGO_ID_C14N_EXCL_OMIT_COMMENTS); + + return signature; + } + + return null; + } +}