001package com.nimbusds.oauth2.sdk.jose.jwk;
002
003
004import java.security.Key;
005import java.security.PublicKey;
006import java.util.Collections;
007import java.util.LinkedList;
008import java.util.List;
009import javax.crypto.SecretKey;
010
011import com.nimbusds.jose.JWSAlgorithm;
012import com.nimbusds.jose.JWSHeader;
013import com.nimbusds.jose.jwk.*;
014import com.nimbusds.jose.proc.JWSKeySelector;
015import com.nimbusds.jose.proc.SecurityContext;
016import com.nimbusds.oauth2.sdk.id.Identifier;
017import net.jcip.annotations.ThreadSafe;
018
019
020/**
021 * Key selector for verifying JWS objects used in OpenID Connect.
022 *
023 * <p>Can be used to select RSA and EC key candidates for the verification of:
024 *
025 * <ul>
026 *     <li>Signed ID tokens
027 *     <li>Signed JWT-encoded UserInfo responses
028 *     <li>Signed OpenID request objects
029 * </ul>
030 *
031 * <p>Client secret candidates for the verification of:
032 *
033 * <ul>
034 *     <li>HMAC ID tokens
035 *     <li>HMAC JWT-encoded UserInfo responses
036 *     <li>HMAC OpenID request objects
037 * </ul>
038 */
039@ThreadSafe
040@Deprecated
041public class JWSVerificationKeySelector extends AbstractJWKSelectorWithSource implements JWSKeySelector {
042
043
044        /**
045         * The expected JWS algorithm.
046         */
047        private final JWSAlgorithm jwsAlg;
048
049
050        /**
051         * Creates a new JWS verification key selector.
052         *
053         * @param id        Identifier for the JWS originator, typically an
054         *                  OAuth 2.0 server issuer ID, or client ID. Must not
055         *                  be {@code null}.
056         * @param jwsAlg    The expected JWS algorithm for the objects to be
057         *                  verified. Must not be {@code null}.
058         * @param jwkSource The JWK source. Must not be {@code null}.
059         */
060        public JWSVerificationKeySelector(final Identifier id, final JWSAlgorithm jwsAlg, final JWKSource jwkSource) {
061                super(id, jwkSource);
062                if (jwsAlg == null) {
063                        throw new IllegalArgumentException("The JWS algorithm must not be null");
064                }
065                this.jwsAlg = jwsAlg;
066        }
067
068
069        /**
070         * Returns the expected JWS algorithm.
071         *
072         * @return The expected JWS algorithm.
073         */
074        public JWSAlgorithm getExpectedJWSAlgorithm() {
075
076                return jwsAlg;
077        }
078
079
080        /**
081         * Creates a JWK matcher for the expected JWS algorithm and the
082         * specified JWS header.
083         *
084         * @param jwsHeader The JWS header. Must not be {@code null}.
085         *
086         * @return The JWK matcher, {@code null} if none could be created.
087         */
088        protected JWKMatcher createJWKMatcher(final JWSHeader jwsHeader) {
089
090                if (! getExpectedJWSAlgorithm().equals(jwsHeader.getAlgorithm())) {
091                        // Unexpected JWS alg
092                        return null;
093                } else if (JWSAlgorithm.Family.RSA.contains(getExpectedJWSAlgorithm()) || JWSAlgorithm.Family.EC.contains(getExpectedJWSAlgorithm())) {
094                        // RSA or EC key matcher
095                        return new JWKMatcher.Builder()
096                                        .keyType(KeyType.forAlgorithm(getExpectedJWSAlgorithm()))
097                                        .keyID(jwsHeader.getKeyID())
098                                        .keyUses(KeyUse.SIGNATURE, null)
099                                        .algorithms(getExpectedJWSAlgorithm(), null)
100                                        .build();
101                } else if (JWSAlgorithm.Family.HMAC_SHA.contains(getExpectedJWSAlgorithm())) {
102                        // Client secret matcher
103                        return new JWKMatcher.Builder()
104                                        .keyType(KeyType.forAlgorithm(getExpectedJWSAlgorithm()))
105                                        .keyID(jwsHeader.getKeyID())
106                                        .privateOnly(true)
107                                        .algorithms(getExpectedJWSAlgorithm(), null)
108                                        .build();
109                } else {
110                        return null; // Unsupported algorithm
111                }
112        }
113
114
115        @Override
116        public List<Key> selectJWSKeys(final JWSHeader jwsHeader, final SecurityContext context) {
117
118                if (! jwsAlg.equals(jwsHeader.getAlgorithm())) {
119                        // Unexpected JWS alg
120                        return Collections.emptyList();
121                }
122
123                JWKMatcher jwkMatcher = createJWKMatcher(jwsHeader);
124                if (jwkMatcher == null) {
125                        return Collections.emptyList();
126                }
127
128                List<JWK> jwkMatches = getJWKSource().get(getIdentifier(), new JWKSelector(jwkMatcher));
129
130                List<Key> sanitizedKeyList = new LinkedList<>();
131
132                for (Key key: KeyConverter.toJavaKeys(jwkMatches)) {
133                        if (key instanceof PublicKey || key instanceof SecretKey) {
134                                sanitizedKeyList.add(key);
135                        } // skip asymmetric private keys
136                }
137
138                return sanitizedKeyList;
139        }
140}