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