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.net.URL;
022import java.security.Key;
023import java.util.Collections;
024import java.util.HashMap;
025import java.util.List;
026import java.util.Map;
027
028import com.nimbusds.jose.JWSAlgorithm;
029import com.nimbusds.jose.JWSHeader;
030import com.nimbusds.jose.KeySourceException;
031import com.nimbusds.jose.jwk.*;
032import com.nimbusds.jose.jwk.source.JWKSource;
033import com.nimbusds.jose.jwk.source.RemoteJWKSet;
034
035/**
036 * A {@link JWSKeySelector} that expects an algorithm from a specified
037 * algorithm family.
038 *
039 * @author Josh Cummings
040 * @since 2019-07-12
041 */
042public class JWSAlgorithmFamilyJWSKeySelector<C extends SecurityContext> extends AbstractJWKSelectorWithSource<C> implements JWSKeySelector<C> {
043        
044        
045        private final Map<JWSAlgorithm, JWSKeySelector<C>> selectors = new HashMap<>();
046
047        
048        /**
049         * Creates a {@link JWSKeySelector} that matches any algorithm from the
050         * given {@link JWSAlgorithm.Family}.
051         *
052         * @param jwsAlgFamily The {@link JWSAlgorithm.Family} to use.
053         * @param jwkSource    The {@link JWKSource} from which to draw the set
054         *                     of {@link JWK}s.
055         */
056        public JWSAlgorithmFamilyJWSKeySelector(final JWSAlgorithm.Family jwsAlgFamily, final JWKSource<C> jwkSource) {
057                super(jwkSource);
058                if (jwsAlgFamily == null) {
059                        throw new IllegalArgumentException("JWS algorithm family must not be null");
060                }
061                for (JWSAlgorithm jwsAlg : jwsAlgFamily) {
062                        this.selectors.put(jwsAlg, new JWSVerificationKeySelector<>(jwsAlg, jwkSource));
063                }
064        }
065
066        
067        @Override
068        public List<? extends Key> selectJWSKeys(final JWSHeader header, final C context)
069                throws KeySourceException {
070                
071                JWSKeySelector<C> selector = this.selectors.get(header.getAlgorithm());
072                if (selector == null) {
073                        return Collections.emptyList();
074                }
075                return selector.selectJWSKeys(header, context);
076        }
077
078        
079        /**
080         * Queries the given JWK Set {@link URL} for keys, creating a
081         * {@link JWSAlgorithmFamilyJWSKeySelector} based on the RSA or EC key
082         * type, whichever comes back first.
083         *
084         * @param jwkSetURL The JWK Set {@link URL} to query.
085         * @param <C>       The {@link SecurityContext}
086         *
087         * @return An instance of {@link JWSAlgorithmFamilyJWSKeySelector}.
088         *
089         * @throws KeySourceException if the JWKs cannot be retrieved or no RSA
090         *                            or EC public JWKs are found.
091         */
092        public static <C extends SecurityContext> JWSAlgorithmFamilyJWSKeySelector<C> fromJWKSetURL(final URL jwkSetURL)
093                throws KeySourceException {
094
095                JWKSource<C> jwkSource = new RemoteJWKSet<>(jwkSetURL);
096                return fromJWKSource(jwkSource);
097        }
098        
099
100        /**
101         * Queries the given {@link JWKSource} for keys, creating a
102         * {@link JWSAlgorithmFamilyJWSKeySelector} based on the RSA or EC key
103         * type, whichever comes back first.
104         *
105         * @param jwkSource The {@link JWKSource}.
106         * @param <C>       The {@link SecurityContext}.
107         *
108         * @return An instance of {@link JWSAlgorithmFamilyJWSKeySelector}.
109         *
110         * @throws KeySourceException If the JWKs cannot be retrieved or no
111         *                            RSA or EC public JWKs are found.
112         */
113        public static <C extends SecurityContext> JWSAlgorithmFamilyJWSKeySelector<C> fromJWKSource(final JWKSource<C> jwkSource)
114                throws KeySourceException {
115                
116                JWKMatcher jwkMatcher = new JWKMatcher.Builder()
117                                .publicOnly(true)
118                                .keyUses(KeyUse.SIGNATURE, null) // use=sig is optional
119                                .keyTypes(KeyType.RSA, KeyType.EC)
120                                .build();
121                List<? extends JWK> jwks = jwkSource.get(new JWKSelector(jwkMatcher), null);
122                for (JWK jwk : jwks) {
123                        if (KeyType.RSA.equals(jwk.getKeyType())) {
124                                return new JWSAlgorithmFamilyJWSKeySelector<>(JWSAlgorithm.Family.RSA, jwkSource);
125                        }
126                        if (KeyType.EC.equals(jwk.getKeyType())) {
127                                return new JWSAlgorithmFamilyJWSKeySelector<>(JWSAlgorithm.Family.EC, jwkSource);
128                        }
129                }
130                throw new KeySourceException("Couldn't retrieve JWKs");
131        }
132}