001 package com.nimbusds.jose.crypto;
002
003
004 import java.math.BigInteger;
005
006 import java.util.HashSet;
007 import java.util.Set;
008
009 import org.bouncycastle.asn1.x9.X9ECParameters;
010
011 import org.bouncycastle.crypto.Digest;
012
013 import org.bouncycastle.crypto.params.ECDomainParameters;
014 import org.bouncycastle.crypto.params.ECPublicKeyParameters;
015
016 import org.bouncycastle.math.ec.ECCurve;
017 import org.bouncycastle.math.ec.ECPoint;
018
019 import net.jcip.annotations.ThreadSafe;
020
021 import com.nimbusds.jose.JOSEException;
022 import com.nimbusds.jose.JWSHeaderFilter;
023 import com.nimbusds.jose.JWSVerifier;
024 import com.nimbusds.jose.ReadOnlyJWSHeader;
025
026 import com.nimbusds.jose.util.Base64URL;
027
028
029
030 /**
031 * Elliptic Curve Digital Signature Algorithm (ECDSA) verifier of
032 * {@link com.nimbusds.jose.JWSObject JWS objects}.
033 *
034 * <p>Supports the following JSON Web Algorithms (JWAs):
035 *
036 * <ul>
037 * <li>{@link com.nimbusds.jose.JWSAlgorithm#ES256}
038 * <li>{@link com.nimbusds.jose.JWSAlgorithm#ES384}
039 * <li>{@link com.nimbusds.jose.JWSAlgorithm#ES512}
040 * </ul>
041 *
042 * <p>Accepts the following JWS header parameters:
043 *
044 * <ul>
045 * <li>{@code alg}
046 * <li>{@code typ}
047 * <li>{@code cty}
048 * </ul>
049 *
050 * @author Axel Nennker
051 * @author Vladimir Dzhuvinov
052 * @version $version$ (2012-10-23)
053 */
054 @ThreadSafe
055 public class ECDSAVerifier extends ECDSAProvider implements JWSVerifier {
056
057
058 /**
059 * The accepted JWS header parameters.
060 */
061 private static final Set<String> ACCEPTED_HEADER_PARAMETERS;
062
063
064 /**
065 * Initialises the accepted JWS header parameters.
066 */
067 static {
068
069 Set<String> params = new HashSet<String>();
070 params.add("alg");
071 params.add("typ");
072 params.add("cty");
073
074 ACCEPTED_HEADER_PARAMETERS = params;
075 }
076
077
078 /**
079 * The JWS header filter.
080 */
081 private DefaultJWSHeaderFilter headerFilter;
082
083
084 /**
085 * The 'x' EC coordinate.
086 */
087 private final BigInteger x;
088
089
090 /**
091 * The 'y' EC coordinate.
092 */
093 private final BigInteger y;
094
095
096
097 /**
098 * Creates a new Elliptic Curve Digital Signature Algorithm (ECDSA)
099 * verifier.
100 *
101 * @param x The 'x' coordinate for the elliptic curve point. Must not be
102 * {@code null}.
103 * @param y The 'y' coordinate for the elliptic curve point. Must not be
104 * {@code null}.
105 */
106 public ECDSAVerifier(final BigInteger x, final BigInteger y) {
107
108 if (x == null)
109 throw new IllegalArgumentException("The \"x\" EC coordinate must not be null");
110
111 this.x = x;
112
113 if (y == null)
114 throw new IllegalArgumentException("The \"y\" EC coordinate must not be null");
115
116 this.y = y;
117
118 headerFilter = new DefaultJWSHeaderFilter(supportedAlgorithms(), ACCEPTED_HEADER_PARAMETERS);
119 }
120
121
122 /**
123 * Gets the 'x' coordinate for the elliptic curve point.
124 *
125 * @return The 'x' coordinate.
126 */
127 public BigInteger getX() {
128
129 return x;
130 }
131
132
133 /**
134 * Gets the 'y' coordinate for the elliptic curve point.
135 *
136 * @return The 'y' coordinate.
137 */
138 public BigInteger getY() {
139
140 return y;
141 }
142
143
144 @Override
145 public JWSHeaderFilter getJWSHeaderFilter() {
146
147 return headerFilter;
148 }
149
150
151 @Override
152 public boolean verify(final ReadOnlyJWSHeader header,
153 final byte[] signedContent,
154 final Base64URL signature)
155 throws JOSEException {
156
157 ECDSAParameters initParams = getECDSAParameters(header.getAlgorithm());
158 X9ECParameters x9ECParameters = initParams.getX9ECParameters();
159 Digest digest = initParams.getDigest();
160
161
162 byte[] signatureBytes = signature.decode();
163
164 byte[] rBytes = new byte[32];
165 byte[] sBytes = new byte[32];
166
167 try {
168 System.arraycopy(signatureBytes, 0, rBytes, 0, 32);
169 System.arraycopy(signatureBytes, 32, sBytes, 0, 32);
170
171 } catch (Exception e) {
172
173 throw new JOSEException("Invalid ECDSA signature format: " + e.getMessage(), e);
174 }
175
176 BigInteger r = new BigInteger(1, rBytes);
177 BigInteger s = new BigInteger(1, sBytes);
178
179
180 ECCurve curve = x9ECParameters.getCurve();
181 ECPoint qB = curve.createPoint(x, y, false);
182 ECPoint q = new ECPoint.Fp(curve, qB.getX(), qB.getY());
183
184 ECDomainParameters ecDomainParameters = new ECDomainParameters(
185 curve,
186 x9ECParameters.getG(),
187 x9ECParameters.getN(),
188 x9ECParameters.getH(),
189 x9ECParameters.getSeed());
190
191 ECPublicKeyParameters ecPublicKeyParameters = new ECPublicKeyParameters(
192 q, ecDomainParameters);
193
194 org.bouncycastle.crypto.signers.ECDSASigner verifier =
195 new org.bouncycastle.crypto.signers.ECDSASigner();
196
197 verifier.init(false, ecPublicKeyParameters);
198
199 digest.update(signedContent, 0, signedContent.length);
200 byte[] out = new byte[digest.getDigestSize()];
201 digest.doFinal(out, 0);
202
203 return verifier.verifySignature(out, r, s);
204 }
205 }