001/*
002 * nimbus-jose-jwt
003 *
004 * Copyright 2012-2016, 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.crypto;
019
020
021import java.security.InvalidKeyException;
022import java.security.Signature;
023import java.security.SignatureException;
024import java.security.interfaces.ECPublicKey;
025import java.util.Set;
026
027import net.jcip.annotations.ThreadSafe;
028
029import com.nimbusds.jose.*;
030import com.nimbusds.jose.crypto.impl.AlgorithmSupportMessage;
031import com.nimbusds.jose.crypto.impl.CriticalHeaderParamsDeferral;
032import com.nimbusds.jose.crypto.impl.ECDSA;
033import com.nimbusds.jose.crypto.impl.ECDSAProvider;
034import com.nimbusds.jose.crypto.utils.ECChecks;
035import com.nimbusds.jose.jwk.Curve;
036import com.nimbusds.jose.jwk.ECKey;
037import com.nimbusds.jose.util.Base64URL;
038
039
040/**
041 * Elliptic Curve Digital Signature Algorithm (ECDSA) verifier of 
042 * {@link com.nimbusds.jose.JWSObject JWS objects}. Expects a public EC key
043 * (with a P-256, P-384,  P-521 or secp256k1 curve).
044 *
045 * <p>See RFC 7518
046 * <a href="https://tools.ietf.org/html/rfc7518#section-3.4">section 3.4</a>
047 * for more information.
048 *
049 * <p>This class is thread-safe.
050 *
051 * <p>Supports the following algorithms:
052 *
053 * <ul>
054 *     <li>{@link com.nimbusds.jose.JWSAlgorithm#ES256}
055 *     <li>{@link com.nimbusds.jose.JWSAlgorithm#ES256K}
056 *     <li>{@link com.nimbusds.jose.JWSAlgorithm#ES384}
057 *     <li>{@link com.nimbusds.jose.JWSAlgorithm#ES512}
058 * </ul>
059 * 
060 * @author Axel Nennker
061 * @author Vladimir Dzhuvinov
062 * @version 2022-04-22
063 */
064@ThreadSafe
065public class ECDSAVerifier extends ECDSAProvider implements JWSVerifier, CriticalHeaderParamsAware {
066
067
068        /**
069         * The critical header policy.
070         */
071        private final CriticalHeaderParamsDeferral critPolicy = new CriticalHeaderParamsDeferral();
072
073
074        /**
075         * The public EC key.
076         */
077        private final ECPublicKey publicKey;
078
079
080        /**
081         * Creates a new Elliptic Curve Digital Signature Algorithm (ECDSA) 
082         * verifier.
083         *
084         * @param publicKey The public EC key. Must not be {@code null}.
085         *
086         * @throws JOSEException If the elliptic curve of key is not supported.
087         */
088        public ECDSAVerifier(final ECPublicKey publicKey)
089                throws JOSEException {
090
091                this(publicKey, null);
092        }
093        
094
095        /**
096         * Creates a new Elliptic Curve Digital Signature Algorithm (ECDSA)
097         * verifier.
098         *
099         * @param ecJWK The EC JSON Web Key (JWK). Must not be {@code null}.
100         *
101         * @throws JOSEException If the elliptic curve of key is not supported.
102         */
103        public ECDSAVerifier(final ECKey ecJWK)
104                throws JOSEException {
105
106                this(ecJWK.toECPublicKey());
107        }
108
109
110        /**
111         * Creates a new Elliptic Curve Digital Signature Algorithm (ECDSA)
112         * verifier.
113         *
114         * @param publicKey      The public EC key. Must not be {@code null}.
115         * @param defCritHeaders The names of the critical header parameters
116         *                       that are deferred to the application for
117         *                       processing, empty set or {@code null} if none.
118         *
119         * @throws JOSEException If the elliptic curve of key is not supported.
120         */
121        public ECDSAVerifier(final ECPublicKey publicKey, final Set<String> defCritHeaders)
122                throws JOSEException {
123
124                super(ECDSA.resolveAlgorithm(publicKey));
125
126                this.publicKey = publicKey;
127                
128                if (! ECChecks.isPointOnCurve(
129                        publicKey,
130                        Curve.forJWSAlgorithm(supportedECDSAAlgorithm()).iterator().next().toECParameterSpec())) {
131                        throw new JOSEException("Curve / public key parameters mismatch");
132                }
133
134                critPolicy.setDeferredCriticalHeaderParams(defCritHeaders);
135        }
136
137
138        /**
139         * Returns the public EC key.
140         *
141         * @return The public EC key.
142         */
143        public ECPublicKey getPublicKey() {
144
145                return publicKey;
146        }
147
148
149        @Override
150        public Set<String> getProcessedCriticalHeaderParams() {
151
152                return critPolicy.getProcessedCriticalHeaderParams();
153        }
154
155
156        @Override
157        public Set<String> getDeferredCriticalHeaderParams() {
158
159                return critPolicy.getProcessedCriticalHeaderParams();
160        }
161
162
163        @Override
164        public boolean verify(final JWSHeader header,
165                              final byte[] signedContent, 
166                              final Base64URL signature)
167                throws JOSEException {
168
169                final JWSAlgorithm alg = header.getAlgorithm();
170
171                if (! supportedJWSAlgorithms().contains(alg)) {
172                        throw new JOSEException(AlgorithmSupportMessage.unsupportedJWSAlgorithm(alg, supportedJWSAlgorithms()));
173                }
174
175                if (! critPolicy.headerPasses(header)) {
176                        return false;
177                }
178
179                final byte[] jwsSignature = signature.decode();
180                
181                // Prevent CVE-2022-21449 and similar attacks
182                try {
183                        ECDSA.ensureLegalSignature(jwsSignature, alg);
184                } catch (JOSEException e) {
185                        return false;
186                }
187                
188                final byte[] derSignature;
189                try {
190                        derSignature = ECDSA.transcodeSignatureToDER(jwsSignature);
191                } catch (JOSEException e) {
192                        // Invalid signature format
193                        return false;
194                }
195
196                Signature sig = ECDSA.getSignerAndVerifier(alg, getJCAContext().getProvider());
197
198                try {
199                        sig.initVerify(publicKey);
200                        sig.update(signedContent);
201                        return sig.verify(derSignature);
202
203                } catch (InvalidKeyException e) {
204                        throw new JOSEException("Invalid EC public key: " + e.getMessage(), e);
205                } catch (SignatureException e) {
206                        return false;
207                }
208        }
209}