001 package com.nimbusds.jose;
002
003
004 import java.text.ParseException;
005
006 import net.minidev.json.JSONObject;
007
008 import net.jcip.annotations.Immutable;
009
010 import com.nimbusds.jose.util.Base64URL;
011 import com.nimbusds.jose.util.JSONObjectUtils;
012
013
014 /**
015 * Public {@link KeyType#EC Elliptic Curve} JSON Web Key (JWK). This class is
016 * immutable.
017 *
018 * <p>Example JSON:
019 *
020 * <pre>
021 * {
022 * "kty" : "EC",
023 * "crv" : "P-256",
024 * "x" : "MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4",
025 * "y" : "4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM",
026 * "use" : "enc",
027 * "kid" : "1"
028 * }
029 * </pre>
030 *
031 * <p>See http://en.wikipedia.org/wiki/Elliptic_curve_cryptography
032 *
033 * @author Vladimir Dzhuvinov
034 * @version $version$ (2013-01-08)
035 */
036 @Immutable
037 public final class ECKey extends JWK {
038
039
040 /**
041 * Cryptographic curve. This class is immutable.
042 *
043 * <p>Includes constants for the following standard cryptographic
044 * curves:
045 *
046 * <ul>
047 * <li>{@link #P_256}
048 * <li>{@link #P_384}
049 * <li>{@link #P_521}
050 * </ul>
051 *
052 * <p>See "Digital Signature Standard (DSS)", FIPS PUB 186-3, June 2009,
053 * National Institute of Standards and Technology (NIST).
054 */
055 @Immutable
056 public static class Curve {
057
058
059 /**
060 * P-256 curve.
061 */
062 public static final Curve P_256 = new Curve("P-256");
063
064
065 /**
066 * P-384 curve.
067 */
068 public static final Curve P_384 = new Curve("P-384");
069
070
071 /**
072 * P-521 curve.
073 */
074 public static final Curve P_521 = new Curve("P-521");
075
076
077 /**
078 * The curve name.
079 */
080 private final String name;
081
082
083 /**
084 * Creates a new cryptographic curve with the specified name.
085 *
086 * @param name The name of the cryptographic curve. Must not be
087 * {@code null}.
088 */
089 public Curve(final String name) {
090
091 if (name == null)
092 throw new IllegalArgumentException("The cryptographic curve name must not be null");
093
094 this.name = name;
095 }
096
097
098 /**
099 * Gets the name of this cryptographic curve.
100 *
101 * @return The name.
102 */
103 public String getName() {
104
105 return name;
106 }
107
108
109 /**
110 * @see #getName
111 */
112 @Override
113 public String toString() {
114
115 return getName();
116 }
117
118
119 /**
120 * Overrides {@code Object.equals()}.
121 *
122 * @param object The object to compare to.
123 *
124 * @return {@code true} if the objects have the same value,
125 * otherwise {@code false}.
126 */
127 @Override
128 public boolean equals(final Object object) {
129
130 return object instanceof Curve && this.toString().equals(object.toString());
131 }
132
133
134 /**
135 * Parses a cryptographic curve from the specified string.
136 *
137 * @param s The string to parse. Must not be {@code null}.
138 *
139 * @return The cryptographic curve.
140 *
141 * @throws ParseException If the string couldn't be parsed.
142 */
143 public static Curve parse(final String s)
144 throws ParseException {
145
146 if (s == null)
147 throw new IllegalArgumentException("The cryptographic curve sting must not be null");
148
149 if (s == P_256.getName())
150 return P_256;
151
152 else if (s == P_384.getName())
153 return P_384;
154
155 else if (s == P_521.getName())
156 return P_521;
157
158 else
159 return new Curve(s);
160 }
161 }
162
163
164 /**
165 * The curve name.
166 */
167 private final Curve crv;
168
169
170 /**
171 * The 'x' EC coordinate.
172 */
173 private final Base64URL x;
174
175
176 /**
177 * The 'y' EC coordinate.
178 */
179 private final Base64URL y;
180
181
182 /**
183 * Creates a new public Elliptic Curve JSON Web Key (JWK) with the
184 * specified parameters.
185 *
186 * @param crv The cryptographic curve. Must not be {@code null}.
187 * @param x The 'x' coordinate for the elliptic curve point. It is
188 * represented as the Base64URL encoding of the coordinate's
189 * big endian representation. Must not be {@code null}.
190 * @param y The 'y' coordinate for the elliptic curve point. It is
191 * represented as the Base64URL encoding of the coordinate's
192 * big endian representation. Must not be {@code null}.
193 * @param use The key use, {@code null} if not specified.
194 * @param alg The intended JOSE algorithm for the key, {@code null} if
195 * not specified.
196 * @param kid The key ID, {@code null} if not specified.
197 */
198 public ECKey(final Curve crv, final Base64URL x, final Base64URL y,
199 final Use use, final Algorithm alg, final String kid) {
200
201 super(KeyType.EC, use, alg, kid);
202
203 if (crv == null)
204 throw new IllegalArgumentException("The curve must not be null");
205
206 this.crv = crv;
207
208 if (x == null)
209 throw new IllegalArgumentException("The x coordinate must not be null");
210
211 this.x = x;
212
213 if (y == null)
214 throw new IllegalArgumentException("The y coordinate must not be null");
215
216 this.y = y;
217 }
218
219
220 /**
221 * Gets the cryptographic curve.
222 *
223 * @return The cryptographic curve.
224 */
225 public Curve getCurve() {
226
227 return crv;
228 }
229
230
231 /**
232 * Gets the 'x' coordinate for the elliptic curve point. It is
233 * represented as the Base64URL encoding of the coordinate's big endian
234 * representation.
235 *
236 * @return The 'x' coordinate.
237 */
238 public Base64URL getX() {
239
240 return x;
241 }
242
243
244 /**
245 * Gets the 'y' coordinate for the elliptic curve point. It is
246 * represented as the Base64URL encoding of the coordinate's big endian
247 * representation.
248 *
249 * @return The 'y' coordinate.
250 */
251 public Base64URL getY() {
252
253 return y;
254 }
255
256
257 @Override
258 public JSONObject toJSONObject() {
259
260 JSONObject o = super.toJSONObject();
261
262 // Append EC specific attributes
263 o.put("crv", crv.toString());
264 o.put("x", x.toString());
265 o.put("y", y.toString());
266
267 return o;
268 }
269
270
271 /**
272 * Parses an Elliptic Curve JWK from the specified JSON object
273 * representation.
274 *
275 * @param jsonObject The JSON object to parse. Must not be
276 * {@code null}.
277 *
278 * @return The Elliptic Curve JWK.
279 *
280 * @throws ParseException If the JSON object couldn't be parsed to a
281 * valid Elliptic Curve JWK.
282 */
283 public static ECKey parse(final JSONObject jsonObject)
284 throws ParseException {
285
286 // Parse the mandatory parameters first
287 KeyType kty = KeyType.parse(JSONObjectUtils.getString(jsonObject, "kty"));
288 Curve crv = Curve.parse(JSONObjectUtils.getString(jsonObject, "crv"));
289 Base64URL x = new Base64URL(JSONObjectUtils.getString(jsonObject, "x"));
290 Base64URL y = new Base64URL(JSONObjectUtils.getString(jsonObject, "y"));
291
292 // Get optional key use
293 Use use = JWK.parseKeyUse(jsonObject);
294
295 // Get optional intended algorithm
296 Algorithm alg = JWK.parseAlgorithm(jsonObject);
297
298 // Get optional key ID
299 String id = JWK.parseKeyID(jsonObject);
300
301 // Check key type
302 if (kty != KeyType.EC)
303 throw new ParseException("The key type \"kty\" must be EC", 0);
304
305 return new ECKey(crv, x, y, use, alg, id);
306 }
307 }