001/*
002 * oauth2-oidc-sdk
003 *
004 * Copyright 2012-2016, Connect2id Ltd and contributors.
005 *
006 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use
007 * this file except in compliance with the License. You may obtain a copy of the
008 * License at
009 *
010 *    http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software distributed
013 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
014 * CONDITIONS OF ANY KIND, either express or implied. See the License for the
015 * specific language governing permissions and limitations under the License.
016 */
017
018package com.nimbusds.oauth2.sdk.assertions.saml2;
019
020
021import java.security.interfaces.RSAPrivateKey;
022
023import com.nimbusds.oauth2.sdk.SerializeException;
024import net.jcip.annotations.ThreadSafe;
025import org.opensaml.Configuration;
026import org.opensaml.saml2.core.Assertion;
027import org.opensaml.saml2.core.impl.AssertionMarshaller;
028import org.opensaml.xml.io.MarshallerFactory;
029import org.opensaml.xml.io.MarshallingException;
030import org.opensaml.xml.security.credential.BasicCredential;
031import org.opensaml.xml.security.credential.Credential;
032import org.opensaml.xml.security.credential.UsageType;
033import org.opensaml.xml.signature.Signature;
034import org.opensaml.xml.signature.SignatureConstants;
035import org.opensaml.xml.signature.SignatureException;
036import org.opensaml.xml.signature.Signer;
037import org.opensaml.xml.util.XMLHelper;
038import org.w3c.dom.Element;
039
040
041/**
042 * Static SAML 2.0 bearer assertion factory.
043 *
044 * <p>Related specifications:
045 *
046 * <ul>
047 *     <li>Assertion Framework for OAuth 2.0 Client Authentication and
048 *         Authorization Grants (RFC 7521).
049 *     <li>Security Assertion Markup Language (SAML) 2.0 Profile for OAuth 2.0
050 *         Client Authentication and Authorization Grants (RFC 7522).
051 * </ul>
052 */
053@ThreadSafe
054public class SAML2AssertionFactory {
055
056
057        /**
058         * Creates a new SAML 2.0 assertion.
059         *
060         * @param details    The SAML 2.0 bearer assertion details. Must not
061         *                   be {@code null}.
062         * @param xmlDsigAlg The XML digital signature algorithm. Must not be
063         *                   {@code null}.
064         * @param credential The appropriate credentials to facilitate signing
065         *                   of the assertion.
066         *
067         * @return The SAML 2.0 bearer assertion.
068         *
069         * @throws SerializeException If serialisation or signing failed.
070         */
071        public static Assertion create(final SAML2AssertionDetails details,
072                                       final String xmlDsigAlg,
073                                       final Credential credential) {
074
075                Assertion a = details.toSAML2Assertion();
076
077                // Create signature element
078                Signature signature = (Signature) Configuration.getBuilderFactory()
079                        .getBuilder(Signature.DEFAULT_ELEMENT_NAME)
080                        .buildObject(Signature.DEFAULT_ELEMENT_NAME);
081
082                signature.setSigningCredential(credential);
083                signature.setSignatureAlgorithm(xmlDsigAlg);
084                signature.setCanonicalizationAlgorithm(SignatureConstants.ALGO_ID_C14N_EXCL_OMIT_COMMENTS);
085
086                a.setSignature(signature);
087
088                MarshallerFactory marshallerFactory = Configuration.getMarshallerFactory();
089
090                try {
091                        // Perform actual signing
092                        marshallerFactory.getMarshaller(a).marshall(a);
093                        Signer.signObject(signature);
094                } catch (MarshallingException | SignatureException e) {
095                        throw new SerializeException(e.getMessage(), e);
096                }
097
098                return a;
099        }
100
101
102        /**
103         * Creates a new SAML 2.0 assertion as an XML element.
104         *
105         * @param details    The SAML 2.0 bearer assertion details. Must not
106         *                   be {@code null}.
107         * @param xmlDsigAlg The XML digital signature algorithm. Must not be
108         *                   {@code null}.
109         * @param credential The appropriate credentials to facilitate signing
110         *                   of the assertion.
111         *
112         * @return The SAML 2.0 bearer assertion as an XML element.
113         *
114         * @throws SerializeException If serialisation or signing failed.
115         */
116        public static Element createAsElement(final SAML2AssertionDetails details,
117                                              final String xmlDsigAlg,
118                                              final Credential credential) {
119
120                Assertion a = create(details, xmlDsigAlg, credential);
121                AssertionMarshaller assertionMarshaller = new AssertionMarshaller();
122                try {
123                        return assertionMarshaller.marshall(a);
124                } catch (MarshallingException e) {
125                        throw new SerializeException(e.getMessage(), e);
126                }
127        }
128
129
130        /**
131         * Creates a new SAML 2.0 assertion as an XML string.
132         *
133         * @param details    The SAML 2.0 bearer assertion details. Must not
134         *                   be {@code null}.
135         * @param xmlDsigAlg The XML digital signature algorithm. Must not be
136         *                   {@code null}.
137         * @param credential The appropriate credentials to facilitate signing
138         *                   of the assertion.
139         *
140         * @return The SAML 2.0 bearer assertion as an XML string. Note that
141         *         an XML declaration is not present in the output string.
142         *
143         * @throws SerializeException If serialisation or signing failed.
144         */
145        public static String createAsString(final SAML2AssertionDetails details,
146                                            final String xmlDsigAlg,
147                                            final Credential credential) {
148
149                Element a = createAsElement(details, xmlDsigAlg, credential);
150                String xml = XMLHelper.nodeToString(a);
151                // Strip XML doc declaration
152                final String header = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
153                return xml.substring(header.length());
154        }
155
156
157        /**
158         * Creates a new SAML 2.0 assertion as an XML string, signed with the
159         * RSA-SHA256 XML digital signature algorithm (mandatory to implement).
160         *
161         * @param details       The SAML 2.0 bearer assertion details. Must not
162         *                      be {@code null}.
163         * @param rsaPrivateKey The private RSA key to sign the assertion. Must
164         *                      not be {@code null}.
165         *
166         * @return The SAML 2.0 bearer assertion as an XML string. Note that
167         *         an XML declaration is not present in the output string.
168         *
169         * @throws SerializeException If serialisation or signing failed.
170         */
171        public static String createAsString(final SAML2AssertionDetails details,
172                                            final RSAPrivateKey rsaPrivateKey) {
173
174                BasicCredential credential = new BasicCredential();
175                credential.setPrivateKey(rsaPrivateKey);
176                credential.setUsageType(UsageType.SIGNING);
177                return createAsString(details, SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA256, credential);
178        }
179
180
181        /**
182         * Prevents public instantiation.
183         */
184        private SAML2AssertionFactory() {}
185}