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.*;
025import com.nimbusds.jose.jwk.source.JWKSource;
026import com.nimbusds.jose.jwk.source.RemoteJWKSet;
027
028import java.net.URL;
029import java.security.Key;
030import java.util.Collections;
031import java.util.HashMap;
032import java.util.List;
033import java.util.Map;
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                for (JWSAlgorithm jwsAlg : jwsAlgFamily) {
059                        this.selectors.put(jwsAlg, new JWSVerificationKeySelector<>(jwsAlg, jwkSource));
060                }
061        }
062
063        
064        @Override
065        public List<? extends Key> selectJWSKeys(final JWSHeader header, final C context)
066                throws KeySourceException {
067                
068                JWSKeySelector<C> selector = this.selectors.get(header.getAlgorithm());
069                if (selector == null) {
070                        return Collections.emptyList();
071                }
072                return selector.selectJWSKeys(header, context);
073        }
074
075        
076        /**
077         * Queries the given JWK Set {@link URL} for keys, creating a
078         * {@link JWSAlgorithmFamilyJWSKeySelector} based on the RSA or EC key
079         * type, whichever comes back first.
080         *
081         * @param jwkSetURL The JWK Set {@link URL} to query.
082         * @param <C>       The {@link SecurityContext}
083         *
084         * @return An instance of {@link JWSAlgorithmFamilyJWSKeySelector}.
085         *
086         * @throws KeySourceException if the JWKs cannot be retrieved or no RSA
087         *                            or EC public JWKs are found.
088         */
089        public static <C extends SecurityContext> JWSAlgorithmFamilyJWSKeySelector<C> fromJWKSetURL(final URL jwkSetURL)
090                throws KeySourceException {
091
092                JWKSource<C> jwkSource = new RemoteJWKSet<>(jwkSetURL);
093                return fromJWKSource(jwkSource);
094        }
095        
096
097        /**
098         * Queries the given {@link JWKSource} for keys, creating a
099         * {@link JWSAlgorithmFamilyJWSKeySelector} based on the RSA or EC key
100         * type, whichever comes back first.
101         *
102         * @param jwkSource The {@link JWKSource}.
103         * @param <C>       The {@link SecurityContext}.
104         *
105         * @return An instance of {@link JWSAlgorithmFamilyJWSKeySelector}.
106         *
107         * @throws KeySourceException If the JWKs cannot be retrieved or no
108         *                            RSA or EC public JWKs are found.
109         */
110        public static <C extends SecurityContext> JWSAlgorithmFamilyJWSKeySelector<C> fromJWKSource(final JWKSource<C> jwkSource)
111                throws KeySourceException {
112                
113                JWKMatcher jwkMatcher = new JWKMatcher.Builder()
114                                .publicOnly(true)
115                                .keyUses(KeyUse.SIGNATURE, null) // use=sig is optional
116                                .keyTypes(KeyType.RSA, KeyType.EC)
117                                .build();
118                List<? extends JWK> jwks = jwkSource.get(new JWKSelector(jwkMatcher), null);
119                for (JWK jwk : jwks) {
120                        if (KeyType.RSA.equals(jwk.getKeyType())) {
121                                return new JWSAlgorithmFamilyJWSKeySelector<>(JWSAlgorithm.Family.RSA, jwkSource);
122                        }
123                        if (KeyType.EC.equals(jwk.getKeyType())) {
124                                return new JWSAlgorithmFamilyJWSKeySelector<>(JWSAlgorithm.Family.EC, jwkSource);
125                        }
126                }
127                throw new KeySourceException("Couldn't retrieve JWKs");
128        }
129}