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.jwk.ECKey;
031import com.nimbusds.jose.util.Base64URL;
032
033
034/**
035 * Elliptic Curve Digital Signature Algorithm (ECDSA) verifier of 
036 * {@link com.nimbusds.jose.JWSObject JWS objects}. Expects a private EC key
037 * (with a P-256, P-384 or P-521 curve).
038 *
039 * <p>See RFC 7518
040 * <a href="https://tools.ietf.org/html/rfc7518#section-3.4">section 3.4</a>
041 * for more information.
042 *
043 * <p>This class is thread-safe.
044 *
045 * <p>Supports the following algorithms:
046 *
047 * <ul>
048 *     <li>{@link com.nimbusds.jose.JWSAlgorithm#ES256}
049 *     <li>{@link com.nimbusds.jose.JWSAlgorithm#ES384}
050 *     <li>{@link com.nimbusds.jose.JWSAlgorithm#ES512}
051 * </ul>
052 * 
053 * @author Axel Nennker
054 * @author Vladimir Dzhuvinov
055 * @version 2015-06-07
056 */
057@ThreadSafe
058public class ECDSAVerifier extends ECDSAProvider implements JWSVerifier, CriticalHeaderParamsAware {
059
060
061        /**
062         * The critical header policy.
063         */
064        private final CriticalHeaderParamsDeferral critPolicy = new CriticalHeaderParamsDeferral();
065
066
067        /**
068         * The public EC key.
069         */
070        private final ECPublicKey publicKey;
071
072
073        /**
074         * Creates a new Elliptic Curve Digital Signature Algorithm (ECDSA) 
075         * verifier.
076         *
077         * @param publicKey The public EC key. Must not be {@code null}.
078         *
079         * @throws JOSEException If the elliptic curve of key is not supported.
080         */
081        public ECDSAVerifier(final ECPublicKey publicKey)
082                throws JOSEException {
083
084                this(publicKey, null);
085        }
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                critPolicy.setDeferredCriticalHeaderParams(defCritHeaders);
123        }
124
125
126        /**
127         * Returns the public EC key.
128         *
129         * @return The public EC key.
130         */
131        public ECPublicKey getPublicKey() {
132
133                return publicKey;
134        }
135
136
137        @Override
138        public Set<String> getProcessedCriticalHeaderParams() {
139
140                return critPolicy.getProcessedCriticalHeaderParams();
141        }
142
143
144        @Override
145        public Set<String> getDeferredCriticalHeaderParams() {
146
147                return critPolicy.getProcessedCriticalHeaderParams();
148        }
149
150
151        @Override
152        public boolean verify(final JWSHeader header,
153                              final byte[] signedContent, 
154                              final Base64URL signature)
155                throws JOSEException {
156
157                final JWSAlgorithm alg = header.getAlgorithm();
158
159                if (! supportedJWSAlgorithms().contains(alg)) {
160                        throw new JOSEException(AlgorithmSupportMessage.unsupportedJWSAlgorithm(alg, supportedJWSAlgorithms()));
161                }
162
163                if (! critPolicy.headerPasses(header)) {
164                        return false;
165                }
166
167                final byte[] jwsSignature = signature.decode();
168
169                final byte[] derSignature;
170
171                try {
172                        derSignature = ECDSA.transcodeSignatureToDER(jwsSignature);
173                } catch (JOSEException e) {
174                        // Invalid signature format
175                        return false;
176                }
177
178                Signature sig = ECDSA.getSignerAndVerifier(alg, getJCAContext().getProvider());
179
180                try {
181                        sig.initVerify(publicKey);
182                        sig.update(signedContent);
183                        return sig.verify(derSignature);
184
185                } catch (InvalidKeyException e) {
186                        throw new JOSEException("Invalid EC public key: " + e.getMessage(), e);
187                } catch (SignatureException e) {
188                        return false;
189                }
190        }
191}