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