001package com.nimbusds.jose.crypto;
002
003
004import java.security.InvalidKeyException;
005import java.security.Signature;
006import java.security.SignatureException;
007import java.security.interfaces.ECPublicKey;
008import java.util.Set;
009
010import net.jcip.annotations.ThreadSafe;
011
012import com.nimbusds.jose.*;
013import com.nimbusds.jose.jwk.ECKey;
014import com.nimbusds.jose.util.Base64URL;
015
016
017/**
018 * Elliptic Curve Digital Signature Algorithm (ECDSA) verifier of 
019 * {@link com.nimbusds.jose.JWSObject JWS objects}. This class is thread-safe.
020 *
021 * <p>Supports the following algorithms:
022 *
023 * <ul>
024 *     <li>{@link com.nimbusds.jose.JWSAlgorithm#ES256}
025 *     <li>{@link com.nimbusds.jose.JWSAlgorithm#ES384}
026 *     <li>{@link com.nimbusds.jose.JWSAlgorithm#ES512}
027 * </ul>
028 * 
029 * @author Axel Nennker
030 * @author Vladimir Dzhuvinov
031 * @version 2015-06-07
032 */
033@ThreadSafe
034public class ECDSAVerifier extends ECDSAProvider implements JWSVerifier, CriticalHeaderParamsAware {
035
036
037        /**
038         * The critical header policy.
039         */
040        private final CriticalHeaderParamsDeferral critPolicy = new CriticalHeaderParamsDeferral();
041
042
043        /**
044         * The public EC key.
045         */
046        private final ECPublicKey publicKey;
047
048
049        /**
050         * Creates a new Elliptic Curve Digital Signature Algorithm (ECDSA) 
051         * verifier.
052         *
053         * @param publicKey The public EC key. Must not be {@code null}.
054         *
055         * @throws JOSEException If the elliptic curve of key is not supported.
056         */
057        public ECDSAVerifier(final ECPublicKey publicKey)
058                throws JOSEException {
059
060                this(publicKey, null);
061        }
062
063
064
065        /**
066         * Creates a new Elliptic Curve Digital Signature Algorithm (ECDSA)
067         * verifier.
068         *
069         * @param ecJWK The EC JSON Web Key (JWK). Must not be {@code null}.
070         *
071         * @throws JOSEException If the elliptic curve of key is not supported.
072         */
073        public ECDSAVerifier(final ECKey ecJWK)
074                throws JOSEException {
075
076                this(ecJWK.toECPublicKey());
077        }
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         * @param defCritHeaders The names of the critical header parameters
086         *                       that are deferred to the application for
087         *                       processing, empty set or {@code null} if none.
088         *
089         * @throws JOSEException If the elliptic curve of key is not supported.
090         */
091        public ECDSAVerifier(final ECPublicKey publicKey, final Set<String> defCritHeaders)
092                throws JOSEException {
093
094                super(ECDSA.resolveAlgorithm(publicKey));
095
096                this.publicKey = publicKey;
097
098                critPolicy.setDeferredCriticalHeaderParams(defCritHeaders);
099        }
100
101
102        /**
103         * Returns the public EC key.
104         *
105         * @return The public EC key.
106         */
107        public ECPublicKey getPublicKey() {
108
109                return publicKey;
110        }
111
112
113        @Override
114        public Set<String> getProcessedCriticalHeaderParams() {
115
116                return critPolicy.getProcessedCriticalHeaderParams();
117        }
118
119
120        @Override
121        public Set<String> getDeferredCriticalHeaderParams() {
122
123                return critPolicy.getProcessedCriticalHeaderParams();
124        }
125
126
127        @Override
128        public boolean verify(final JWSHeader header,
129                              final byte[] signedContent, 
130                              final Base64URL signature)
131                throws JOSEException {
132
133                final JWSAlgorithm alg = header.getAlgorithm();
134
135                if (! supportedJWSAlgorithms().contains(alg)) {
136                        throw new JOSEException(AlgorithmSupportMessage.unsupportedJWSAlgorithm(alg, supportedJWSAlgorithms()));
137                }
138
139                if (! critPolicy.headerPasses(header)) {
140                        return false;
141                }
142
143                final byte[] jwsSignature = signature.decode();
144
145                final byte[] derSignature;
146
147                try {
148                        derSignature = ECDSA.transcodeSignatureToDER(jwsSignature);
149                } catch (JOSEException e) {
150                        // Invalid signature format
151                        return false;
152                }
153
154                Signature sig = ECDSA.getSignerAndVerifier(alg, getJCAContext().getProvider());
155
156                try {
157                        sig.initVerify(publicKey);
158                        sig.update(signedContent);
159                        return sig.verify(derSignature);
160
161                } catch (InvalidKeyException e) {
162                        throw new JOSEException("Invalid EC public key: " + e.getMessage(), e);
163                } catch (SignatureException e) {
164                        return false;
165                }
166        }
167}