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 /** 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 if (jwsAlg == null) { 069 throw new IllegalArgumentException("The JWS algorithm must not be null"); 070 } 071 this.jwsAlgs = Collections.singleton(jwsAlg); 072 } 073 074 075 /** 076 * Creates a new JWS verification key selector. 077 * 078 * @param jwsAlgs The allowed JWS algorithms for the objects to be 079 * verified. Must not be empty or {@code null}. 080 * @param jwkSource The JWK source. Must not be {@code null}. 081 */ 082 public JWSVerificationKeySelector(final Set<JWSAlgorithm> jwsAlgs, final JWKSource<C> jwkSource) { 083 super(jwkSource); 084 if (jwsAlgs == null || jwsAlgs.isEmpty()) { 085 throw new IllegalArgumentException("The JWS algorithms must not be null or empty"); 086 } 087 this.jwsAlgs = Collections.unmodifiableSet(jwsAlgs); 088 } 089 090 091 /** 092 * Checks if a JWS algorithm is allowed for key selection. 093 * 094 * @param jwsAlg The JWS algorithm to check. 095 * 096 * @return {@code true} if allowed, else {@code false}. 097 */ 098 public boolean isAllowed(final JWSAlgorithm jwsAlg) { 099 return jwsAlgs.contains(jwsAlg); 100 } 101 102 103 /** 104 * Creates a JWK matcher for the expected JWS algorithm and the 105 * specified JWS header. 106 * 107 * @param jwsHeader The JWS header. Must not be {@code null}. 108 * 109 * @return The JWK matcher, {@code null} if none could be created. 110 */ 111 protected JWKMatcher createJWKMatcher(final JWSHeader jwsHeader) { 112 113 if (! isAllowed(jwsHeader.getAlgorithm())) { 114 // Unexpected JWS alg 115 return null; 116 } else { 117 return JWKMatcher.forJWSHeader(jwsHeader); 118 } 119 } 120 121 122 @Override 123 public List<Key> selectJWSKeys(final JWSHeader jwsHeader, final C context) 124 throws KeySourceException { 125 126 if (! jwsAlgs.contains(jwsHeader.getAlgorithm())) { 127 // Unexpected JWS alg 128 return Collections.emptyList(); 129 } 130 131 JWKMatcher jwkMatcher = createJWKMatcher(jwsHeader); 132 if (jwkMatcher == null) { 133 return Collections.emptyList(); 134 } 135 136 List<JWK> jwkMatches = getJWKSource().get(new JWKSelector(jwkMatcher), context); 137 138 List<Key> sanitizedKeyList = new LinkedList<>(); 139 140 for (Key key: KeyConverter.toJavaKeys(jwkMatches)) { 141 if (key instanceof PublicKey || key instanceof SecretKey) { 142 sanitizedKeyList.add(key); 143 } // skip asymmetric private keys 144 } 145 146 return sanitizedKeyList; 147 } 148}