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 com.nimbusds.common.contenttype.ContentType;
022import com.nimbusds.jose.JOSEException;
023import com.nimbusds.jose.JWSAlgorithm;
024import com.nimbusds.jose.util.Base64;
025import com.nimbusds.jose.util.Base64URL;
026import com.nimbusds.jwt.SignedJWT;
027import com.nimbusds.oauth2.sdk.ParseException;
028import com.nimbusds.oauth2.sdk.assertions.jwt.JWTAssertionFactory;
029import com.nimbusds.oauth2.sdk.http.HTTPRequest;
030import com.nimbusds.oauth2.sdk.id.Audience;
031import com.nimbusds.oauth2.sdk.id.ClientID;
032import com.nimbusds.oauth2.sdk.id.Issuer;
033import com.nimbusds.oauth2.sdk.util.URLUtils;
034import net.jcip.annotations.Immutable;
035
036import java.net.URI;
037import java.security.PrivateKey;
038import java.security.Provider;
039import java.security.interfaces.ECPrivateKey;
040import java.security.interfaces.RSAPrivateKey;
041import java.util.*;
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 1 minute 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 audience     The identity of the audience, for example the
113         *                     issuer URI of the authorisation server. Must not
114         *                     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 audience,
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(audience)),
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 1 minute 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 iss          The issuer. May be different from the client
154         *                     identifier. Must not be {@code null}.
155         * @param clientID     The client identifier. Must not be {@code null}.
156         * @param audience     The identity of the audience, for example the
157         *                     issuer URI of the authorisation server. Must not
158         *                     be {@code null}.
159         * @param jwsAlgorithm The expected RSA (RS256, RS384, RS512, PS256,
160         *                     PS384 or PS512) or EC (ES256, ES384, ES512)
161         *                     signature algorithm for the JWT assertion. Must
162         *                     be supported and not {@code null}.
163         * @param privateKey   The signing private RSA or EC key. Must not be
164         *                     {@code null}.
165         * @param keyID        Optional identifier for the key, to aid key
166         *                     selection on the recipient side. Recommended.
167         *                     {@code null} if not specified.
168         * @param jcaProvider  Optional specific JCA provider, {@code null} to
169         *                     use the default one.
170         *
171         * @throws JOSEException If RSA signing failed.
172         */
173        public PrivateKeyJWT(final Issuer iss,
174                             final ClientID clientID,
175                             final URI audience,
176                             final JWSAlgorithm jwsAlgorithm,
177                             final PrivateKey privateKey,
178                             final String keyID,
179                             final Provider jcaProvider)
180                throws JOSEException {
181
182                this(new JWTAuthenticationClaimsSet(iss, clientID, new Audience(audience)),
183                        jwsAlgorithm,
184                        privateKey,
185                        keyID,
186                        null,
187                        null,
188                        jcaProvider);
189        }
190
191
192        /**
193         * Creates a new private key JWT authentication. The expiration
194         * time (exp) is set to 1 minute from the current system time.
195         * Generates a default identifier (jti) for the JWT. The issued-at
196         * (iat) and not-before (nbf) claims are not set.
197         *
198         * @param clientID     The client identifier. Must not be {@code null}.
199         * @param audience     The identity of the audience, for example the
200         *                     issuer URI of the authorisation server. Must not
201         *                     be {@code null}.
202         * @param jwsAlgorithm The expected RSA (RS256, RS384, RS512, PS256,
203         *                     PS384 or PS512) or EC (ES256, ES384, ES512)
204         *                     signature algorithm for the JWT assertion. Must
205         *                     be supported and not {@code null}.
206         * @param privateKey   The signing private RSA or EC key. Must not be
207         *                     {@code null}.
208         * @param keyID        Optional identifier for the key, to aid key
209         *                     selection on the recipient side. Recommended.
210         *                     {@code null} if not specified.
211         * @param x5c          Optional X.509 certificate chain for the public
212         *                     key, {@code null} if not specified.
213         * @param x5t256       Optional X.509 certificate SHA-256 thumbprint,
214         *                     {@code null} if not specified.
215         * @param jcaProvider  Optional specific JCA provider, {@code null} to
216         *                     use the default one.
217         *
218         * @throws JOSEException If RSA signing failed.
219         */
220        public PrivateKeyJWT(final ClientID clientID,
221                             final URI audience,
222                             final JWSAlgorithm jwsAlgorithm,
223                             final PrivateKey privateKey,
224                             final String keyID,
225                             final List<Base64> x5c,
226                             final Base64URL x5t256,
227                             final Provider jcaProvider)
228                throws JOSEException {
229
230                this(new JWTAuthenticationClaimsSet(clientID, new Audience(audience)),
231                        jwsAlgorithm,
232                        privateKey,
233                        keyID,
234                        x5c,
235                        x5t256,
236                        jcaProvider);
237        }
238
239
240        /**
241         * Creates a new private key JWT authentication. The expiration
242         * time (exp) is set to 1 minute from the current system time.
243         * Generates a default identifier (jti) for the JWT. The issued-at
244         * (iat) and not-before (nbf) claims are not set.
245         *
246         * @param iss          The issuer. May be different from the client
247         *                     identifier. Must not be {@code null}.
248         * @param clientID     The client identifier. Must not be {@code null}.
249         * @param audience     The identity of the audience, for example the
250         *                     issuer URI of the authorisation server. Must not
251         *                     be {@code null}.
252         * @param jwsAlgorithm The expected RSA (RS256, RS384, RS512, PS256,
253         *                     PS384 or PS512) or EC (ES256, ES384, ES512)
254         *                     signature algorithm for the JWT assertion. Must
255         *                     be supported and not {@code null}.
256         * @param privateKey   The signing private RSA or EC key. Must not be
257         *                     {@code null}.
258         * @param keyID        Optional identifier for the key, to aid key
259         *                     selection on the recipient side. Recommended.
260         *                     {@code null} if not specified.
261         * @param x5c          Optional X.509 certificate chain for the public
262         *                     key, {@code null} if not specified.
263         * @param x5t256       Optional X.509 certificate SHA-256 thumbprint,
264         *                     {@code null} if not specified.
265         * @param jcaProvider  Optional specific JCA provider, {@code null} to
266         *                     use the default one.
267         *
268         * @throws JOSEException If RSA signing failed.
269         */
270        public PrivateKeyJWT(final Issuer iss,
271                             final ClientID clientID,
272                             final URI audience,
273                             final JWSAlgorithm jwsAlgorithm,
274                             final PrivateKey privateKey,
275                             final String keyID,
276                             final List<Base64> x5c,
277                             final Base64URL x5t256,
278                             final Provider jcaProvider)
279                throws JOSEException {
280
281                this(new JWTAuthenticationClaimsSet(iss, clientID, new Audience(audience)),
282                        jwsAlgorithm,
283                        privateKey,
284                        keyID,
285                        x5c,
286                        x5t256,
287                        jcaProvider);
288        }
289        
290        
291        /**
292         * Creates a new private key JWT authentication.
293         *
294         * @param jwtAuthClaimsSet The JWT authentication claims set. Must not
295         *                         be {@code null}.
296         * @param jwsAlgorithm     The expected RSA (RS256, RS384, RS512,
297         *                         PS256, PS384 or PS512) or EC (ES256, ES384,
298         *                         ES512) signature algorithm for the JWT
299         *                         assertion. Must be supported and not
300         *                         {@code null}.
301         * @param privateKey       The signing private RSA or EC key. Must not
302         *                         be {@code null}.
303         * @param keyID            Optional identifier for the key, to aid key
304         *                         selection on the recipient side.
305         *                         Recommended. {@code null} if not specified.
306         * @param jcaProvider      Optional specific JCA provider, {@code null}
307         *                         to use the default one.
308         *
309         * @throws JOSEException If RSA signing failed.
310         */
311        public PrivateKeyJWT(final JWTAuthenticationClaimsSet jwtAuthClaimsSet,
312                             final JWSAlgorithm jwsAlgorithm,
313                             final PrivateKey privateKey,
314                             final String keyID,
315                             final Provider jcaProvider)
316                throws JOSEException {
317                
318                this(jwtAuthClaimsSet, jwsAlgorithm, privateKey, keyID, null, null, jcaProvider);
319        }
320        
321        
322        /**
323         * Creates a new private key JWT authentication.
324         *
325         * @param jwtAuthClaimsSet The JWT authentication claims set. Must not
326         *                         be {@code null}.
327         * @param jwsAlgorithm     The expected RSA (RS256, RS384, RS512,
328         *                         PS256, PS384 or PS512) or EC (ES256, ES384,
329         *                         ES512) signature algorithm for the JWT
330         *                         assertion. Must be supported and not
331         *                         {@code null}.
332         * @param privateKey       The signing private RSA or EC key. Must not
333         *                         be {@code null}.
334         * @param keyID            Optional identifier for the key, to aid key
335         *                         selection on the recipient side.
336         *                         Recommended. {@code null} if not specified.
337         * @param x5c              Optional X.509 certificate chain for the
338         *                         public key, {@code null} if not specified.
339         * @param x5t256           Optional X.509 certificate SHA-256
340         *                         thumbprint, {@code null} if not specified.
341         * @param jcaProvider      Optional specific JCA provider, {@code null}
342         *                         to use the default one.
343         *
344         * @throws JOSEException If RSA signing failed.
345         */
346        public PrivateKeyJWT(final JWTAuthenticationClaimsSet jwtAuthClaimsSet,
347                             final JWSAlgorithm jwsAlgorithm,
348                             final PrivateKey privateKey,
349                             final String keyID,
350                             final List<Base64> x5c,
351                             final Base64URL x5t256,
352                             final Provider jcaProvider)
353                throws JOSEException {
354                
355                this(JWTAssertionFactory.create(jwtAuthClaimsSet, jwsAlgorithm, privateKey, keyID, x5c, x5t256, jcaProvider));
356        }
357
358
359        /**
360         * Creates a new RSA private key JWT authentication. The expiration
361         * time (exp) is set to 1 minute from the current system time.
362         * Generates a default identifier (jti) for the JWT. The issued-at
363         * (iat) and not-before (nbf) claims are not set.
364         *
365         * @param clientID      The client identifier. Must not be
366         *                      {@code null}.
367         * @param audience      The identity of the audience, for example the
368         *                      issuer URI of the authorisation server. Must
369         *                      not be {@code null}.
370         * @param jwsAlgorithm  The expected RSA signature algorithm (RS256,
371         *                      RS384 or RS512) for the private key JWT
372         *                      assertion. Must be supported and not
373         *                      {@code null}.
374         * @param rsaPrivateKey The RSA private key. Must not be {@code null}.
375         * @param keyID         Optional identifier for the RSA key, to aid
376         *                      key selection at the authorisation server.
377         *                      Recommended. {@code null} if not specified.
378         * @param jcaProvider   Optional specific JCA provider, {@code null} to
379         *                      use the default one.
380         *
381         * @throws JOSEException If RSA signing failed.
382         */
383        @Deprecated
384        public PrivateKeyJWT(final ClientID clientID,
385                             final URI audience,
386                             final JWSAlgorithm jwsAlgorithm,
387                             final RSAPrivateKey rsaPrivateKey,
388                             final String keyID,
389                             final Provider jcaProvider)
390                throws JOSEException {
391
392                this(new JWTAuthenticationClaimsSet(clientID, new Audience(audience)),
393                        jwsAlgorithm,
394                        rsaPrivateKey,
395                        keyID,
396                        jcaProvider);
397        }
398
399
400        /**
401         * Creates a new RSA private key JWT authentication.
402         *
403         * @param jwtAuthClaimsSet The JWT authentication claims set. Must not
404         *                         be {@code null}.
405         * @param jwsAlgorithm     The expected RSA signature algorithm (RS256,
406         *                         RS384 or RS512) for the private key JWT
407         *                         assertion. Must be supported and not
408         *                         {@code null}.
409         * @param rsaPrivateKey    The RSA private key. Must not be
410         *                         {@code null}.
411         * @param keyID            Optional identifier for the RSA key, to aid
412         *                         key selection at the authorisation server.
413         *                         Recommended. {@code null} if not specified.
414         * @param jcaProvider      Optional specific JCA provider, {@code null}
415         *                         to use the default one.
416         *
417         * @throws JOSEException If RSA signing failed.
418         */
419        @Deprecated
420        public PrivateKeyJWT(final JWTAuthenticationClaimsSet jwtAuthClaimsSet,
421                             final JWSAlgorithm jwsAlgorithm,
422                             final RSAPrivateKey rsaPrivateKey,
423                             final String keyID,
424                             final Provider jcaProvider)
425                throws JOSEException {
426
427                this(JWTAssertionFactory.create(jwtAuthClaimsSet, jwsAlgorithm, rsaPrivateKey, keyID, null, null, jcaProvider));
428        }
429
430
431        /**
432         * Creates a new EC private key JWT authentication. The expiration
433         * time (exp) is set to 1 minute from the current system time.
434         * Generates a default identifier (jti) for the JWT. The issued-at
435         * (iat) and not-before (nbf) claims are not set.
436         *
437         * @param clientID      The client identifier. Must not be
438         *                      {@code null}.
439         * @param audience      The identity of the audience, for example the
440         *                      issuer URI of the authorisation server. Must
441         *                      not be {@code null}.
442         * @param jwsAlgorithm  The expected EC signature algorithm (ES256,
443         *                      ES384 or ES512) for the private key JWT
444         *                      assertion. Must be supported and not
445         *                      {@code null}.
446         * @param ecPrivateKey  The EC private key. Must not be {@code null}.
447         * @param keyID         Optional identifier for the EC key, to aid key
448         *                      selection at the authorisation server.
449         *                      Recommended. {@code null} if not specified.
450         * @param jcaProvider   Optional specific JCA provider, {@code null} to
451         *                      use the default one.
452         *
453         * @throws JOSEException If RSA signing failed.
454         */
455        @Deprecated
456        public PrivateKeyJWT(final ClientID clientID,
457                             final URI audience,
458                             final JWSAlgorithm jwsAlgorithm,
459                             final ECPrivateKey ecPrivateKey,
460                             final String keyID,
461                             final Provider jcaProvider)
462                throws JOSEException {
463
464                this(new JWTAuthenticationClaimsSet(clientID, new Audience(audience)),
465                        jwsAlgorithm,
466                        ecPrivateKey,
467                        keyID,
468                        jcaProvider);
469        }
470        
471        
472        /**
473         * Creates a new EC private key JWT authentication.
474         *
475         * @param jwtAuthClaimsSet The JWT authentication claims set. Must not
476         *                         be {@code null}.
477         * @param jwsAlgorithm     The expected ES signature algorithm (ES256,
478         *                         ES384 or ES512) for the private key JWT
479         *                         assertion. Must be supported and not
480         *                         {@code null}.
481         * @param ecPrivateKey     The EC private key. Must not be
482         *                         {@code null}.
483         * @param keyID            Optional identifier for the EC key, to aid
484         *                         key selection at the authorisation server.
485         *                         Recommended. {@code null} if not specified.
486         * @param jcaProvider      Optional specific JCA provider, {@code null}
487         *                         to use the default one.
488         *
489         * @throws JOSEException If RSA signing failed.
490         */
491        @Deprecated
492        public PrivateKeyJWT(final JWTAuthenticationClaimsSet jwtAuthClaimsSet,
493                             final JWSAlgorithm jwsAlgorithm,
494                             final ECPrivateKey ecPrivateKey,
495                             final String keyID,
496                             final Provider jcaProvider)
497                throws JOSEException {
498
499                this(JWTAssertionFactory.create(jwtAuthClaimsSet, jwsAlgorithm, ecPrivateKey, keyID, null, null, jcaProvider));
500        }
501
502
503        /**
504         * Creates a new private key JWT authentication.
505         *
506         * @param clientAssertion The client assertion, corresponding to the
507         *                        {@code client_assertion} parameter, as a
508         *                        supported RSA or ECDSA-signed JWT. Must be
509         *                        signed and not {@code null}.
510         */
511        public PrivateKeyJWT(final SignedJWT clientAssertion) {
512        
513                super(ClientAuthenticationMethod.PRIVATE_KEY_JWT, clientAssertion);
514
515                JWSAlgorithm alg = clientAssertion.getHeader().getAlgorithm();
516
517                if (! JWSAlgorithm.Family.RSA.contains(alg) && ! JWSAlgorithm.Family.EC.contains(alg))
518                        throw new IllegalArgumentException("The client assertion JWT must be RSA or ECDSA-signed (RS256, RS384, RS512, PS256, PS384, PS512, ES256, ES384 or ES512)");
519        }
520        
521        
522        /**
523         * Parses the specified parameters map for a private key JSON Web Token
524         * (JWT) authentication. Note that the parameters must not be
525         * {@code application/x-www-form-urlencoded} encoded.
526         *
527         * @param params The parameters map to parse. The private key JSON
528         *               Web Token (JWT) parameters must be keyed under 
529         *               "client_assertion" and "client_assertion_type". The 
530         *               map must not be {@code null}.
531         *
532         * @return The private key JSON Web Token (JWT) authentication.
533         *
534         * @throws ParseException If the parameters map couldn't be parsed to a 
535         *                        private key JSON Web Token (JWT) 
536         *                        authentication.
537         */
538        public static PrivateKeyJWT parse(final Map<String,List<String>> params)
539                throws ParseException {
540        
541                JWTAuthentication.ensureClientAssertionType(params);
542                
543                SignedJWT clientAssertion = JWTAuthentication.parseClientAssertion(params);
544
545                PrivateKeyJWT privateKeyJWT;
546
547                try {
548                        privateKeyJWT = new PrivateKeyJWT(clientAssertion);
549
550                }catch (IllegalArgumentException e) {
551
552                        throw new ParseException(e.getMessage(), e);
553                }
554
555                // Check that the top level client_id matches the assertion subject + issuer
556
557                ClientID clientID = JWTAuthentication.parseClientID(params);
558
559                if (clientID != null) {
560
561                        if (! clientID.equals(privateKeyJWT.getClientID()))
562                                throw new ParseException("Invalid private key JWT authentication: The client identifier doesn't match the client assertion subject");
563                }
564
565                return privateKeyJWT;
566        }
567        
568        
569        /**
570         * Parses a private key JSON Web Token (JWT) authentication from the 
571         * specified {@code application/x-www-form-urlencoded} encoded 
572         * parameters string.
573         *
574         * @param paramsString The parameters string to parse. The private key
575         *                     JSON Web Token (JWT) parameters must be keyed 
576         *                     under "client_assertion" and 
577         *                     "client_assertion_type". The string must not be 
578         *                     {@code null}.
579         *
580         * @return The private key JSON Web Token (JWT) authentication.
581         *
582         * @throws ParseException If the parameters string couldn't be parsed 
583         *                        to a private key JSON Web Token (JWT) 
584         *                        authentication.
585         */
586        public static PrivateKeyJWT parse(final String paramsString)
587                throws ParseException {
588                
589                Map<String,List<String>> params = URLUtils.parseParameters(paramsString);
590                
591                return parse(params);
592        }
593        
594        
595        /**
596         * Parses the specified HTTP POST request for a private key JSON Web 
597         * Token (JWT) authentication.
598         *
599         * @param httpRequest The HTTP POST request to parse. Must not be 
600         *                    {@code null} and must contain a valid 
601         *                    {@code application/x-www-form-urlencoded} encoded 
602         *                    parameters string in the entity body. The private 
603         *                    key JSON Web Token (JWT) parameters must be 
604         *                    keyed under "client_assertion" and 
605         *                    "client_assertion_type".
606         *
607         * @return The private key JSON Web Token (JWT) authentication.
608         *
609         * @throws ParseException If the HTTP request header couldn't be parsed
610         *                        to a private key JSON Web Token (JWT) 
611         *                        authentication.
612         */
613        public static PrivateKeyJWT parse(final HTTPRequest httpRequest)
614                throws ParseException {
615                
616                httpRequest.ensureMethod(HTTPRequest.Method.POST);
617                httpRequest.ensureEntityContentType(ContentType.APPLICATION_URLENCODED);
618                
619                return parse(httpRequest.getBodyAsFormParameters());
620        }
621}