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.auth;
019
020
021import java.net.URI;
022import java.security.PrivateKey;
023import java.security.Provider;
024import java.security.interfaces.ECPrivateKey;
025import java.security.interfaces.RSAPrivateKey;
026import java.util.*;
027
028import net.jcip.annotations.Immutable;
029
030import com.nimbusds.common.contenttype.ContentType;
031import com.nimbusds.jose.JOSEException;
032import com.nimbusds.jose.JWSAlgorithm;
033import com.nimbusds.jose.util.Base64;
034import com.nimbusds.jose.util.Base64URL;
035import com.nimbusds.jwt.SignedJWT;
036import com.nimbusds.oauth2.sdk.ParseException;
037import com.nimbusds.oauth2.sdk.assertions.jwt.JWTAssertionFactory;
038import com.nimbusds.oauth2.sdk.http.HTTPRequest;
039import com.nimbusds.oauth2.sdk.id.Audience;
040import com.nimbusds.oauth2.sdk.id.ClientID;
041import com.nimbusds.oauth2.sdk.util.URLUtils;
042
043
044/**
045 * Private key JWT authentication at the Token endpoint. Implements
046 * {@link ClientAuthenticationMethod#PRIVATE_KEY_JWT}.
047 *
048 * <p>Supported signature JSON Web Algorithms (JWAs) by this implementation:
049 *
050 * <ul>
051 *     <li>RS256
052 *     <li>RS384
053 *     <li>RS512
054 *     <li>PS256
055 *     <li>PS384
056 *     <li>PS512
057 *     <li>ES256
058 *     <li>ES256K
059 *     <li>ES384
060 *     <li>ES512
061 * </ul>
062 *
063 * <p>Example {@link com.nimbusds.oauth2.sdk.TokenRequest} with private key JWT
064 * authentication:
065 *
066 * <pre>
067 * POST /token HTTP/1.1
068 * Host: server.example.com
069 * Content-Type: application/x-www-form-urlencoded
070 *
071 * grant_type=authorization_code&amp;
072 * code=i1WsRn1uB1&amp;
073 * client_id=s6BhdRkqt3&amp;
074 * client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer&amp;
075 * client_assertion=PHNhbWxwOl...[omitted for brevity]...ZT
076 * </pre>
077 *
078 * <p>Related specifications:
079 *
080 * <ul>
081 *     <li>Assertion Framework for OAuth 2.0 Client Authentication and
082 *         Authorization Grants (RFC 7521).
083 *     <li>JSON Web Token (JWT) Profile for OAuth 2.0 Client Authentication and
084 *         Authorization Grants (RFC 7523)
085 * </ul>
086 */
087@Immutable
088public final class PrivateKeyJWT extends JWTAuthentication {
089
090
091        /**
092         * Returns the supported signature JSON Web Algorithms (JWAs).
093         *
094         * @return The supported JSON Web Algorithms (JWAs).
095         */
096        public static Set<JWSAlgorithm> supportedJWAs() {
097
098                Set<JWSAlgorithm> supported = new HashSet<>();
099                supported.addAll(JWSAlgorithm.Family.RSA);
100                supported.addAll(JWSAlgorithm.Family.EC);
101                return Collections.unmodifiableSet(supported);
102        }
103
104
105        /**
106         * Creates a new private key JWT authentication. The expiration
107         * time (exp) is set to five minutes from the current system time.
108         * Generates a default identifier (jti) for the JWT. The issued-at
109         * (iat) and not-before (nbf) claims are not set.
110         *
111         * @param clientID     The client identifier. Must not be {@code null}.
112         * @param endpoint     The endpoint URI where the client will submit
113         *                     the JWT authentication, for example the token
114         *                     endpoint. Must not be {@code null}.
115         * @param jwsAlgorithm The expected RSA (RS256, RS384, RS512, PS256,
116         *                     PS384 or PS512) or EC (ES256, ES384, ES512)
117         *                     signature algorithm for the JWT assertion. Must
118         *                     be supported and not {@code null}.
119         * @param privateKey   The signing private RSA or EC key. Must not be
120         *                     {@code null}.
121         * @param keyID        Optional identifier for the key, to aid key
122         *                     selection on the recipient side. Recommended.
123         *                     {@code null} if not specified.
124         * @param jcaProvider  Optional specific JCA provider, {@code null} to
125         *                     use the default one.
126         *
127         * @throws JOSEException If RSA signing failed.
128         */
129        public PrivateKeyJWT(final ClientID clientID,
130                             final URI endpoint,
131                             final JWSAlgorithm jwsAlgorithm,
132                             final PrivateKey privateKey,
133                             final String keyID,
134                             final Provider jcaProvider)
135                throws JOSEException {
136
137                this(new JWTAuthenticationClaimsSet(clientID, new Audience(endpoint.toString())),
138                        jwsAlgorithm,
139                        privateKey,
140                        keyID,
141                        null,
142                        null,
143                        jcaProvider);
144        }
145
146
147        /**
148         * Creates a new private key JWT authentication. The expiration
149         * time (exp) is set to five minutes from the current system time.
150         * Generates a default identifier (jti) for the JWT. The issued-at
151         * (iat) and not-before (nbf) claims are not set.
152         *
153         * @param clientID     The client identifier. Must not be {@code null}.
154         * @param endpoint     The endpoint URI where the client will submit
155         *                     the JWT authentication, for example the token
156         *                     endpoint. Must not be {@code null}.
157         * @param jwsAlgorithm The expected RSA (RS256, RS384, RS512, PS256,
158         *                     PS384 or PS512) or EC (ES256, ES384, ES512)
159         *                     signature algorithm for the JWT assertion. Must
160         *                     be supported and not {@code null}.
161         * @param privateKey   The signing private RSA or EC key. Must not be
162         *                     {@code null}.
163         * @param keyID        Optional identifier for the key, to aid key
164         *                     selection on the recipient side. Recommended.
165         *                     {@code null} if not specified.
166         * @param x5c          Optional X.509 certificate chain for the public
167         *                     key, {@code null} if not specified.
168         * @param x5t256       Optional X.509 certificate SHA-256 thumbprint,
169         *                     {@code null} if not specified.
170         * @param jcaProvider  Optional specific JCA provider, {@code null} to
171         *                     use the default one.
172         *
173         * @throws JOSEException If RSA signing failed.
174         */
175        public PrivateKeyJWT(final ClientID clientID,
176                             final URI endpoint,
177                             final JWSAlgorithm jwsAlgorithm,
178                             final PrivateKey privateKey,
179                             final String keyID,
180                             final List<Base64> x5c,
181                             final Base64URL x5t256,
182                             final Provider jcaProvider)
183                throws JOSEException {
184
185                this(new JWTAuthenticationClaimsSet(clientID, new Audience(endpoint.toString())),
186                        jwsAlgorithm,
187                        privateKey,
188                        keyID,
189                        x5c,
190                        x5t256,
191                        jcaProvider);
192        }
193        
194        
195        /**
196         * Creates a new private key JWT authentication.
197         *
198         * @param jwtAuthClaimsSet The JWT authentication claims set. Must not
199         *                         be {@code null}.
200         * @param jwsAlgorithm     The expected RSA (RS256, RS384, RS512,
201         *                         PS256, PS384 or PS512) or EC (ES256, ES384,
202         *                         ES512) signature algorithm for the JWT
203         *                         assertion. Must be supported and not
204         *                         {@code null}.
205         * @param privateKey       The signing private RSA or EC key. Must not
206         *                         be {@code null}.
207         * @param keyID            Optional identifier for the key, to aid key
208         *                         selection on the recipient side.
209         *                         Recommended. {@code null} if not specified.
210         * @param jcaProvider      Optional specific JCA provider, {@code null}
211         *                         to use the default one.
212         *
213         * @throws JOSEException If RSA signing failed.
214         */
215        public PrivateKeyJWT(final JWTAuthenticationClaimsSet jwtAuthClaimsSet,
216                             final JWSAlgorithm jwsAlgorithm,
217                             final PrivateKey privateKey,
218                             final String keyID,
219                             final Provider jcaProvider)
220                throws JOSEException {
221                
222                this(jwtAuthClaimsSet, jwsAlgorithm, privateKey, keyID, null, null, jcaProvider);
223        }
224        
225        
226        /**
227         * Creates a new private key JWT authentication.
228         *
229         * @param jwtAuthClaimsSet The JWT authentication claims set. Must not
230         *                         be {@code null}.
231         * @param jwsAlgorithm     The expected RSA (RS256, RS384, RS512,
232         *                         PS256, PS384 or PS512) or EC (ES256, ES384,
233         *                         ES512) signature algorithm for the JWT
234         *                         assertion. Must be supported and not
235         *                         {@code null}.
236         * @param privateKey       The signing private RSA or EC key. Must not
237         *                         be {@code null}.
238         * @param keyID            Optional identifier for the key, to aid key
239         *                         selection on the recipient side.
240         *                         Recommended. {@code null} if not specified.
241         * @param x5c              Optional X.509 certificate chain for the
242         *                         public key, {@code null} if not specified.
243         * @param x5t256           Optional X.509 certificate SHA-256
244         *                         thumbprint, {@code null} if not specified.
245         * @param jcaProvider      Optional specific JCA provider, {@code null}
246         *                         to use the default one.
247         *
248         * @throws JOSEException If RSA signing failed.
249         */
250        public PrivateKeyJWT(final JWTAuthenticationClaimsSet jwtAuthClaimsSet,
251                             final JWSAlgorithm jwsAlgorithm,
252                             final PrivateKey privateKey,
253                             final String keyID,
254                             final List<Base64> x5c,
255                             final Base64URL x5t256,
256                             final Provider jcaProvider)
257                throws JOSEException {
258                
259                this(JWTAssertionFactory.create(jwtAuthClaimsSet, jwsAlgorithm, privateKey, keyID, x5c, x5t256, jcaProvider));
260        }
261
262
263        /**
264         * Creates a new RSA private key JWT authentication. The expiration
265         * time (exp) is set to five minutes from the current system time.
266         * Generates a default identifier (jti) for the JWT. The issued-at
267         * (iat) and not-before (nbf) claims are not set.
268         *
269         * @param clientID      The client identifier. Must not be
270         *                      {@code null}.
271         * @param endpoint      The endpoint URI where the client will submit
272         *                      the JWT authentication, for example the token
273         *                      endpoint. Must not be {@code null}.
274         * @param jwsAlgorithm  The expected RSA signature algorithm (RS256,
275         *                      RS384 or RS512) for the private key JWT
276         *                      assertion. Must be supported and not
277         *                      {@code null}.
278         * @param rsaPrivateKey The RSA private key. Must not be {@code null}.
279         * @param keyID         Optional identifier for the RSA key, to aid
280         *                      key selection at the authorisation server.
281         *                      Recommended. {@code null} if not specified.
282         * @param jcaProvider   Optional specific JCA provider, {@code null} to
283         *                      use the default one.
284         *
285         * @throws JOSEException If RSA signing failed.
286         */
287        @Deprecated
288        public PrivateKeyJWT(final ClientID clientID,
289                             final URI endpoint,
290                             final JWSAlgorithm jwsAlgorithm,
291                             final RSAPrivateKey rsaPrivateKey,
292                             final String keyID,
293                             final Provider jcaProvider)
294                throws JOSEException {
295
296                this(new JWTAuthenticationClaimsSet(clientID, new Audience(endpoint.toString())),
297                        jwsAlgorithm,
298                        rsaPrivateKey,
299                        keyID,
300                        jcaProvider);
301        }
302
303
304        /**
305         * Creates a new RSA private key JWT authentication.
306         *
307         * @param jwtAuthClaimsSet The JWT authentication claims set. Must not
308         *                         be {@code null}.
309         * @param jwsAlgorithm     The expected RSA signature algorithm (RS256,
310         *                         RS384 or RS512) for the private key JWT
311         *                         assertion. Must be supported and not
312         *                         {@code null}.
313         * @param rsaPrivateKey    The RSA private key. Must not be
314         *                         {@code null}.
315         * @param keyID            Optional identifier for the RSA key, to aid
316         *                         key selection at the authorisation server.
317         *                         Recommended. {@code null} if not specified.
318         * @param jcaProvider      Optional specific JCA provider, {@code null}
319         *                         to use the default one.
320         *
321         * @throws JOSEException If RSA signing failed.
322         */
323        @Deprecated
324        public PrivateKeyJWT(final JWTAuthenticationClaimsSet jwtAuthClaimsSet,
325                             final JWSAlgorithm jwsAlgorithm,
326                             final RSAPrivateKey rsaPrivateKey,
327                             final String keyID,
328                             final Provider jcaProvider)
329                throws JOSEException {
330
331                this(JWTAssertionFactory.create(jwtAuthClaimsSet, jwsAlgorithm, rsaPrivateKey, keyID, null, null, jcaProvider));
332        }
333
334
335        /**
336         * Creates a new EC private key JWT authentication. The expiration
337         * time (exp) is set to five minutes from the current system time.
338         * Generates a default identifier (jti) for the JWT. The issued-at
339         * (iat) and not-before (nbf) claims are not set.
340         *
341         * @param clientID      The client identifier. Must not be
342         *                      {@code null}.
343         * @param endpoint      The endpoint URI where the client will submit
344         *                      the JWT authentication, for example the token
345         *                      endpoint. Must not be {@code null}.
346         * @param jwsAlgorithm  The expected EC signature algorithm (ES256,
347         *                      ES384 or ES512) for the private key JWT
348         *                      assertion. Must be supported and not
349         *                      {@code null}.
350         * @param ecPrivateKey  The EC private key. Must not be {@code null}.
351         * @param keyID         Optional identifier for the EC key, to aid key
352         *                      selection at the authorisation server.
353         *                      Recommended. {@code null} if not specified.
354         * @param jcaProvider   Optional specific JCA provider, {@code null} to
355         *                      use the default one.
356         *
357         * @throws JOSEException If RSA signing failed.
358         */
359        @Deprecated
360        public PrivateKeyJWT(final ClientID clientID,
361                             final URI endpoint,
362                             final JWSAlgorithm jwsAlgorithm,
363                             final ECPrivateKey ecPrivateKey,
364                             final String keyID,
365                             final Provider jcaProvider)
366                throws JOSEException {
367
368                this(new JWTAuthenticationClaimsSet(clientID, new Audience(endpoint.toString())),
369                        jwsAlgorithm,
370                        ecPrivateKey,
371                        keyID,
372                        jcaProvider);
373        }
374        
375        
376        /**
377         * Creates a new EC private key JWT authentication.
378         *
379         * @param jwtAuthClaimsSet The JWT authentication claims set. Must not
380         *                         be {@code null}.
381         * @param jwsAlgorithm     The expected ES signature algorithm (ES256,
382         *                         ES384 or ES512) for the private key JWT
383         *                         assertion. Must be supported and not
384         *                         {@code null}.
385         * @param ecPrivateKey     The EC private key. Must not be
386         *                         {@code null}.
387         * @param keyID            Optional identifier for the EC key, to aid
388         *                         key selection at the authorisation server.
389         *                         Recommended. {@code null} if not specified.
390         * @param jcaProvider      Optional specific JCA provider, {@code null}
391         *                         to use the default one.
392         *
393         * @throws JOSEException If RSA signing failed.
394         */
395        @Deprecated
396        public PrivateKeyJWT(final JWTAuthenticationClaimsSet jwtAuthClaimsSet,
397                             final JWSAlgorithm jwsAlgorithm,
398                             final ECPrivateKey ecPrivateKey,
399                             final String keyID,
400                             final Provider jcaProvider)
401                throws JOSEException {
402
403                this(JWTAssertionFactory.create(jwtAuthClaimsSet, jwsAlgorithm, ecPrivateKey, keyID, null, null, jcaProvider));
404        }
405
406
407        /**
408         * Creates a new private key JWT authentication.
409         *
410         * @param clientAssertion The client assertion, corresponding to the
411         *                        {@code client_assertion} parameter, as a
412         *                        supported RSA or ECDSA-signed JWT. Must be
413         *                        signed and not {@code null}.
414         */
415        public PrivateKeyJWT(final SignedJWT clientAssertion) {
416        
417                super(ClientAuthenticationMethod.PRIVATE_KEY_JWT, clientAssertion);
418
419                JWSAlgorithm alg = clientAssertion.getHeader().getAlgorithm();
420
421                if (! JWSAlgorithm.Family.RSA.contains(alg) && ! JWSAlgorithm.Family.EC.contains(alg))
422                        throw new IllegalArgumentException("The client assertion JWT must be RSA or ECDSA-signed (RS256, RS384, RS512, PS256, PS384, PS512, ES256, ES384 or ES512)");
423        }
424        
425        
426        /**
427         * Parses the specified parameters map for a private key JSON Web Token
428         * (JWT) authentication. Note that the parameters must not be
429         * {@code application/x-www-form-urlencoded} encoded.
430         *
431         * @param params The parameters map to parse. The private key JSON
432         *               Web Token (JWT) parameters must be keyed under 
433         *               "client_assertion" and "client_assertion_type". The 
434         *               map must not be {@code null}.
435         *
436         * @return The private key JSON Web Token (JWT) authentication.
437         *
438         * @throws ParseException If the parameters map couldn't be parsed to a 
439         *                        private key JSON Web Token (JWT) 
440         *                        authentication.
441         */
442        public static PrivateKeyJWT parse(final Map<String,List<String>> params)
443                throws ParseException {
444        
445                JWTAuthentication.ensureClientAssertionType(params);
446                
447                SignedJWT clientAssertion = JWTAuthentication.parseClientAssertion(params);
448
449                PrivateKeyJWT privateKeyJWT;
450
451                try {
452                        privateKeyJWT = new PrivateKeyJWT(clientAssertion);
453
454                }catch (IllegalArgumentException e) {
455
456                        throw new ParseException(e.getMessage(), e);
457                }
458
459                // Check that the top level client_id matches the assertion subject + issuer
460
461                ClientID clientID = JWTAuthentication.parseClientID(params);
462
463                if (clientID != null) {
464
465                        if (! clientID.equals(privateKeyJWT.getClientID()))
466                                throw new ParseException("Invalid private key JWT authentication: The client identifier doesn't match the client assertion subject / issuer");
467                }
468
469                return privateKeyJWT;
470        }
471        
472        
473        /**
474         * Parses a private key JSON Web Token (JWT) authentication from the 
475         * specified {@code application/x-www-form-urlencoded} encoded 
476         * parameters string.
477         *
478         * @param paramsString The parameters string to parse. The private key
479         *                     JSON Web Token (JWT) parameters must be keyed 
480         *                     under "client_assertion" and 
481         *                     "client_assertion_type". The string must not be 
482         *                     {@code null}.
483         *
484         * @return The private key JSON Web Token (JWT) authentication.
485         *
486         * @throws ParseException If the parameters string couldn't be parsed 
487         *                        to a private key JSON Web Token (JWT) 
488         *                        authentication.
489         */
490        public static PrivateKeyJWT parse(final String paramsString)
491                throws ParseException {
492                
493                Map<String,List<String>> params = URLUtils.parseParameters(paramsString);
494                
495                return parse(params);
496        }
497        
498        
499        /**
500         * Parses the specified HTTP POST request for a private key JSON Web 
501         * Token (JWT) authentication.
502         *
503         * @param httpRequest The HTTP POST request to parse. Must not be 
504         *                    {@code null} and must contain a valid 
505         *                    {@code application/x-www-form-urlencoded} encoded 
506         *                    parameters string in the entity body. The private 
507         *                    key JSON Web Token (JWT) parameters must be 
508         *                    keyed under "client_assertion" and 
509         *                    "client_assertion_type".
510         *
511         * @return The private key JSON Web Token (JWT) authentication.
512         *
513         * @throws ParseException If the HTTP request header couldn't be parsed
514         *                        to a private key JSON Web Token (JWT) 
515         *                        authentication.
516         */
517        public static PrivateKeyJWT parse(final HTTPRequest httpRequest)
518                throws ParseException {
519                
520                httpRequest.ensureMethod(HTTPRequest.Method.POST);
521                httpRequest.ensureEntityContentType(ContentType.APPLICATION_URLENCODED);
522                
523                return parse(httpRequest.getQueryParameters());
524        }
525}