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