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. 321 * @param audCheck The type of audience (aud) check. 322 * Must not be {@code null}. 323 * @param expendedJTIChecker Optional expended JWT ID (jti) 324 * claim checker to prevent JWT 325 * replay, {@code null} if none. 326 * @param expMaxAhead The maximum number of seconds the 327 * expiration time (exp) claim can be 328 * ahead of the current time, if zero 329 * or negative this check is disabled. 330 */ 331 public ClientAuthenticationVerifier(final ClientCredentialsSelector<T> clientCredentialsSelector, 332 final PKIClientX509CertificateBindingVerifier<T> pkiCertBindingVerifier, 333 final Set<Audience> aud, 334 final JWTAudienceCheck audCheck, 335 final ExpendedJTIChecker<T> expendedJTIChecker, 336 final long expMaxAhead) { 337 338 claimsSetVerifier = new JWTAuthenticationClaimsSetVerifier(aud, audCheck, expMaxAhead); 339 this.certBindingVerifier = null; 340 this.pkiCertBindingVerifier = pkiCertBindingVerifier; 341 this.clientCredentialsSelector = Objects.requireNonNull(clientCredentialsSelector); 342 this.expendedJTIChecker = expendedJTIChecker; 343 } 344 345 346 /** 347 * Returns the client credentials selector. 348 * 349 * @return The client credentials selector. 350 */ 351 public ClientCredentialsSelector<T> getClientCredentialsSelector() { 352 353 return clientCredentialsSelector; 354 } 355 356 357 /** 358 * Returns the client X.509 certificate binding verifier for use in 359 * {@code tls_client_auth}. 360 * 361 * @return The client X.509 certificate binding verifier, {@code null} 362 * if not specified. 363 * @deprecated See {@link PKIClientX509CertificateBindingVerifier} 364 */ 365 @Deprecated 366 public ClientX509CertificateBindingVerifier<T> getClientX509CertificateBindingVerifier() { 367 368 return certBindingVerifier; 369 } 370 371 372 /** 373 * Returns the client X.509 certificate binding verifier for use in 374 * {@code tls_client_auth}. 375 * 376 * @return The client X.509 certificate binding verifier, {@code null} 377 * if not specified. 378 */ 379 public PKIClientX509CertificateBindingVerifier<T> getPKIClientX509CertificateBindingVerifier() { 380 381 return pkiCertBindingVerifier; 382 } 383 384 385 /** 386 * Returns the permitted audience in JWT authentication assertions. 387 * 388 * @return The permitted audience (aud) claim values. 389 */ 390 public Set<Audience> getExpectedAudience() { 391 392 return claimsSetVerifier.getExpectedAudience(); 393 } 394 395 396 /** 397 * Returns the configured audience check. 398 * 399 * @return The type of audience (aud) check. 400 */ 401 public JWTAudienceCheck getJWTAudienceCheck() { 402 403 return claimsSetVerifier.getAudienceCheck(); 404 } 405 406 407 /** 408 * Returns the optional expended JWT ID (jti) claim checker to prevent 409 * JWT replay. 410 * 411 * @return The expended JWT ID (jti) claim checker, {@code null} if 412 * none. 413 */ 414 public ExpendedJTIChecker<T> getExpendedJTIChecker() { 415 416 return expendedJTIChecker; 417 } 418 419 420 private static List<Secret> removeNullOrErased(final List<Secret> secrets) { 421 List<Secret> allSet = ListUtils.removeNullItems(secrets); 422 if (allSet == null) { 423 return null; 424 } 425 List<Secret> out = new LinkedList<>(); 426 for (Secret secret: secrets) { 427 if (secret.getValue() != null && secret.getValueBytes() != null) { 428 out.add(secret); 429 } 430 } 431 return out; 432 } 433 434 435 private void preventJWTReplay(final JWTID jti, 436 final ClientID clientID, 437 final ClientAuthenticationMethod method, 438 final Context<T> context) 439 throws InvalidClientException { 440 441 if (jti == null || getExpendedJTIChecker() == null) { 442 return; 443 } 444 445 if (getExpendedJTIChecker().isExpended(jti, clientID, method, context)) { 446 throw new InvalidClientException("Detected JWT ID replay"); 447 } 448 } 449 450 451 private void markExpended(final JWTID jti, 452 final Date exp, 453 final ClientID clientID, 454 final ClientAuthenticationMethod method, 455 final Context<T> context) { 456 457 if (jti == null || getExpendedJTIChecker() == null) { 458 return; 459 } 460 461 getExpendedJTIChecker().markExpended(jti, exp, clientID, method, context); 462 } 463 464 465 /** 466 * Verifies a client authentication request. 467 * 468 * @param clientAuth The client authentication. Must not be 469 * {@code null}. 470 * @param hints Optional hints to the verifier, empty set of 471 * {@code null} if none. 472 * @param context Additional context to be passed to the client 473 * credentials selector. May be {@code null}. 474 * 475 * @throws InvalidClientException If the client authentication is 476 * invalid, typically due to bad 477 * credentials. 478 * @throws JOSEException If authentication failed due to an 479 * internal JOSE / JWT processing 480 * exception. 481 */ 482 public void verify(final ClientAuthentication clientAuth, final Set<Hint> hints, final Context<T> context) 483 throws InvalidClientException, JOSEException { 484 485 if (clientAuth instanceof PlainClientSecret) { 486 487 List<Secret> secretCandidates = ListUtils.removeNullItems( 488 clientCredentialsSelector.selectClientSecrets( 489 clientAuth.getClientID(), 490 clientAuth.getMethod(), 491 context 492 ) 493 ); 494 495 if (CollectionUtils.isEmpty(secretCandidates)) { 496 throw InvalidClientException.NO_REGISTERED_SECRET; 497 } 498 499 PlainClientSecret plainAuth = (PlainClientSecret)clientAuth; 500 501 for (Secret candidate: secretCandidates) { 502 503 // Constant time, SHA-256 based, unless overridden 504 if (candidate.equals(plainAuth.getClientSecret())) { 505 return; // success 506 } 507 } 508 509 throw InvalidClientException.BAD_SECRET; 510 511 } else if (clientAuth instanceof ClientSecretJWT) { 512 513 ClientSecretJWT jwtAuth = (ClientSecretJWT) clientAuth; 514 515 // Check claims first before requesting secret from backend 516 JWTAuthenticationClaimsSet jwtAuthClaims = jwtAuth.getJWTAuthenticationClaimsSet(); 517 518 preventJWTReplay(jwtAuthClaims.getJWTID(), clientAuth.getClientID(), ClientAuthenticationMethod.CLIENT_SECRET_JWT, context); 519 520 try { 521 claimsSetVerifier.verify(jwtAuthClaims.toJWTClaimsSet(), null); 522 } catch (BadJWTException e) { 523 throw new InvalidClientException("Bad / expired JWT claims: " + e.getMessage()); 524 } 525 526 List<Secret> secretCandidates = removeNullOrErased( 527 clientCredentialsSelector.selectClientSecrets( 528 clientAuth.getClientID(), 529 clientAuth.getMethod(), 530 context 531 ) 532 ); 533 534 if (CollectionUtils.isEmpty(secretCandidates)) { 535 throw InvalidClientException.NO_REGISTERED_SECRET; 536 } 537 538 SignedJWT assertion = jwtAuth.getClientAssertion(); 539 540 for (Secret candidate : secretCandidates) { 541 542 boolean valid = assertion.verify(new MACVerifier(candidate.getValueBytes())); 543 544 if (valid) { 545 markExpended(jwtAuthClaims.getJWTID(), jwtAuthClaims.getExpirationTime(), clientAuth.getClientID(), ClientAuthenticationMethod.CLIENT_SECRET_JWT, context); 546 return; // success 547 } 548 } 549 550 throw InvalidClientException.BAD_JWT_HMAC; 551 552 } else if (clientAuth instanceof PrivateKeyJWT) { 553 554 PrivateKeyJWT jwtAuth = (PrivateKeyJWT) clientAuth; 555 556 // Check claims first before requesting / retrieving public keys 557 JWTAuthenticationClaimsSet jwtAuthClaims = jwtAuth.getJWTAuthenticationClaimsSet(); 558 559 preventJWTReplay(jwtAuthClaims.getJWTID(), clientAuth.getClientID(), ClientAuthenticationMethod.PRIVATE_KEY_JWT, context); 560 561 try { 562 claimsSetVerifier.verify(jwtAuthClaims.toJWTClaimsSet(), null); 563 } catch (BadJWTException e) { 564 throw new InvalidClientException("Bad / expired JWT claims: " + e.getMessage()); 565 } 566 567 List<? extends PublicKey> keyCandidates = ListUtils.removeNullItems( 568 clientCredentialsSelector.selectPublicKeys( 569 jwtAuth.getClientID(), 570 jwtAuth.getMethod(), 571 jwtAuth.getClientAssertion().getHeader(), 572 false, // don't force refresh if we have a remote JWK set; 573 // selector may however do so if it encounters an unknown key ID 574 context 575 ) 576 ); 577 578 if (CollectionUtils.isEmpty(keyCandidates)) { 579 throw InvalidClientException.NO_MATCHING_JWK; 580 } 581 582 SignedJWT assertion = jwtAuth.getClientAssertion(); 583 584 for (PublicKey candidate : keyCandidates) { 585 586 JWSVerifier jwsVerifier = jwsVerifierFactory.createJWSVerifier( 587 jwtAuth.getClientAssertion().getHeader(), 588 candidate); 589 590 boolean valid = assertion.verify(jwsVerifier); 591 592 if (valid) { 593 markExpended(jwtAuthClaims.getJWTID(), jwtAuthClaims.getExpirationTime(), clientAuth.getClientID(), ClientAuthenticationMethod.PRIVATE_KEY_JWT, context); 594 return; // success 595 } 596 } 597 598 // Second pass 599 if (hints != null && hints.contains(Hint.CLIENT_HAS_REMOTE_JWK_SET)) { 600 // Client possibly registered JWK set URL with keys that have no IDs 601 // force JWK set reload from URL and retry 602 keyCandidates = ListUtils.removeNullItems( 603 clientCredentialsSelector.selectPublicKeys( 604 jwtAuth.getClientID(), 605 jwtAuth.getMethod(), 606 jwtAuth.getClientAssertion().getHeader(), 607 true, // force reload of remote JWK set 608 context 609 ) 610 ); 611 612 if (CollectionUtils.isEmpty(keyCandidates)) { 613 throw InvalidClientException.NO_MATCHING_JWK; 614 } 615 616 assertion = jwtAuth.getClientAssertion(); 617 618 for (PublicKey candidate : keyCandidates) { 619 620 JWSVerifier jwsVerifier = jwsVerifierFactory.createJWSVerifier( 621 jwtAuth.getClientAssertion().getHeader(), 622 candidate); 623 624 boolean valid = assertion.verify(jwsVerifier); 625 626 if (valid) { 627 markExpended(jwtAuthClaims.getJWTID(), jwtAuthClaims.getExpirationTime(), clientAuth.getClientID(), ClientAuthenticationMethod.PRIVATE_KEY_JWT, context); 628 return; // success 629 } 630 } 631 } 632 633 throw InvalidClientException.BAD_JWT_SIGNATURE; 634 635 } else if (clientAuth instanceof SelfSignedTLSClientAuthentication) { 636 637 SelfSignedTLSClientAuthentication tlsClientAuth = (SelfSignedTLSClientAuthentication) clientAuth; 638 639 X509Certificate clientCert = tlsClientAuth.getClientX509Certificate(); 640 641 if (clientCert == null) { 642 // Sanity check 643 throw new InvalidClientException("Missing client X.509 certificate"); 644 } 645 646 // Self-signed certs bound to registered public key in client jwks / jwks_uri 647 List<? extends PublicKey> keyCandidates = ListUtils.removeNullItems( 648 clientCredentialsSelector.selectPublicKeys( 649 tlsClientAuth.getClientID(), 650 tlsClientAuth.getMethod(), 651 null, 652 false, // don't force refresh if we have a remote JWK set; 653 // selector may however do so if it encounters an unknown key ID 654 context 655 ) 656 ); 657 658 if (CollectionUtils.isEmpty(keyCandidates)) { 659 throw InvalidClientException.NO_MATCHING_JWK; 660 } 661 662 for (PublicKey candidate : keyCandidates) { 663 664 boolean valid = X509CertificateUtils.publicKeyMatches(clientCert, candidate); 665 666 if (valid) { 667 return; // success 668 } 669 } 670 671 // Second pass 672 if (hints != null && hints.contains(Hint.CLIENT_HAS_REMOTE_JWK_SET)) { 673 // Client possibly registered JWK set URL with keys that have no IDs 674 // force JWK set reload from URL and retry 675 keyCandidates = ListUtils.removeNullItems( 676 clientCredentialsSelector.selectPublicKeys( 677 tlsClientAuth.getClientID(), 678 tlsClientAuth.getMethod(), 679 null, 680 true, // force reload of remote JWK set 681 context 682 ) 683 ); 684 685 if (CollectionUtils.isEmpty(keyCandidates)) { 686 throw InvalidClientException.NO_MATCHING_JWK; 687 } 688 689 for (PublicKey candidate : keyCandidates) { 690 691 if (candidate == null) { 692 continue; // skip 693 } 694 695 boolean valid = X509CertificateUtils.publicKeyMatches(clientCert, candidate); 696 697 if (valid) { 698 return; // success 699 } 700 } 701 } 702 703 throw InvalidClientException.BAD_SELF_SIGNED_CLIENT_CERTIFICATE; 704 705 } else if (clientAuth instanceof PKITLSClientAuthentication) { 706 707 PKITLSClientAuthentication tlsClientAuth = (PKITLSClientAuthentication) clientAuth; 708 if (pkiCertBindingVerifier != null) { 709 pkiCertBindingVerifier.verifyCertificateBinding( 710 clientAuth.getClientID(), 711 tlsClientAuth.getClientX509Certificate(), 712 context); 713 714 } else if (certBindingVerifier != null) { 715 certBindingVerifier.verifyCertificateBinding( 716 clientAuth.getClientID(), 717 tlsClientAuth.getClientX509CertificateSubjectDN(), 718 context); 719 } else { 720 throw new InvalidClientException("Mutual TLS client Authentication (tls_client_auth) not supported"); 721 } 722 } else { 723 throw new RuntimeException("Unexpected client authentication: " + clientAuth.getMethod()); 724 } 725 } 726}