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