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.verifier; 019 020 021import com.nimbusds.jose.JOSEException; 022import com.nimbusds.jose.JWSVerifier; 023import com.nimbusds.jose.crypto.MACVerifier; 024import com.nimbusds.jose.crypto.factories.DefaultJWSVerifierFactory; 025import com.nimbusds.jose.proc.JWSVerifierFactory; 026import com.nimbusds.jwt.SignedJWT; 027import com.nimbusds.jwt.proc.BadJWTException; 028import com.nimbusds.oauth2.sdk.auth.*; 029import com.nimbusds.oauth2.sdk.id.Audience; 030import com.nimbusds.oauth2.sdk.id.ClientID; 031import com.nimbusds.oauth2.sdk.id.JWTID; 032import com.nimbusds.oauth2.sdk.util.CollectionUtils; 033import com.nimbusds.oauth2.sdk.util.ListUtils; 034import com.nimbusds.oauth2.sdk.util.X509CertificateUtils; 035import net.jcip.annotations.ThreadSafe; 036 037import java.security.PublicKey; 038import java.security.cert.X509Certificate; 039import java.util.*; 040 041 042/** 043 * Client authentication verifier. 044 * 045 * <p>Related specifications: 046 * 047 * <ul> 048 * <li>OAuth 2.0 (RFC 6749) 049 * <li>OpenID Connect Core 1.0 050 * <li>JSON Web Token (JWT) Profile for OAuth 2.0 Client Authentication and 051 * Authorization Grants (RFC 7523) 052 * <li>OAuth 2.0 Mutual TLS Client Authentication and Certificate Bound 053 * Access Tokens (RFC 8705) 054 * </ul> 055 */ 056@ThreadSafe 057public class ClientAuthenticationVerifier<T> { 058 059 060 /** 061 * The client credentials selector. 062 */ 063 private final ClientCredentialsSelector<T> clientCredentialsSelector; 064 065 066 /** 067 * Optional client X.509 certificate binding verifier for 068 * {@code tls_client_auth}. 069 * @deprecated Replaced by pkiCertBindingVerifier 070 */ 071 @Deprecated 072 private final ClientX509CertificateBindingVerifier<T> certBindingVerifier; 073 074 075 /** 076 * Optional client X.509 certificate binding verifier for 077 * {@code tls_client_auth}. 078 */ 079 private final PKIClientX509CertificateBindingVerifier<T> pkiCertBindingVerifier; 080 081 082 /** 083 * The JWT assertion claims set verifier. 084 */ 085 private final JWTAuthenticationClaimsSetVerifier claimsSetVerifier; 086 087 088 /** 089 * Optional expended JWT ID (jti) checker. 090 */ 091 private final ExpendedJTIChecker<T> expendedJTIChecker; 092 093 094 /** 095 * JWS verifier factory for private_key_jwt authentication. 096 */ 097 private final JWSVerifierFactory jwsVerifierFactory = new DefaultJWSVerifierFactory(); 098 099 100 /** 101 * Creates a new client authentication verifier. 102 * 103 * @param clientCredentialsSelector The client credentials selector. 104 * Must not be {@code null}. 105 * @param certBindingVerifier Optional client X.509 certificate 106 * binding verifier for 107 * {@code tls_client_auth}, 108 * {@code null} if not supported. 109 * @param aud The permitted audience (aud) claim. 110 * Must not be empty or {@code null}. 111 * Should be the identity of the 112 * recipient, such as the issuer URI 113 * for an OpenID provider. 114 * 115 * @deprecated Use the constructor with {@link PKIClientX509CertificateBindingVerifier} 116 */ 117 @Deprecated 118 public ClientAuthenticationVerifier(final ClientCredentialsSelector<T> clientCredentialsSelector, 119 final ClientX509CertificateBindingVerifier<T> certBindingVerifier, 120 final Set<Audience> aud) { 121 122 claimsSetVerifier = new JWTAuthenticationClaimsSetVerifier(aud); 123 this.certBindingVerifier = certBindingVerifier; 124 this.pkiCertBindingVerifier = null; 125 this.clientCredentialsSelector = Objects.requireNonNull(clientCredentialsSelector); 126 this.expendedJTIChecker = null; 127 } 128 129 130 /** 131 * Creates a new client authentication verifier without support for 132 * {@code tls_client_auth}. The audience check is 133 * {@link JWTAudienceCheck#LEGACY legacy}. 134 * 135 * @param clientCredentialsSelector The client credentials selector. 136 * Must not be {@code null}. 137 * @param aud The permitted audience (aud) claim. 138 * Must not be empty or {@code null}. 139 * Should be the identity of the 140 * recipient, such as the issuer URI 141 * for an OpenID provider. 142 */ 143 @Deprecated 144 public ClientAuthenticationVerifier(final ClientCredentialsSelector<T> clientCredentialsSelector, 145 final Set<Audience> aud) { 146 147 this(clientCredentialsSelector, aud, JWTAudienceCheck.LEGACY); 148 } 149 150 151 /** 152 * Creates a new client authentication verifier without support for 153 * {@code tls_client_auth}. 154 * 155 * @param clientCredentialsSelector The client credentials selector. 156 * Must not be {@code null}. 157 * @param aud The permitted audience (aud) claim. 158 * Must not be empty or {@code null}. 159 * Should be the identity of the 160 * recipient, such as the issuer URI 161 * for an OpenID provider. 162 */ 163 public ClientAuthenticationVerifier(final ClientCredentialsSelector<T> clientCredentialsSelector, 164 final Set<Audience> aud, 165 final JWTAudienceCheck audCheck) { 166 167 this(clientCredentialsSelector, aud, audCheck, null); 168 } 169 170 171 /** 172 * Creates a new client authentication verifier without support for 173 * {@code tls_client_auth}. The audience check is 174 * {@link JWTAudienceCheck#LEGACY legacy}. 175 * 176 * @param clientCredentialsSelector The client credentials selector. 177 * Must not be {@code null}. 178 * @param aud The permitted audience (aud) claim. 179 * Must not be empty or {@code null}. 180 * Should be the identity of the 181 * recipient, such as the issuer URI 182 * for an OpenID provider. 183 * @param expendedJTIChecker Optional expended JWT ID (jti) 184 * claim checker to prevent JWT 185 * replay, {@code null} if none. 186 */ 187 @Deprecated 188 public ClientAuthenticationVerifier(final ClientCredentialsSelector<T> clientCredentialsSelector, 189 final Set<Audience> aud, 190 final ExpendedJTIChecker<T> expendedJTIChecker) { 191 192 this(clientCredentialsSelector, aud, JWTAudienceCheck.LEGACY, expendedJTIChecker); 193 } 194 195 196 /** 197 * Creates a new client authentication verifier without support for 198 * {@code tls_client_auth}. 199 * 200 * @param clientCredentialsSelector The client credentials selector. 201 * Must not be {@code null}. 202 * @param aud The permitted audience (aud) claim. 203 * Must not be empty or {@code null}. 204 * Should be the identity of the 205 * recipient, such as the issuer URI 206 * for an OpenID provider. 207 * @param expendedJTIChecker Optional expended JWT ID (jti) 208 * claim checker to prevent JWT 209 * replay, {@code null} if none. 210 */ 211 public ClientAuthenticationVerifier(final ClientCredentialsSelector<T> clientCredentialsSelector, 212 final Set<Audience> aud, 213 final JWTAudienceCheck audCheck, 214 final ExpendedJTIChecker<T> expendedJTIChecker) { 215 216 claimsSetVerifier = new JWTAuthenticationClaimsSetVerifier(aud, audCheck, -1L); 217 this.certBindingVerifier = null; 218 this.pkiCertBindingVerifier = null; 219 this.clientCredentialsSelector = Objects.requireNonNull(clientCredentialsSelector); 220 this.expendedJTIChecker = expendedJTIChecker; 221 } 222 223 224 /** 225 * Creates a new client authentication verifier. The audience check is 226 * {@link JWTAudienceCheck#LEGACY legacy}. 227 * 228 * @param clientCredentialsSelector The client credentials selector. 229 * Must not be {@code null}. 230 * @param pkiCertBindingVerifier Optional client X.509 certificate 231 * binding verifier for 232 * {@code tls_client_auth}, 233 * {@code null} if not supported. 234 * @param aud The permitted audience (aud) claim. 235 * Must not be empty or {@code null}. 236 * Should be the identity of the 237 * recipient, such as the issuer URI 238 * for an OpenID provider. 239 */ 240 @Deprecated 241 public ClientAuthenticationVerifier(final ClientCredentialsSelector<T> clientCredentialsSelector, 242 final PKIClientX509CertificateBindingVerifier<T> pkiCertBindingVerifier, 243 final Set<Audience> aud) { 244 245 this(clientCredentialsSelector, pkiCertBindingVerifier, aud, JWTAudienceCheck.LEGACY); 246 } 247 248 249 /** 250 * Creates a new client authentication verifier. 251 * 252 * @param clientCredentialsSelector The client credentials selector. 253 * Must not be {@code null}. 254 * @param pkiCertBindingVerifier Optional client X.509 certificate 255 * binding verifier for 256 * {@code tls_client_auth}, 257 * {@code null} if not supported. 258 * @param aud The permitted audience (aud) claim. 259 * Must not be empty or {@code null}. 260 * Should be the identity of the 261 * recipient, such as the issuer URI 262 * for an OpenID provider. 263 */ 264 public ClientAuthenticationVerifier(final ClientCredentialsSelector<T> clientCredentialsSelector, 265 final PKIClientX509CertificateBindingVerifier<T> pkiCertBindingVerifier, 266 final Set<Audience> aud, 267 final JWTAudienceCheck audCheck) { 268 269 this(clientCredentialsSelector, pkiCertBindingVerifier, aud, audCheck, null, -1L); 270 } 271 272 273 /** 274 * Creates a new client authentication verifier. The audience check is 275 * {@link JWTAudienceCheck#LEGACY legacy}. 276 * 277 * @param clientCredentialsSelector The client credentials selector. 278 * Must not be {@code null}. 279 * @param pkiCertBindingVerifier Optional client X.509 certificate 280 * binding verifier for 281 * {@code tls_client_auth}, 282 * {@code null} if not supported. 283 * @param aud The permitted audience (aud) claim. 284 * Must not be empty or {@code null}. 285 * Should be the identity of the 286 * recipient, such as the issuer URI 287 * for an OpenID provider. 288 * @param expendedJTIChecker Optional expended JWT ID (jti) 289 * claim checker to prevent JWT 290 * replay, {@code null} if none. 291 * @param expMaxAhead The maximum number of seconds the 292 * expiration time (exp) claim can be 293 * ahead of the current time, if zero 294 * or negative this check is disabled. 295 */ 296 @Deprecated 297 public ClientAuthenticationVerifier(final ClientCredentialsSelector<T> clientCredentialsSelector, 298 final PKIClientX509CertificateBindingVerifier<T> pkiCertBindingVerifier, 299 final Set<Audience> aud, 300 final ExpendedJTIChecker<T> expendedJTIChecker, 301 final long expMaxAhead) { 302 303 this(clientCredentialsSelector, pkiCertBindingVerifier, aud, JWTAudienceCheck.LEGACY, expendedJTIChecker, expMaxAhead); 304 } 305 306 307 /** 308 * Creates a new client authentication verifier. 309 * 310 * @param clientCredentialsSelector The client credentials selector. 311 * Must not be {@code null}. 312 * @param pkiCertBindingVerifier Optional client X.509 certificate 313 * binding verifier for 314 * {@code tls_client_auth}, 315 * {@code null} if not supported. 316 * @param aud The permitted audience (aud) claim. 317 * Must not be empty or {@code null}. 318 * Should be the identity of the 319 * recipient, such as the issuer URI 320 * for an OpenID provider. When the 321 * audience check is 322 * {@link JWTAudienceCheck#STRICT strict}, 323 * the permitted audience must be 324 * single-valued. 325 * @param audCheck The type of audience (aud) check. 326 * Must not be {@code null}. 327 * @param expendedJTIChecker Optional expended JWT ID (jti) 328 * claim checker to prevent JWT 329 * replay, {@code null} if none. 330 * @param expMaxAhead The maximum number of seconds the 331 * expiration time (exp) claim can be 332 * ahead of the current time, if zero 333 * or negative this check is disabled. 334 */ 335 public ClientAuthenticationVerifier(final ClientCredentialsSelector<T> clientCredentialsSelector, 336 final PKIClientX509CertificateBindingVerifier<T> pkiCertBindingVerifier, 337 final Set<Audience> aud, 338 final JWTAudienceCheck audCheck, 339 final ExpendedJTIChecker<T> expendedJTIChecker, 340 final long expMaxAhead) { 341 342 claimsSetVerifier = new JWTAuthenticationClaimsSetVerifier(aud, audCheck, expMaxAhead); 343 this.certBindingVerifier = null; 344 this.pkiCertBindingVerifier = pkiCertBindingVerifier; 345 this.clientCredentialsSelector = Objects.requireNonNull(clientCredentialsSelector); 346 this.expendedJTIChecker = expendedJTIChecker; 347 } 348 349 350 /** 351 * Returns the client credentials selector. 352 * 353 * @return The client credentials selector. 354 */ 355 public ClientCredentialsSelector<T> getClientCredentialsSelector() { 356 357 return clientCredentialsSelector; 358 } 359 360 361 /** 362 * Returns the client X.509 certificate binding verifier for use in 363 * {@code tls_client_auth}. 364 * 365 * @return The client X.509 certificate binding verifier, {@code null} 366 * if not specified. 367 * @deprecated See {@link PKIClientX509CertificateBindingVerifier} 368 */ 369 @Deprecated 370 public ClientX509CertificateBindingVerifier<T> getClientX509CertificateBindingVerifier() { 371 372 return certBindingVerifier; 373 } 374 375 376 /** 377 * Returns the client X.509 certificate binding verifier for use in 378 * {@code tls_client_auth}. 379 * 380 * @return The client X.509 certificate binding verifier, {@code null} 381 * if not specified. 382 */ 383 public PKIClientX509CertificateBindingVerifier<T> getPKIClientX509CertificateBindingVerifier() { 384 385 return pkiCertBindingVerifier; 386 } 387 388 389 /** 390 * Returns the permitted audience in JWT authentication assertions. 391 * 392 * @return The permitted audience (aud) claim values. 393 */ 394 public Set<Audience> getExpectedAudience() { 395 396 return claimsSetVerifier.getExpectedAudience(); 397 } 398 399 400 /** 401 * Returns the configured audience check. 402 * 403 * @return The type of audience (aud) check. 404 */ 405 public JWTAudienceCheck getJWTAudienceCheck() { 406 407 return claimsSetVerifier.getAudienceCheck(); 408 } 409 410 411 /** 412 * Returns the optional expended JWT ID (jti) claim checker to prevent 413 * JWT replay. 414 * 415 * @return The expended JWT ID (jti) claim checker, {@code null} if 416 * none. 417 */ 418 public ExpendedJTIChecker<T> getExpendedJTIChecker() { 419 420 return expendedJTIChecker; 421 } 422 423 424 private static List<Secret> removeNullOrErased(final List<Secret> secrets) { 425 List<Secret> allSet = ListUtils.removeNullItems(secrets); 426 if (allSet == null) { 427 return null; 428 } 429 List<Secret> out = new LinkedList<>(); 430 for (Secret secret: secrets) { 431 if (secret.getValue() != null && secret.getValueBytes() != null) { 432 out.add(secret); 433 } 434 } 435 return out; 436 } 437 438 439 private void preventJWTReplay(final JWTID jti, 440 final ClientID clientID, 441 final ClientAuthenticationMethod method, 442 final Context<T> context) 443 throws InvalidClientException { 444 445 if (jti == null || getExpendedJTIChecker() == null) { 446 return; 447 } 448 449 if (getExpendedJTIChecker().isExpended(jti, clientID, method, context)) { 450 throw new InvalidClientException("Detected JWT ID replay"); 451 } 452 } 453 454 455 private void markExpended(final JWTID jti, 456 final Date exp, 457 final ClientID clientID, 458 final ClientAuthenticationMethod method, 459 final Context<T> context) { 460 461 if (jti == null || getExpendedJTIChecker() == null) { 462 return; 463 } 464 465 getExpendedJTIChecker().markExpended(jti, exp, clientID, method, context); 466 } 467 468 469 /** 470 * Verifies a client authentication request. 471 * 472 * @param clientAuth The client authentication. Must not be 473 * {@code null}. 474 * @param hints Optional hints to the verifier, empty set of 475 * {@code null} if none. 476 * @param context Additional context to be passed to the client 477 * credentials selector. May be {@code null}. 478 * 479 * @throws InvalidClientException If the client authentication is 480 * invalid, typically due to bad 481 * credentials. 482 * @throws JOSEException If authentication failed due to an 483 * internal JOSE / JWT processing 484 * exception. 485 */ 486 public void verify(final ClientAuthentication clientAuth, final Set<Hint> hints, final Context<T> context) 487 throws InvalidClientException, JOSEException { 488 489 if (clientAuth instanceof PlainClientSecret) { 490 491 List<Secret> secretCandidates = ListUtils.removeNullItems( 492 clientCredentialsSelector.selectClientSecrets( 493 clientAuth.getClientID(), 494 clientAuth.getMethod(), 495 context 496 ) 497 ); 498 499 if (CollectionUtils.isEmpty(secretCandidates)) { 500 throw InvalidClientException.NO_REGISTERED_SECRET; 501 } 502 503 PlainClientSecret plainAuth = (PlainClientSecret)clientAuth; 504 505 for (Secret candidate: secretCandidates) { 506 507 // Constant time, SHA-256 based, unless overridden 508 if (candidate.equals(plainAuth.getClientSecret())) { 509 return; // success 510 } 511 } 512 513 throw InvalidClientException.BAD_SECRET; 514 515 } else if (clientAuth instanceof ClientSecretJWT) { 516 517 ClientSecretJWT jwtAuth = (ClientSecretJWT) clientAuth; 518 519 // Check claims first before requesting secret from backend 520 JWTAuthenticationClaimsSet jwtAuthClaims = jwtAuth.getJWTAuthenticationClaimsSet(); 521 522 preventJWTReplay(jwtAuthClaims.getJWTID(), clientAuth.getClientID(), ClientAuthenticationMethod.CLIENT_SECRET_JWT, context); 523 524 try { 525 claimsSetVerifier.verify(jwtAuthClaims.toJWTClaimsSet(), null); 526 } catch (BadJWTException e) { 527 throw new InvalidClientException("Bad / expired JWT claims: " + e.getMessage()); 528 } 529 530 List<Secret> secretCandidates = removeNullOrErased( 531 clientCredentialsSelector.selectClientSecrets( 532 clientAuth.getClientID(), 533 clientAuth.getMethod(), 534 context 535 ) 536 ); 537 538 if (CollectionUtils.isEmpty(secretCandidates)) { 539 throw InvalidClientException.NO_REGISTERED_SECRET; 540 } 541 542 SignedJWT assertion = jwtAuth.getClientAssertion(); 543 544 for (Secret candidate : secretCandidates) { 545 546 boolean valid = assertion.verify(new MACVerifier(candidate.getValueBytes())); 547 548 if (valid) { 549 markExpended(jwtAuthClaims.getJWTID(), jwtAuthClaims.getExpirationTime(), clientAuth.getClientID(), ClientAuthenticationMethod.CLIENT_SECRET_JWT, context); 550 return; // success 551 } 552 } 553 554 throw InvalidClientException.BAD_JWT_HMAC; 555 556 } else if (clientAuth instanceof PrivateKeyJWT) { 557 558 PrivateKeyJWT jwtAuth = (PrivateKeyJWT) clientAuth; 559 560 // Check claims first before requesting / retrieving public keys 561 JWTAuthenticationClaimsSet jwtAuthClaims = jwtAuth.getJWTAuthenticationClaimsSet(); 562 563 preventJWTReplay(jwtAuthClaims.getJWTID(), clientAuth.getClientID(), ClientAuthenticationMethod.PRIVATE_KEY_JWT, context); 564 565 try { 566 claimsSetVerifier.verify(jwtAuthClaims.toJWTClaimsSet(), null); 567 } catch (BadJWTException e) { 568 throw new InvalidClientException("Bad / expired JWT claims: " + e.getMessage()); 569 } 570 571 List<? extends PublicKey> keyCandidates = ListUtils.removeNullItems( 572 clientCredentialsSelector.selectPublicKeys( 573 jwtAuth.getClientID(), 574 jwtAuth.getMethod(), 575 jwtAuth.getClientAssertion().getHeader(), 576 false, // don't force refresh if we have a remote JWK set; 577 // selector may however do so if it encounters an unknown key ID 578 context 579 ) 580 ); 581 582 if (CollectionUtils.isEmpty(keyCandidates)) { 583 throw InvalidClientException.NO_MATCHING_JWK; 584 } 585 586 SignedJWT assertion = jwtAuth.getClientAssertion(); 587 588 for (PublicKey candidate : keyCandidates) { 589 590 JWSVerifier jwsVerifier = jwsVerifierFactory.createJWSVerifier( 591 jwtAuth.getClientAssertion().getHeader(), 592 candidate); 593 594 boolean valid = assertion.verify(jwsVerifier); 595 596 if (valid) { 597 markExpended(jwtAuthClaims.getJWTID(), jwtAuthClaims.getExpirationTime(), clientAuth.getClientID(), ClientAuthenticationMethod.PRIVATE_KEY_JWT, context); 598 return; // success 599 } 600 } 601 602 // Second pass 603 if (hints != null && hints.contains(Hint.CLIENT_HAS_REMOTE_JWK_SET)) { 604 // Client possibly registered JWK set URL with keys that have no IDs 605 // force JWK set reload from URL and retry 606 keyCandidates = ListUtils.removeNullItems( 607 clientCredentialsSelector.selectPublicKeys( 608 jwtAuth.getClientID(), 609 jwtAuth.getMethod(), 610 jwtAuth.getClientAssertion().getHeader(), 611 true, // force reload of remote JWK set 612 context 613 ) 614 ); 615 616 if (CollectionUtils.isEmpty(keyCandidates)) { 617 throw InvalidClientException.NO_MATCHING_JWK; 618 } 619 620 assertion = jwtAuth.getClientAssertion(); 621 622 for (PublicKey candidate : keyCandidates) { 623 624 JWSVerifier jwsVerifier = jwsVerifierFactory.createJWSVerifier( 625 jwtAuth.getClientAssertion().getHeader(), 626 candidate); 627 628 boolean valid = assertion.verify(jwsVerifier); 629 630 if (valid) { 631 markExpended(jwtAuthClaims.getJWTID(), jwtAuthClaims.getExpirationTime(), clientAuth.getClientID(), ClientAuthenticationMethod.PRIVATE_KEY_JWT, context); 632 return; // success 633 } 634 } 635 } 636 637 throw InvalidClientException.BAD_JWT_SIGNATURE; 638 639 } else if (clientAuth instanceof SelfSignedTLSClientAuthentication) { 640 641 SelfSignedTLSClientAuthentication tlsClientAuth = (SelfSignedTLSClientAuthentication) clientAuth; 642 643 X509Certificate clientCert = tlsClientAuth.getClientX509Certificate(); 644 645 if (clientCert == null) { 646 // Sanity check 647 throw new InvalidClientException("Missing client X.509 certificate"); 648 } 649 650 // Self-signed certs bound to registered public key in client jwks / jwks_uri 651 List<? extends PublicKey> keyCandidates = ListUtils.removeNullItems( 652 clientCredentialsSelector.selectPublicKeys( 653 tlsClientAuth.getClientID(), 654 tlsClientAuth.getMethod(), 655 null, 656 false, // don't force refresh if we have a remote JWK set; 657 // selector may however do so if it encounters an unknown key ID 658 context 659 ) 660 ); 661 662 if (CollectionUtils.isEmpty(keyCandidates)) { 663 throw InvalidClientException.NO_MATCHING_JWK; 664 } 665 666 for (PublicKey candidate : keyCandidates) { 667 668 boolean valid = X509CertificateUtils.publicKeyMatches(clientCert, candidate); 669 670 if (valid) { 671 return; // success 672 } 673 } 674 675 // Second pass 676 if (hints != null && hints.contains(Hint.CLIENT_HAS_REMOTE_JWK_SET)) { 677 // Client possibly registered JWK set URL with keys that have no IDs 678 // force JWK set reload from URL and retry 679 keyCandidates = ListUtils.removeNullItems( 680 clientCredentialsSelector.selectPublicKeys( 681 tlsClientAuth.getClientID(), 682 tlsClientAuth.getMethod(), 683 null, 684 true, // force reload of remote JWK set 685 context 686 ) 687 ); 688 689 if (CollectionUtils.isEmpty(keyCandidates)) { 690 throw InvalidClientException.NO_MATCHING_JWK; 691 } 692 693 for (PublicKey candidate : keyCandidates) { 694 695 if (candidate == null) { 696 continue; // skip 697 } 698 699 boolean valid = X509CertificateUtils.publicKeyMatches(clientCert, candidate); 700 701 if (valid) { 702 return; // success 703 } 704 } 705 } 706 707 throw InvalidClientException.BAD_SELF_SIGNED_CLIENT_CERTIFICATE; 708 709 } else if (clientAuth instanceof PKITLSClientAuthentication) { 710 711 PKITLSClientAuthentication tlsClientAuth = (PKITLSClientAuthentication) clientAuth; 712 if (pkiCertBindingVerifier != null) { 713 pkiCertBindingVerifier.verifyCertificateBinding( 714 clientAuth.getClientID(), 715 tlsClientAuth.getClientX509Certificate(), 716 context); 717 718 } else if (certBindingVerifier != null) { 719 certBindingVerifier.verifyCertificateBinding( 720 clientAuth.getClientID(), 721 tlsClientAuth.getClientX509CertificateSubjectDN(), 722 context); 723 } else { 724 throw new InvalidClientException("Mutual TLS client Authentication (tls_client_auth) not supported"); 725 } 726 } else { 727 throw new RuntimeException("Unexpected client authentication: " + clientAuth.getMethod()); 728 } 729 } 730}