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