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    }