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