package com.nimbusds.jose;
import java.text.ParseException;
import net.minidev.json.JSONObject;
import net.jcip.annotations.Immutable;
import com.nimbusds.jose.util.Base64URL;
import com.nimbusds.jose.util.JSONObjectUtils;
/**
* Public {@link AlgorithmFamily#EC Elliptic Curve} JSON Web Key (JWK). This
* class is immutable.
*
* <p>Example JSON:
*
* <pre>
* {
* "alg" : "EC",
* "crv" : "P-256",
* "x" : "MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4",
* "y" : "4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM",
* "use" : "enc",
* "kid" : "1"
* }
* </pre>
*
* <p>See http://en.wikipedia.org/wiki/Elliptic_curve_cryptography
*
* @author Vladimir Dzhuvinov
* @version $version$ (2012-10-23)
*/
@Immutable
public final class ECKey extends JWK {
/**
* Cryptographic curve. This class is immutable.
*
* <p>Includes constants for the following standard cryptographic
* curves:
*
* <ul>
* <li>{@link #P_256}
* <li>{@link #P_384}
* <li>{@link #P_521}
* </ul>
*
* <p>See "Digital Signature Standard (DSS)", FIPS PUB 186-3, June 2009,
* National Institute of Standards and Technology (NIST).
*/
@Immutable
public static class Curve {
/**
* P-256 curve.
*/
public static final Curve P_256 = new Curve("P-256");
/**
* P-384 curve.
*/
public static final Curve P_384 = new Curve("P-384");
/**
* P-521 curve.
*/
public static final Curve P_521 = new Curve("P-521");
/**
* The curve name.
*/
private final String name;
/**
* Creates a new cryptographic curve with the specified name.
*
* @param name The name of the cryptographic curve. Must not be
* {@code null}.
*/
public Curve(final String name) {
if (name == null)
throw new IllegalArgumentException("The cryptographic curve name must not be null");
this.name = name;
}
/**
* Gets the name of this cryptographic curve.
*
* @return The name.
*/
public String getName() {
return name;
}
/**
* @see #getName
*/
@Override
public String toString() {
return getName();
}
/**
* Overrides {@code Object.equals()}.
*
* @param object The object to compare to.
*
* @return {@code true} if the objects have the same value,
* otherwise {@code false}.
*/
@Override
public boolean equals(final Object object) {
return object instanceof Curve && this.toString().equals(object.toString());
}
/**
* Parses a cryptographic curve from the specified string.
*
* @param s The string to parse. Must not be {@code null}.
*
* @return The cryptographic curve.
*
* @throws ParseException If the string couldn't be parsed.
*/
public static Curve parse(final String s)
throws ParseException {
if (s == null)
throw new IllegalArgumentException("The cryptographic curve sting must not be null");
if (s == P_256.getName())
return P_256;
else if (s == P_384.getName())
return P_384;
else if (s == P_521.getName())
return P_521;
else
return new Curve(s);
}
}
/**
* The curve name.
*/
private final Curve crv;
/**
* The 'x' EC coordinate.
*/
private final Base64URL x;
/**
* The 'y' EC coordinate.
*/
private final Base64URL y;
/**
* Creates a new public Elliptic Curve JSON Web Key (JWK) with the
* specified parameters.
*
* @param crv The cryptographic curve. Must not be {@code null}.
* @param x The 'x' coordinate for the elliptic curve point. It is
* represented as the Base64URL encoding of the coordinate's
* big endian representation. Must not be {@code null}.
* @param y The 'y' coordinate for the elliptic curve point. It is
* represented as the Base64URL encoding of the coordinate's
* big endian representation. Must not be {@code null}.
* @param use The key use, {@code null} if not specified.
* @param kid The key ID, {@code null} if not specified.
*/
public ECKey(final Curve crv, final Base64URL x, final Base64URL y,
final Use use, final String kid) {
super(AlgorithmFamily.EC, use, kid);
if (crv == null)
throw new IllegalArgumentException("The curve must not be null");
this.crv = crv;
if (x == null)
throw new IllegalArgumentException("The x coordinate must not be null");
this.x = x;
if (y == null)
throw new IllegalArgumentException("The y coordinate must not be null");
this.y = y;
}
/**
* Gets the cryptographic curve.
*
* @return The cryptograhic curve.
*/
public Curve getCurve() {
return crv;
}
/**
* Gets the 'x' coordinate for the elliptic curve point. It is
* represented as the Base64URL encoding of the coordinate's big endian
* representation.
*
* @return The 'x' coordinate.
*/
public Base64URL getX() {
return x;
}
/**
* Gets the 'y' coordinate for the elliptic curve point. It is
* represented as the Base64URL encoding of the coordinate's big endian
* representation.
*
* @return The 'y' coordinate.
*/
public Base64URL getY() {
return y;
}
@Override
public JSONObject toJSONObject() {
JSONObject o = super.toJSONObject();
// Append EC specific attributes
o.put("crv", crv.toString());
o.put("x", x.toString());
o.put("y", y.toString());
return o;
}
/**
* Parses an Elliptic Curve JWK from the specified JSON object
* representation.
*
* @param jsonObject The JSON object to parse. Must not be {@code null}.
*
* @return The Elliptic Curve JWK.
*
* @throws ParseException If the JSON object couldn't be parsed to a
* valid Elliptic Curve JWK.
*/
public static ECKey parse(final JSONObject jsonObject)
throws ParseException {
// Parse the mandatory parameters first
AlgorithmFamily af = AlgorithmFamily.parse(JSONObjectUtils.getString(jsonObject, "alg"));
Curve crv = Curve.parse(JSONObjectUtils.getString(jsonObject, "crv"));
Base64URL x = new Base64URL(JSONObjectUtils.getString(jsonObject, "x"));
Base64URL y = new Base64URL(JSONObjectUtils.getString(jsonObject, "y"));
// Get optional key use
Use use = JWK.parseKeyUse(jsonObject);
// Get optional key ID
String id = JWK.parseKeyID(jsonObject);
// Check alg family
if (af != AlgorithmFamily.EC)
throw new ParseException("The algorithm family \"alg\" must be EC", 0);
return new ECKey(crv, x, y, use, id);
}
}