001/* 002 * nimbus-jose-jwt 003 * 004 * Copyright 2012-2019, Connect2id Ltd. 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.jose.proc; 019 020 021import com.nimbusds.jose.JWSAlgorithm; 022import com.nimbusds.jose.JWSHeader; 023import com.nimbusds.jose.KeySourceException; 024import com.nimbusds.jose.jwk.JWK; 025import com.nimbusds.jose.jwk.JWKMatcher; 026import com.nimbusds.jose.jwk.JWKSelector; 027import com.nimbusds.jose.jwk.KeyConverter; 028import com.nimbusds.jose.jwk.source.JWKSource; 029import net.jcip.annotations.ThreadSafe; 030 031import javax.crypto.SecretKey; 032import java.security.Key; 033import java.security.PublicKey; 034import java.util.*; 035 036 037/** 038 * Key selector for verifying JWS objects, where the key candidates are 039 * retrieved from a {@link JWKSource JSON Web Key (JWK) source}. 040 * 041 * @author Vladimir Dzhuvinov 042 * @author Marco Vermeulen 043 * @version 2024-04-20 044 */ 045@ThreadSafe 046public class JWSVerificationKeySelector<C extends SecurityContext> extends AbstractJWKSelectorWithSource<C> implements JWSKeySelector<C> { 047 048 049 /** 050 * The allowed JWS algorithms. 051 */ 052 private final Set<JWSAlgorithm> jwsAlgs; 053 054 /** 055 * Present to maintain backward compatibility 056 */ 057 private final boolean singleJwsAlgConstructorWasCalled; 058 059 /** 060 * Creates a new JWS verification key selector. 061 * 062 * @param jwsAlg The allowed JWS algorithm for the objects to be 063 * verified. Must not be {@code null}. 064 * @param jwkSource The JWK source. Must not be {@code null}. 065 */ 066 public JWSVerificationKeySelector(final JWSAlgorithm jwsAlg, final JWKSource<C> jwkSource) { 067 super(jwkSource); 068 this.jwsAlgs = Collections.singleton(Objects.requireNonNull(jwsAlg)); 069 this.singleJwsAlgConstructorWasCalled = true; 070 } 071 072 073 /** 074 * Creates a new JWS verification key selector. 075 * 076 * @param jwsAlgs The allowed JWS algorithms for the objects to be 077 * verified. Must not be empty or {@code null}. 078 * @param jwkSource The JWK source. Must not be {@code null}. 079 */ 080 public JWSVerificationKeySelector(final Set<JWSAlgorithm> jwsAlgs, final JWKSource<C> jwkSource) { 081 super(jwkSource); 082 if (jwsAlgs.isEmpty()) { 083 throw new IllegalArgumentException("The JWS algorithms must not be empty"); 084 } 085 this.jwsAlgs = Collections.unmodifiableSet(jwsAlgs); 086 this.singleJwsAlgConstructorWasCalled = false; 087 } 088 089 090 /** 091 * Checks if a JWS algorithm is allowed for key selection. 092 * 093 * @param jwsAlg The JWS algorithm to check. 094 * 095 * @return {@code true} if allowed, else {@code false}. 096 */ 097 public boolean isAllowed(final JWSAlgorithm jwsAlg) { 098 return jwsAlgs.contains(jwsAlg); 099 } 100 101 102 /** 103 * Returns the expected JWS algorithm. 104 * 105 * @return The expected JWS algorithm. 106 * @deprecated Use {@link #isAllowed(JWSAlgorithm)} instead 107 */ 108 @Deprecated 109 public JWSAlgorithm getExpectedJWSAlgorithm() { 110 if (singleJwsAlgConstructorWasCalled) { 111 return jwsAlgs.iterator().next(); 112 } 113 throw new UnsupportedOperationException("Since this class was constructed with multiple " + 114 "algorithms, the behavior of this method is undefined."); 115 } 116 117 /** 118 * Creates a JWK matcher for the expected JWS algorithm and the 119 * specified JWS header. 120 * 121 * @param jwsHeader The JWS header. Must not be {@code null}. 122 * 123 * @return The JWK matcher, {@code null} if none could be created. 124 */ 125 protected JWKMatcher createJWKMatcher(final JWSHeader jwsHeader) { 126 127 if (! isAllowed(jwsHeader.getAlgorithm())) { 128 // Unexpected JWS alg 129 return null; 130 } else { 131 return JWKMatcher.forJWSHeader(jwsHeader); 132 } 133 } 134 135 136 @Override 137 public List<Key> selectJWSKeys(final JWSHeader jwsHeader, final C context) 138 throws KeySourceException { 139 140 if (! jwsAlgs.contains(jwsHeader.getAlgorithm())) { 141 // Unexpected JWS alg 142 return Collections.emptyList(); 143 } 144 145 JWKMatcher jwkMatcher = createJWKMatcher(jwsHeader); 146 if (jwkMatcher == null) { 147 return Collections.emptyList(); 148 } 149 150 List<JWK> jwkMatches = getJWKSource().get(new JWKSelector(jwkMatcher), context); 151 152 List<Key> sanitizedKeyList = new LinkedList<>(); 153 154 for (Key key: KeyConverter.toJavaKeys(jwkMatches)) { 155 if (key instanceof PublicKey || key instanceof SecretKey) { 156 sanitizedKeyList.add(key); 157 } // skip asymmetric private keys 158 } 159 160 return sanitizedKeyList; 161 } 162}