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& 072 * code=i1WsRn1uB1& 073 * client_id=s6BhdRkqt3& 074 * client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer& 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}