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.jwt;
019
020
021import java.security.PrivateKey;
022import java.security.Provider;
023import java.security.interfaces.ECPrivateKey;
024import java.security.interfaces.RSAPrivateKey;
025import java.util.Collections;
026import java.util.HashSet;
027import java.util.List;
028import java.util.Set;
029
030import com.nimbusds.jose.JOSEException;
031import com.nimbusds.jose.JWSAlgorithm;
032import com.nimbusds.jose.JWSHeader;
033import com.nimbusds.jose.JWSSigner;
034import com.nimbusds.jose.crypto.ECDSASigner;
035import com.nimbusds.jose.crypto.MACSigner;
036import com.nimbusds.jose.crypto.RSASSASigner;
037import com.nimbusds.jose.jwk.Curve;
038import com.nimbusds.jose.util.Base64;
039import com.nimbusds.jose.util.Base64URL;
040import com.nimbusds.jwt.SignedJWT;
041import com.nimbusds.oauth2.sdk.auth.Secret;
042
043
044/**
045 * Static JWT bearer assertion factory.
046 *
047 * <p>Related specifications:
048 *
049 * <ul>
050 *     <li>Assertion Framework for OAuth 2.0 Client Authentication and
051 *         Authorization Grants (RFC 7521).
052 *     <li>JSON Web Token (JWT) Profile for OAuth 2.0 Client Authentication and
053 *         Authorization Grants (RFC 7523).
054 * </ul>
055 */
056public class JWTAssertionFactory {
057
058
059        /**
060         * Returns the supported signature JSON Web Algorithms (JWAs).
061         *
062         * @return The supported JSON Web Algorithms (JWAs).
063         */
064        public static Set<JWSAlgorithm> supportedJWAs() {
065
066                Set<JWSAlgorithm> supported = new HashSet<>();
067                supported.addAll(JWSAlgorithm.Family.HMAC_SHA);
068                supported.addAll(JWSAlgorithm.Family.RSA);
069                supported.addAll(JWSAlgorithm.Family.EC);
070                return Collections.unmodifiableSet(supported);
071        }
072
073
074        /**
075         * Creates a new HMAC-protected JWT bearer assertion.
076         *
077         * @param details      The JWT bearer assertion details. Must not be
078         *                     {@code null}.
079         * @param jwsAlgorithm The expected HMAC algorithm (HS256, HS384 or
080         *                     HS512) for the JWT assertion. Must be supported
081         *                     and not {@code null}.
082         * @param secret       The secret. Must be at least 256-bits long.
083         *
084         * @return The JWT bearer assertion.
085         *
086         * @throws JOSEException If the client secret is too short, or HMAC
087         *                       computation failed.
088         */
089        public static SignedJWT create(final JWTAssertionDetails details,
090                                       final JWSAlgorithm jwsAlgorithm,
091                                       final Secret secret)
092                throws JOSEException {
093
094                SignedJWT signedJWT = new SignedJWT(new JWSHeader(jwsAlgorithm), details.toJWTClaimsSet());
095                signedJWT.sign(new MACSigner(secret.getValueBytes()));
096                return signedJWT;
097        }
098
099
100        /**
101         * Creates a new signed JWT bearer assertion.
102         *
103         * @param details      The JWT bearer assertion details. Must not be
104         *                     {@code null}.
105         * @param jwsAlgorithm The expected RSA (RS256, RS384, RS512, PS256,
106         *                     PS384 or PS512) or EC (ES256, ES384, ES512)
107         *                     signature algorithm for the JWT assertion. Must
108         *                     be supported and not {@code null}.
109         * @param privateKey   The signing private RSA or EC key. Must not be
110         *                     {@code null}.
111         * @param keyID        Optional identifier for the key, to aid key
112         *                     selection on the recipient side. Recommended.
113         *                     {@code null} if not specified.
114         * @param x5c          Optional X.509 certificate chain for the public
115         *                     key, {@code null} if not specified.
116         * @param x5t256       Optional X.509 certificate SHA-256 thumbprint,
117         *                     {@code null} if not specified.
118         * @param jcaProvider  Optional specific JCA provider, {@code null} to
119         *                     use the default one.
120         *
121         * @return The JWT bearer assertion.
122         *
123         * @throws JOSEException If signing failed.
124         */
125        public static SignedJWT create(final JWTAssertionDetails details,
126                                       final JWSAlgorithm jwsAlgorithm,
127                                       final PrivateKey privateKey,
128                                       final String keyID,
129                                       final List<Base64> x5c,
130                                       final Base64URL x5t256,
131                                       final Provider jcaProvider)
132                throws JOSEException {
133
134                SignedJWT signedJWT = new SignedJWT(
135                        new JWSHeader.Builder(jwsAlgorithm)
136                                .keyID(keyID)
137                                .x509CertChain(x5c)
138                                .x509CertSHA256Thumbprint(x5t256)
139                                .build(),
140                        details.toJWTClaimsSet());
141                
142                final JWSSigner signer;
143                if (RSASSASigner.SUPPORTED_ALGORITHMS.contains(jwsAlgorithm)) {
144                        signer = new RSASSASigner(privateKey);
145                } else if (ECDSASigner.SUPPORTED_ALGORITHMS.contains(jwsAlgorithm)) {
146                        Set<Curve> curves = Curve.forJWSAlgorithm(jwsAlgorithm);
147                        if (curves.size() != 1) {
148                                throw new JOSEException("Couldn't determine curve for JWS algorithm: " + jwsAlgorithm);
149                        }
150                        signer = new ECDSASigner(privateKey, curves.iterator().next());
151                } else {
152                        throw new JOSEException("Unsupported JWS algorithm: " + jwsAlgorithm);
153                }
154                
155                if (jcaProvider != null) {
156                        signer.getJCAContext().setProvider(jcaProvider);
157                }
158                
159                signedJWT.sign(signer);
160                
161                return signedJWT;
162        }
163
164
165        /**
166         * Creates a new RSA-signed JWT bearer assertion.
167         *
168         * @param details       The JWT bearer assertion details. Must not be
169         *                      {@code null}.
170         * @param jwsAlgorithm  The expected RSA signature algorithm (RS256,
171         *                      RS384, RS512, PS256, PS384 or PS512) for the
172         *                      JWT assertion. Must be supported and not
173         *                      {@code null}.
174         * @param rsaPrivateKey The RSA private key. Must not be {@code null}.
175         * @param keyID         Optional identifier for the RSA key, to aid key
176         *                      selection on the recipient side. Recommended.
177         *                      {@code null} if not specified.
178         * @param jcaProvider   Optional specific JCA provider, {@code null} to
179         *                      use the default one.
180         *
181         * @return The JWT bearer assertion.
182         *
183         * @throws JOSEException If RSA signing failed.
184         */
185        @Deprecated
186        public static SignedJWT create(final JWTAssertionDetails details,
187                                       final JWSAlgorithm jwsAlgorithm,
188                                       final RSAPrivateKey rsaPrivateKey,
189                                       final String keyID,
190                                       final Provider jcaProvider)
191                throws JOSEException {
192
193                return create(details, jwsAlgorithm, rsaPrivateKey, keyID, null, null, jcaProvider);
194        }
195
196
197        /**
198         * Creates a new EC-signed JWT bearer assertion.
199         *
200         * @param details      The JWT bearer assertion details. Must not be
201         *                     {@code null}.
202         * @param jwsAlgorithm The expected EC signature algorithm (ES256,
203         *                     ES384 or ES512) for the JWT assertion. Must be
204         *                     supported and not {@code null}.
205         * @param ecPrivateKey The EC private key. Must not be {@code null}.
206         * @param keyID        Optional identifier for the EC key, to aid key
207         *                     selection on the recipient side. Recommended.
208         *                     {@code null} if not specified.
209         * @param jcaProvider  Optional specific JCA provider, {@code null} to
210         *                     use the default one.
211         *
212         * @return The JWT bearer assertion.
213         *
214         * @throws JOSEException If RSA signing failed.
215         */
216        @Deprecated
217        public static SignedJWT create(final JWTAssertionDetails details,
218                                       final JWSAlgorithm jwsAlgorithm,
219                                       final ECPrivateKey ecPrivateKey,
220                                       final String keyID,
221                                       final Provider jcaProvider)
222                throws JOSEException {
223
224                return create(details, jwsAlgorithm, ecPrivateKey, keyID, null, null, jcaProvider);
225        }
226
227
228        /**
229         * Prevents public instantiation.
230         */
231        private JWTAssertionFactory() {}
232}