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