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 }