CVE-2017-12974 (Java, published 2017-08-20T16:29Z)
Description: value: Nimbus JOSE+JWT before 4.36 proceeds with ECKey construction without ensuring that the public x and y coordinates are on the specified curve, which allows attackers to conduct an Invalid Curve Attack in environments where the JCE provider lacks the applicable curve validation.
Severity: MEDIUM (CVSS v3 base score: 7.5)
Weakness: CWE-347 \u2014 Improper Verification of Cryptographic Signature
The product does not verify, or incorrectly verifies, the cryptographic signature for data.
Repository: https://bitbucket.org/connect2id/nimbus-jose-jwt
Files changed:
CHANGELOG.txt
src/main/java/com/nimbusds/jose/crypto/ECDH.java
src/main/java/com/nimbusds/jose/crypto/ECDHDecrypter.java
src/main/java/com/nimbusds/jose/crypto/ECDSA.java
src/main/java/com/nimbusds/jose/crypto/ECDSAProvider.java
src/main/java/com/nimbusds/jose/crypto/ECDSAVerifier.java
src/main/java/com/nimbusds/jose/crypto/utils/ECChecks.java
src/main/java/com/nimbusds/jose/crypto/utils/package-info.java
src/main/java/com/nimbusds/jose/jwk/ECKey.java
src/test/java/com/nimbusds/jose/crypto/ECDHCryptoTest.java
src/test/java/com/nimbusds/jose/crypto/ECDHCurveCheckTest.java
src/test/java/com/nimbusds/jose/crypto/ECDHTest.java
src/test/java/com/nimbusds/jose/crypto/utils/ECChecksTest.java
src/test/java/com/nimbusds/jose/jwk/ECKeyTest.java
src/test/java/com/nimbusds/jose/jwk/JWKMatcherTest.java
src/test/java/com/nimbusds/jose/jwk/JWKSelectorTest.java
src/test/java/com/nimbusds/jose/jwk/JWKSetTest.java
Commit message: Adds EC JWK constructor checks for x and y being on crv (iss #217)


Vulnerable code:
import java.math.BigInteger;
import java.security.spec.ECFieldFp;
import java.security.spec.ECParameterSpec;
import java.security.spec.ECPoint;
import java.security.spec.EllipticCurve;

 * @version 2017-02-28
	public static AlgorithmMode resolveAlgorithmMode(final JWEAlgorithm alg)
	public static int sharedKeyLength(final JWEAlgorithm alg, final EncryptionMethod enc)
	public static SecretKey deriveSharedSecret(final ECPublicKey publicKey,
						   final ECPrivateKey privateKey,
						   final Provider provider)
	public static SecretKey deriveSharedKey(final JWEHeader header,
						final SecretKey Z,
						final ConcatKDF concatKDF)
	
	
	/**
	 * Ensures the specified ephemeral public key is on the curve of the
	 * private key. Intended to prevent an "Invalid Curve Attack",
	 * independent from any JCA provider checks (the SUN provider in Java
	 * 1.8.0_51+ and BouncyCastle have them, other / older provider do
	 * not).
	 *
	 * <p>See https://www.cs.bris.ac.uk/Research/CryptographySecurity/RWC/2017/nguyen.quan.pdf
	 *
	 * @param ephemeralPublicKey The ephemeral public EC key. Must not be
	 *                           {@code null}.
	 * @param privateKey         The private EC key. Must not be
	 *                           {@code null}.
	 *
	 * @throws JOSEException If the ephemeral public key didn't pass the
	 *                       curve check.
	 */
	public static void ensurePointOnCurve(final ECPublicKey ephemeralPublicKey, final ECPrivateKey privateKey)
		throws JOSEException {
		
		// Ensure the following is met:
		// (y^2) mod p = (x^3 + ax + b) mod p
		ECParameterSpec ecParameterSpec = privateKey.getParams();
		EllipticCurve curve = ecParameterSpec.getCurve();
		ECPoint point = ephemeralPublicKey.getW();
		BigInteger x = point.getAffineX();
		BigInteger y = point.getAffineY();
		BigInteger a = curve.getA();
		BigInteger b = curve.getB();
		BigInteger p = ((ECFieldFp) curve.getField()).getP();
		BigInteger leftSide = (y.pow(2)).mod(p);
		BigInteger rightSide = (x.pow(3).add(a.multiply(x)).add(b)).mod(p);
		
		if (! leftSide.equals(rightSide)) {
			throw new JOSEException("Invalid ephemeral public key: Point not on expected curve");
		}
	}


 * @version 2017-02-28
		ECDH.ensurePointOnCurve(ephemeralPublicKey, getPrivateKey());
			throw new JOSEException("Invalid  ECDSA signature format");
			throw new JOSEException("Invalid  ECDSA signature format");
			throw new JOSEException("Invalid  ECDSA signature format");
 * @version 2015-06-07
import net.jcip.annotations.ThreadSafe;

 * @version 2015-06-07


 * @version 2015-09-18
	public void testRejectECJWKWithUnsupportedCurve()
		throws Exception {

		KeyPair keyPair = createUnsupportedECKeyPair();
		ECPublicKey ecPublicKey = (ECPublicKey)keyPair.getPublic();
		ECPrivateKey ecPrivateKey = (ECPrivateKey)keyPair.getPrivate();

		ECKey ecJWK = new ECKey.Builder(new ECKey.Curve("P-224"), ecPublicKey).privateKey(ecPrivateKey).build();

		try {
			new ECDHEncrypter(ecJWK);
			fail();
		} catch (JOSEException e) {
			// ok
		}

		try {
			new ECDHDecrypter(ecJWK);
			fail();
		} catch (JOSEException e) {
			// ok
		}
	}


 * Submitted by Antonio Sanso. See iss #210.
		JWEObject jweObject1 = JWEObject.parse(maliciousJWE1);
		
		ECDHDecrypter decrypter = new ECDHDecrypter(ecJWK.toECPrivateKey());
//		decrypter.getJCAContext().setKeyEncryptionProvider(BouncyCastleProviderSingleton.getInstance());
			jweObject1.decrypt(decrypter);
		} catch (JOSEException e) {
			assertEquals("Invalid ephemeral public key: Point not on expected curve", e.getMessage());
		JWEObject jweObject2 = JWEObject.parse(maliciousJWE2);
//		decrypter.getJCAContext().setContentEncryptionProvider(BouncyCastleProviderSingleton.getInstance());
			jweObject2.decrypt(decrypter);
		} catch (JOSEException e) {
			assertEquals("Invalid ephemeral public key: Point not on expected curve", e.getMessage());
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.ECPublicKey;
import java.security.spec.ECParameterSpec;
import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.JWEObject;
 * @version 2017-02-28
	
	
	private static ECPrivateKey generateECPrivateKey(final ECKey.Curve curve)
		throws Exception {
		
		final ECParameterSpec ecParameterSpec = curve.toECParameterSpec();
		
		KeyPairGenerator generator = KeyPairGenerator.getInstance("EC");
		generator.initialize(ecParameterSpec);
		KeyPair keyPair = generator.generateKeyPair();
		
		return (ECPrivateKey) keyPair.getPrivate();
	}
	
	
	private static ECPublicKey generateECPublicKey(final ECKey.Curve curve)
		throws Exception {
		
		final ECParameterSpec ecParameterSpec = curve.toECParameterSpec();
		
		KeyPairGenerator generator = KeyPairGenerator.getInstance("EC");
		generator.initialize(ecParameterSpec);
		KeyPair keyPair = generator.generateKeyPair();
		
		return (ECPublicKey) keyPair.getPublic();
	}
	
	
	public void testCurveCheckOk()
		throws Exception {
		
		ECPublicKey ephemeralPublicKey = generateECPublicKey(ECKey.Curve.P_256);
		ECPrivateKey privateKey = generateECPrivateKey(ECKey.Curve.P_256);
		ECDH.ensurePointOnCurve(ephemeralPublicKey, privateKey);
	}
	
	
	public void testCurveCheckNegative_P256_attackPt1()
		throws Exception {
		
		// The malicious JWE contains a public key with order 113
		String maliciousJWE = "eyJhbGciOiJFQ0RILUVTK0ExMjhLVyIsImVuYyI6IkExMjhDQkMtSFMyNTYiLCJlcGsiOnsia3R5IjoiRUMiLCJ4IjoiZ1RsaTY1ZVRRN3otQmgxNDdmZjhLM203azJVaURpRzJMcFlrV0FhRkpDYyIsInkiOiJjTEFuakthNGJ6akQ3REpWUHdhOUVQclJ6TUc3ck9OZ3NpVUQta2YzMEZzIiwiY3J2IjoiUC0yNTYifX0.qGAdxtEnrV_3zbIxU2ZKrMWcejNltjA_dtefBFnRh9A2z9cNIqYRWg.pEA5kX304PMCOmFSKX_cEg.a9fwUrx2JXi1OnWEMOmZhXd94-bEGCH9xxRwqcGuG2AMo-AwHoljdsH5C_kcTqlXS5p51OB1tvgQcMwB5rpTxg.72CHiYFecyDvuUa43KKT6w";
		JWEObject jweObject = JWEObject.parse(maliciousJWE);
		
		ECPublicKey ephemeralPublicKey = jweObject.getHeader().getEphemeralPublicKey().toECPublicKey();
		
		ECPrivateKey privateKey = generateECPrivateKey(ECKey.Curve.P_256);
		
		try {
			ECDH.ensurePointOnCurve(ephemeralPublicKey, privateKey);
			fail();
		} catch (JOSEException e) {
			assertEquals("Invalid ephemeral public key: Point not on expected curve", e.getMessage());
		}
	}
	
	
	public void testCurveCheckNegative_P256_attackPt2()
		throws Exception {
		
		// The malicious JWE contains a public key with order 2447
		String maliciousJWE = "eyJhbGciOiJFQ0RILUVTK0ExMjhLVyIsImVuYyI6IkExMjhDQkMtSFMyNTYiLCJlcGsiOnsia3R5IjoiRUMiLCJ4IjoiWE9YR1E5XzZRQ3ZCZzN1OHZDSS1VZEJ2SUNBRWNOTkJyZnFkN3RHN29RNCIsInkiOiJoUW9XTm90bk56S2x3aUNuZUprTElxRG5UTnc3SXNkQkM1M1ZVcVZqVkpjIiwiY3J2IjoiUC0yNTYifX0.UGb3hX3ePAvtFB9TCdWsNkFTv9QWxSr3MpYNiSBdW630uRXRBT3sxw.6VpU84oMob16DxOR98YTRw.y1UslvtkoWdl9HpugfP0rSAkTw1xhm_LbK1iRXzGdpYqNwIG5VU33UBpKAtKFBoA1Kk_sYtfnHYAvn-aes4FTg.UZPN8h7FcvA5MIOq-Pkj8A";
		JWEObject jweObject = JWEObject.parse(maliciousJWE);
		
		ECPublicKey ephemeralPublicKey = jweObject.getHeader().getEphemeralPublicKey().toECPublicKey();
		
		ECPrivateKey privateKey = generateECPrivateKey(ECKey.Curve.P_256);
		
		try {
			ECDH.ensurePointOnCurve(ephemeralPublicKey, privateKey);
			fail();
		} catch (JOSEException e) {
			assertEquals("Invalid ephemeral public key: Point not on expected curve", e.getMessage());
		}
	}
 * @version 2017-04-09
	public void testKeySizeForUnknownCurve() {
			new ECKey.Builder(new ECKey.Curve("unknown"), ExampleKeyP256.X, ExampleKeyP256.Y).build().size();
		} catch (UnsupportedOperationException e) {
			assertEquals("Couldn't determine field size for curve unknown", e.getMessage());
 * @version 2016-08-24
		assertFalse(matcher.matches(new ECKey.Builder(ECKey.Curve.P_256, new Base64URL("x"), new Base64URL("y")).keyID("2").build()));
		assertTrue(matcher.matches(new ECKey.Builder(ECKey.Curve.P_256, new Base64URL("x"), new Base64URL("y")).keyID("2").build()));
		assertFalse(matcher.matches(new ECKey.Builder(ECKey.Curve.P_256, new Base64URL("x"), new Base64URL("y")).keyID("2").build()));
		assertTrue(matcher.matches(new ECKey.Builder(ECKey.Curve.P_256, new Base64URL("x"), new Base64URL("y")).keyID("2").build()));
		assertFalse(matcher.matches(new ECKey.Builder(ECKey.Curve.P_256, new Base64URL("x"), new Base64URL("y")).keyID("3").keyUse(KeyUse.ENCRYPTION).build()));
		assertFalse(matcher.matches(new ECKey.Builder(ECKey.Curve.P_256, new Base64URL("x"), new Base64URL("y")).keyID("2").build()));
		assertFalse(matcher.matches(new ECKey.Builder(ECKey.Curve.P_256, new Base64URL("x"), new Base64URL("y")).keyID("2").build()));
		assertTrue(matcher.matches(new ECKey.Builder(ECKey.Curve.P_256, new Base64URL("x"), new Base64URL("y")).keyID("2").build()));
		assertFalse(matcher.matches(new ECKey.Builder(ECKey.Curve.P_256, new Base64URL("x"), new Base64URL("y")).keyID("3")
 * @version 2015-04-15

		keyList.add(new ECKey.Builder(ECKey.Curve.P_256, new Base64URL("x"), new Base64URL("y")).keyID("2").build());
		keyList.add(new ECKey.Builder(ECKey.Curve.P_256, new Base64URL("x"), new Base64URL("y")).keyID("2").build());
		keyList.add(new ECKey.Builder(ECKey.Curve.P_256, new Base64URL("x"), new Base64URL("y")).keyID("2").build());
		keyList.add(new ECKey.Builder(ECKey.Curve.P_256, new Base64URL("x"), new Base64URL("y")).keyID("2").build());
		keyList.add(new ECKey.Builder(ECKey.Curve.P_256, new Base64URL("x"), new Base64URL("y")).keyID("3").keyUse(KeyUse.ENCRYPTION).build());
		keyList.add(new ECKey.Builder(ECKey.Curve.P_256, new Base64URL("x"), new Base64URL("y")).keyID("2").build());
		keyList.add(new ECKey.Builder(ECKey.Curve.P_256, new Base64URL("x"), new Base64URL("y")).keyID("2").build());
		keyList.add(new ECKey.Builder(ECKey.Curve.P_256, new Base64URL("x"), new Base64URL("y")).keyID("3")
 * @version 2017-04-09

		ECKey ecKey = new ECKey.Builder(ECKey.Curve.P_256,
			new Base64URL("abc"),
			new Base64URL("def"))
		ecKey = (ECKey)keySet.getKeys().get(0);
		assertNotNull(ecKey);
		assertEquals(ECKey.Curve.P_256, ecKey.getCurve());
		assertEquals("abc", ecKey.getX().toString());
		assertEquals("def", ecKey.getY().toString());
		assertEquals(KeyUse.ENCRYPTION, ecKey.getKeyUse());
		assertNull(ecKey.getKeyOperations());
		assertEquals(JWEAlgorithm.ECDH_ES, ecKey.getAlgorithm());
		assertEquals("1234", ecKey.getKeyID());
		rsaKey = (RSAKey)keySet.getKeys().get(1);
		assertNotNull(rsaKey);
		assertEquals("abc", rsaKey.getModulus().toString());
		assertEquals("def", rsaKey.getPublicExponent().toString());
		assertEquals(KeyUse.SIGNATURE, rsaKey.getKeyUse());
		assertNull(rsaKey.getKeyOperations());
		assertEquals(JWSAlgorithm.RS256, rsaKey.getAlgorithm());
		assertEquals("5678", rsaKey.getKeyID());

Fixed code:
version 4.36 (2017-04-13)
    * Adds a check at ECKey construction time to ensure the public 'x' and 'y'
      coordinates are on the specified curve (iss #217).
    * Adds a new ECDSAProvider.supportedECDSAAlgorithm() method that returns
      the name of the supported ECDSA algorithm (ES256, ES384 or ES512).

 * @version 2017-04-13
	static AlgorithmMode resolveAlgorithmMode(final JWEAlgorithm alg)
	static int sharedKeyLength(final JWEAlgorithm alg, final EncryptionMethod enc)
	static SecretKey deriveSharedSecret(final ECPublicKey publicKey,
					    final ECPrivateKey privateKey,
					    final Provider provider)
	static SecretKey deriveSharedKey(final JWEHeader header,
					 final SecretKey Z,
					 final ConcatKDF concatKDF)
	
import com.nimbusds.jose.crypto.utils.ECChecks;
 * @version 2017-04-13
		if (! ECChecks.isPointOnCurve(ephemeralPublicKey, getPrivateKey())) {
			throw new JOSEException("Invalid ephemeral public EC key: Point(s) not on the expected curve");
		}
			throw new JOSEException("Invalid ECDSA signature format");
			throw new JOSEException("Invalid ECDSA signature format");
			throw new JOSEException("Invalid ECDSA signature format");
 * @version 2017-05-13
	
	
	/**
	 * Returns the supported ECDSA algorithm.
	 *
	 * @see #supportedECDSAAlgorithm()
	 *
	 * @return The supported ECDSA algorithm.
	 */
	public JWSAlgorithm supportedECDSAAlgorithm() {
		
		return supportedJWSAlgorithms().iterator().next();
	}
import com.nimbusds.jose.crypto.utils.ECChecks;
import net.jcip.annotations.ThreadSafe;
 * @version 2017-04-13
	
		
		if (! ECChecks.isPointOnCurve(
			publicKey,
			ECKey.Curve.forJWSAlgoritm(supportedECDSAAlgorithm()).toECParameterSpec())) {
			throw new JOSEException("Curve / public key parameters mismatch");
		}
/*
 * nimbus-jose-jwt
 *
 * Copyright 2012-2016, Connect2id Ltd and contributors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use
 * this file except in compliance with the License. You may obtain a copy of the
 * License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed
 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
 * CONDITIONS OF ANY KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations under the License.
 */

package com.nimbusds.jose.crypto.utils;


import java.math.BigInteger;
import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.ECPublicKey;
import java.security.spec.ECFieldFp;
import java.security.spec.ECParameterSpec;
import java.security.spec.ECPoint;
import java.security.spec.EllipticCurve;


/**
 * Elliptic curve checks.
 *
 * @author Vladimir Dzhuvinov
 * @version 2017-04-13
 */
public class ECChecks {
	
	
	/**
	 * Checks if the specified (ephemeral) public key is on the curve of
	 * the private key. Intended to prevent an "Invalid Curve Attack",
	 * independent from any JCA provider checks (the SUN provider in Java
	 * 1.8.0_51+ and BouncyCastle have them, other / older provider do
	 * not).
	 *
	 * <p>See https://www.cs.bris.ac.uk/Research/CryptographySecurity/RWC/2017/nguyen.quan.pdf
	 *
	 * @param publicKey  The public EC key. Must not be {@code null}.
	 * @param privateKey The private EC key. Must not be {@code null}.
	 *
	 * @return {@code true} if public key passed the curve check.
	 */
	public static boolean isPointOnCurve(final ECPublicKey publicKey, final ECPrivateKey privateKey) {
		
		return isPointOnCurve(publicKey, privateKey.getParams());
	}
	
	
	/**
	 * Checks if the specified (ephemeral) public key is on the given
	 * curve. Intended to prevent an "Invalid Curve Attack", independent
	 * from any JCA provider checks (the SUN provider in Java 1.8.0_51+ and
	 * BouncyCastle have them, other / older provider do not).
	 *
	 * <p>See https://www.cs.bris.ac.uk/Research/CryptographySecurity/RWC/2017/nguyen.quan.pdf
	 *
	 * @param publicKey       The public EC key. Must not be {@code null}.
	 * @param ecParameterSpec The EC spec. Must not be {@code null}.
	 *
	 * @return {@code true} if public key passed the curve check.
	 */
	public static boolean isPointOnCurve(final ECPublicKey publicKey, final ECParameterSpec ecParameterSpec) {
		
		ECPoint point = publicKey.getW();
		return isPointOnCurve(point.getAffineX(), point.getAffineY(), ecParameterSpec);
	}
	
	
	/**
	 * Checks if the specified (ephemeral) public key is on the given
	 * curve. Intended to prevent an "Invalid Curve Attack", independent
	 * from any JCA provider checks (the SUN provider in Java 1.8.0_51+ and
	 * BouncyCastle have them, other / older provider do not).
	 *
	 * <p>See https://www.cs.bris.ac.uk/Research/CryptographySecurity/RWC/2017/nguyen.quan.pdf
	 *
	 * @param x               The public EC x coordinate. Must not be
	 *                        {@code null}.
	 * @param y               The public EC y coordinate. Must not be
	 *                        {@code null}.
	 * @param ecParameterSpec The EC spec. Must not be {@code null}.
	 *
	 * @return {@code true} if public key passed the curve check.
	 */
	public static boolean isPointOnCurve(final BigInteger x, final BigInteger y, final ECParameterSpec ecParameterSpec) {
		
		// Ensure the following condition is met:
		// (y^2) mod p = (x^3 + ax + b) mod p
		EllipticCurve curve = ecParameterSpec.getCurve();
		BigInteger a = curve.getA();
		BigInteger b = curve.getB();
		BigInteger p = ((ECFieldFp) curve.getField()).getP();
		BigInteger leftSide = (y.pow(2)).mod(p);
		BigInteger rightSide = (x.pow(3).add(a.multiply(x)).add(b)).mod(p);
		
		return leftSide.equals(rightSide);
	}
	
	
	/**
	 * Prevents public instantiation.
	 */
	private ECChecks() {}
}
/*
 * nimbus-jose-jwt
 *
 * Copyright 2012-2016, Connect2id Ltd and contributors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use
 * this file except in compliance with the License. You may obtain a copy of the
 * License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed
 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
 * CONDITIONS OF ANY KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations under the License.
 */

/**
 * Cryptographic utilities.
 */
package com.nimbusds.jose.crypto.utils;
import com.nimbusds.jose.crypto.utils.ECChecks;
	
	
	/**
	 * Ensures the specified 'x' and 'y' public coordinates are on the
	 * given curve.
	 *
	 * @param crv The curve. Must not be {@code null}.
	 * @param x   The public 'x' coordinate. Must not be {@code null}.
	 * @param y   The public 'y' coordinate. Must not be {@code null}.
	 */
	private static void ensurePublicCoordinatesOnCurve(final Curve crv, final Base64URL x, final Base64URL y) {
		
		ECParameterSpec ecSpec = crv.toECParameterSpec();
		
		if (ecSpec == null) {
			throw new IllegalArgumentException("Unknown / unsupported curve: " + crv);
		}
		
		if (! ECChecks.isPointOnCurve(x.decodeToBigInteger(), y.decodeToBigInteger(), crv.toECParameterSpec())) {
			throw new IllegalArgumentException("Invalid EC JWK: The 'x' and 'y' public coordinates are not on the " + crv + " curve");
		}
	}
		
		ensurePublicCoordinatesOnCurve(crv, x, y);
		ensurePublicCoordinatesOnCurve(crv, x, y);
		
		ensurePublicCoordinatesOnCurve(crv, x, y);
		
 * @version 2017-04-13
import java.text.ParseException;
 * Submitted by Antonio Sanso. See iss #210. Modified to suit new checks for
 * iss #217.
		JWEObject jweObject1 = null;
			jweObject1 = JWEObject.parse(maliciousJWE1);
		} catch (ParseException e) {
			assertEquals("Invalid JWE header: Invalid EC JWK: The 'x' and 'y' public coordinates are not on the P-256 curve", e.getMessage());
//		ECDHDecrypter decrypter = new ECDHDecrypter(ecJWK.toECPrivateKey());
////		decrypter.getJCAContext().setKeyEncryptionProvider(BouncyCastleProviderSingleton.getInstance());
//
//		try {
//			jweObject1.decrypt(decrypter);
//			fail();
//		} catch (JOSEException e) {
//			assertEquals("Invalid ephemeral public key: Point not on expected curve", e.getMessage());
//		}
		
		JWEObject jweObject2 = null;
			jweObject2 = JWEObject.parse(maliciousJWE2);
		} catch (ParseException e) {
			assertEquals("Invalid JWE header: Invalid EC JWK: The 'x' and 'y' public coordinates are not on the P-256 curve", e.getMessage());
//		decrypter.getJCAContext().setContentEncryptionProvider(BouncyCastleProviderSingleton.getInstance());
		
//		try {
//			jweObject2.decrypt(decrypter);
//			fail();
//		} catch (JOSEException e) {
//			assertEquals("Invalid ephemeral public key: Point not on expected curve", e.getMessage());
//		}
 * @version 2017-04-13
/*
 * nimbus-jose-jwt
 *
 * Copyright 2012-2016, Connect2id Ltd and contributors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use
 * this file except in compliance with the License. You may obtain a copy of the
 * License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed
 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
 * CONDITIONS OF ANY KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations under the License.
 */

package com.nimbusds.jose.crypto.utils;


import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.ECPublicKey;
import java.security.spec.ECParameterSpec;

import com.nimbusds.jose.jwk.ECKey;
import junit.framework.TestCase;


public class ECChecksTest extends TestCase {
	
	
	private static ECPrivateKey generateECPrivateKey(final ECKey.Curve curve)
		throws Exception {
		
		final ECParameterSpec ecParameterSpec = curve.toECParameterSpec();
		
		KeyPairGenerator generator = KeyPairGenerator.getInstance("EC");
		generator.initialize(ecParameterSpec);
		KeyPair keyPair = generator.generateKeyPair();
		
		return (ECPrivateKey) keyPair.getPrivate();
	}
	
	
	private static ECPublicKey generateECPublicKey(final ECKey.Curve curve)
		throws Exception {
		
		final ECParameterSpec ecParameterSpec = curve.toECParameterSpec();
		
		KeyPairGenerator generator = KeyPairGenerator.getInstance("EC");
		generator.initialize(ecParameterSpec);
		KeyPair keyPair = generator.generateKeyPair();
		
		return (ECPublicKey) keyPair.getPublic();
	}
	
	
	public void testCurveCheckOk()
		throws Exception {
		
		ECPublicKey ephemeralPublicKey = generateECPublicKey(ECKey.Curve.P_256);
		ECPrivateKey privateKey = generateECPrivateKey(ECKey.Curve.P_256);
		assertTrue(ECChecks.isPointOnCurve(ephemeralPublicKey, privateKey));
	}
}
import java.security.spec.ECPoint;
import java.security.spec.ECPublicKeySpec;
import java.security.spec.InvalidKeySpecException;
 * @version 2017-04-13
	public void testUnknownCurve() {
			new ECKey.Builder(new ECKey.Curve("unknown"), ExampleKeyP256.X, ExampleKeyP256.Y).build();
		} catch (IllegalStateException e) {
			assertEquals("Unknown / unsupported curve: unknown", e.getMessage());
	
	
	// iss #217
	public void testEnsurePublicXYCoordinatesOnCurve()
		throws Exception {
		
		try {
			new ECKey(
				ECKey.Curve.P_256,
				ExampleKeyP384Alt.X, // on diff curve
				ExampleKeyP384Alt.Y, // on diff curve
				null,
				null,
				null,
				null,
				null,
				null,
				null,
				null,
				null);
			fail();
		} catch (IllegalArgumentException e) {
			assertEquals("Invalid EC JWK: The 'x' and 'y' public coordinates are not on the P-256 curve", e.getMessage());
		}
		
		try {
			new ECKey(
				ECKey.Curve.P_256,
				ExampleKeyP384Alt.X, // on diff curve
				ExampleKeyP384Alt.Y, // on diff curve
				ExampleKeyP256.D,    // private D coordinate
				null,
				null,
				null,
				null,
				null,
				null,
				null,
				null,
				null);
			fail();
		} catch (IllegalArgumentException e) {
			assertEquals("Invalid EC JWK: The 'x' and 'y' public coordinates are not on the P-256 curve", e.getMessage());
		}
	}
	
	
	// iss #217
	public void testCurveMismatch()
		throws Exception {
		
		// EC key on P_256
		ECParameterSpec ecParameterSpec = ECKey.Curve.P_256.toECParameterSpec();
		KeyPairGenerator generator = KeyPairGenerator.getInstance("EC");
		generator.initialize(ecParameterSpec);
		KeyPair keyPair = generator.generateKeyPair();
		ECKey ecJWK_p256 = new ECKey.Builder(ECKey.Curve.P_256, (ECPublicKey) keyPair.getPublic())
			.privateKey((ECPrivateKey) keyPair.getPrivate())
			.build();
		
		// EC key on P_384
		ecParameterSpec = ECKey.Curve.P_384.toECParameterSpec();
		generator = KeyPairGenerator.getInstance("EC");
		generator.initialize(ecParameterSpec);
		keyPair = generator.generateKeyPair();
		ECKey ecJWK_p384 = new ECKey.Builder(ECKey.Curve.P_384, (ECPublicKey) keyPair.getPublic())
			.privateKey((ECPrivateKey) keyPair.getPrivate())
			.build();
		
		
		// Try to create EC key with P_256 params, but with x and y from P_384 curve key
		
		ECPoint w = new ECPoint(ecJWK_p384.getX().decodeToBigInteger(), ecJWK_p384.getY().decodeToBigInteger());
		ECPublicKeySpec publicKeySpec = new ECPublicKeySpec(w, ECKey.Curve.P_256.toECParameterSpec());
		
		// Default Sun provider
		try {
			KeyFactory keyFactory = KeyFactory.getInstance("EC");
			keyFactory.generatePublic(publicKeySpec);
			fail();
		} catch (RuntimeException e) {
			assertEquals("Point coordinates do not match field size", e.getMessage());
		}
		
		// BouncyCastle provider
		try {
			KeyFactory keyFactory = KeyFactory.getInstance("EC", BouncyCastleProviderSingleton.getInstance());
			keyFactory.generatePublic(publicKeySpec);
			fail();
		} catch (InvalidKeySpecException e) {
			assertEquals("invalid KeySpec: x value invalid for SecP256R1FieldElement", e.getMessage());
		}
	}
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.ECPublicKey;
import java.security.spec.ECParameterSpec;
 * @version 2017-04-13
	
	
	private static final Base64URL EC_P256_X;
	
	private static final Base64URL EC_P256_Y;
	
	static {
		try {
			ECParameterSpec ecParameterSpec = ECKey.Curve.P_256.toECParameterSpec();
			
			KeyPairGenerator generator = KeyPairGenerator.getInstance("EC");
			generator.initialize(ecParameterSpec);
			KeyPair keyPair = generator.generateKeyPair();
			
			ECKey ecJWK = new ECKey.Builder(ECKey.Curve.P_256, (ECPublicKey)keyPair.getPublic()).
				privateKey((ECPrivateKey) keyPair.getPrivate()).
				build();
			
			EC_P256_X = ecJWK.getX();
			EC_P256_Y = ecJWK.getY();
			
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}
		assertFalse(matcher.matches(new ECKey.Builder(ECKey.Curve.P_256, EC_P256_X, EC_P256_Y).keyID("2").build()));
		assertTrue(matcher.matches(new ECKey.Builder(ECKey.Curve.P_256, EC_P256_X, EC_P256_Y).keyID("2").build()));
		assertFalse(matcher.matches(new ECKey.Builder(ECKey.Curve.P_256,EC_P256_X, EC_P256_Y).keyID("2").build()));
		assertTrue(matcher.matches(new ECKey.Builder(ECKey.Curve.P_256, EC_P256_X, EC_P256_Y).keyID("2").build()));
		assertFalse(matcher.matches(new ECKey.Builder(ECKey.Curve.P_256, EC_P256_X, EC_P256_Y).keyID("3").keyUse(KeyUse.ENCRYPTION).build()));
		assertFalse(matcher.matches(new ECKey.Builder(ECKey.Curve.P_256, EC_P256_X, EC_P256_Y).keyID("2").build()));
		assertFalse(matcher.matches(new ECKey.Builder(ECKey.Curve.P_256, EC_P256_X, EC_P256_Y).keyID("2").build()));
		assertTrue(matcher.matches(new ECKey.Builder(ECKey.Curve.P_256, EC_P256_X, EC_P256_Y).keyID("2").build()));
		assertFalse(matcher.matches(new ECKey.Builder(ECKey.Curve.P_256, EC_P256_X, EC_P256_Y).keyID("3")
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.ECPublicKey;
import java.security.spec.ECParameterSpec;
 * @version 2017-04-13
	
	private static final Base64URL EC_P256_X;
	
	private static final Base64URL EC_P256_Y;
	
	static {
		try {
			ECParameterSpec ecParameterSpec = ECKey.Curve.P_256.toECParameterSpec();
			
			KeyPairGenerator generator = KeyPairGenerator.getInstance("EC");
			generator.initialize(ecParameterSpec);
			KeyPair keyPair = generator.generateKeyPair();
			
			ECKey ecJWK = new ECKey.Builder(ECKey.Curve.P_256, (ECPublicKey)keyPair.getPublic()).
				privateKey((ECPrivateKey) keyPair.getPrivate()).
				build();
			
			EC_P256_X = ecJWK.getX();
			EC_P256_Y = ecJWK.getY();
			
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}
		keyList.add(new ECKey.Builder(ECKey.Curve.P_256, EC_P256_X, EC_P256_Y).keyID("2").build());
		keyList.add(new ECKey.Builder(ECKey.Curve.P_256, EC_P256_X, EC_P256_Y).keyID("2").build());
		keyList.add(new ECKey.Builder(ECKey.Curve.P_256, EC_P256_X, EC_P256_Y).keyID("2").build());
		keyList.add(new ECKey.Builder(ECKey.Curve.P_256, EC_P256_X, EC_P256_Y).keyID("2").build());
		keyList.add(new ECKey.Builder(ECKey.Curve.P_256, EC_P256_X, EC_P256_Y).keyID("3").keyUse(KeyUse.ENCRYPTION).build());
		keyList.add(new ECKey.Builder(ECKey.Curve.P_256, EC_P256_X, EC_P256_Y).keyID("2").build());
		keyList.add(new ECKey.Builder(ECKey.Curve.P_256, EC_P256_X, EC_P256_Y).keyID("2").build());
		keyList.add(new ECKey.Builder(ECKey.Curve.P_256, EC_P256_X, EC_P256_Y).keyID("3")
import java.security.spec.ECParameterSpec;
 * @version 2017-04-13
		
		ECParameterSpec ecParameterSpec = ECKey.Curve.P_256.toECParameterSpec();
		
		KeyPairGenerator generator = KeyPairGenerator.getInstance("EC");
		generator.initialize(ecParameterSpec);
		KeyPair keyPair = generator.generateKeyPair();
		
		ECKey ecKey = new ECKey.Builder(ECKey.Curve.P_256, (ECPublicKey)keyPair.getPublic())
			.privateKey((ECPrivateKey) keyPair.getPrivate())
		ECKey ecKeyOut = (ECKey)keySet.getKeys().get(0);
		assertNotNull(ecKeyOut);
		assertEquals(ECKey.Curve.P_256, ecKeyOut.getCurve());
		assertEquals(ecKey.getX(), ecKeyOut.getX());
		assertEquals(ecKey.getY(), ecKeyOut.getY());
		assertEquals(KeyUse.ENCRYPTION, ecKeyOut.getKeyUse());
		assertNull(ecKeyOut.getKeyOperations());
		assertEquals(JWEAlgorithm.ECDH_ES, ecKeyOut.getAlgorithm());
		assertEquals("1234", ecKeyOut.getKeyID());
		RSAKey rsaKeyOut = (RSAKey)keySet.getKeys().get(1);
		assertNotNull(rsaKeyOut);
		assertEquals("abc", rsaKeyOut.getModulus().toString());
		assertEquals("def", rsaKeyOut.getPublicExponent().toString());
		assertEquals(KeyUse.SIGNATURE, rsaKeyOut.getKeyUse());
		assertNull(rsaKeyOut.getKeyOperations());
		assertEquals(JWSAlgorithm.RS256, rsaKeyOut.getAlgorithm());
		assertEquals("5678", rsaKeyOut.getKeyID());