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-15)
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 != 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                            if (s == P_256.getName())
152                                    return P_256;
153                            
154                            else if (s == P_384.getName())
155                                    return P_384;
156                            
157                            else if (s == P_521.getName())
158                                    return P_521;
159                            
160                            else
161                                    return new Curve(s);
162                    }
163            }
164            
165            
166            /**
167             * The curve name.
168             */
169            private final Curve crv;
170            
171            
172            /**
173             * The 'x' EC coordinate.
174             */
175            private final Base64URL x;
176            
177            
178            /**
179             * The 'y' EC coordinate.
180             */
181            private final Base64URL y;
182             
183            
184            /**
185             * Creates a new public Elliptic Curve JSON Web Key (JWK) with the 
186             * specified parameters.
187             *
188             * @param crv The cryptographic curve. Must not be {@code null}.
189             * @param x   The 'x' coordinate for the elliptic curve point. It is 
190             *            represented as the Base64URL encoding of the coordinate's 
191             *            big endian representation. Must not be {@code null}.
192             * @param y   The 'y' coordinate for the elliptic curve point. It is 
193             *            represented as the Base64URL encoding of the coordinate's 
194             *            big endian representation. Must not be {@code null}.
195             * @param use The key use, {@code null} if not specified.
196             * @param alg The intended JOSE algorithm for the key, {@code null} if
197             *            not specified.
198             * @param kid The key ID, {@code null} if not specified.
199             */
200            public ECKey(final Curve crv, final Base64URL x, final Base64URL y, 
201                         final Use use, final Algorithm alg, final String kid) {
202            
203                    super(KeyType.EC, use, alg, kid);
204                    
205                    if (crv == null)
206                            throw new IllegalArgumentException("The curve must not be null");
207                            
208                    this.crv = crv;
209                    
210                    if (x == null)
211                            throw new IllegalArgumentException("The x coordinate must not be null");
212                    
213                    this.x = x;
214                    
215                    if (y == null)
216                            throw new IllegalArgumentException("The y coordinate must not be null");
217                    
218                    this.y = y;
219            }
220            
221            
222            /**
223             * Gets the cryptographic curve.
224             *
225             * @return The cryptographic curve.
226             */
227            public Curve getCurve() {
228            
229                    return crv;
230            }
231            
232            
233            /**
234             * Gets the 'x' coordinate for the elliptic curve point. It is 
235             * represented as the Base64URL encoding of the coordinate's big endian 
236             * representation.
237             *
238             * @return The 'x' coordinate.
239             */
240            public Base64URL getX() {
241            
242                    return x;
243            }
244            
245            
246            /**
247             * Gets the 'y' coordinate for the elliptic curve point. It is 
248             * represented as the Base64URL encoding of the coordinate's big endian 
249             * representation.
250             *
251             * @return The 'y' coordinate.
252             */
253            public Base64URL getY() {
254            
255                    return y;
256            }
257            
258            
259            @Override
260            public JSONObject toJSONObject() {
261            
262                    JSONObject o = super.toJSONObject();
263                    
264                    // Append EC specific attributes
265                    o.put("crv", crv.toString());
266                    o.put("x", x.toString());
267                    o.put("y", y.toString());
268            
269                    return o;
270            }
271            
272            
273            /**
274             * Parses an Elliptic Curve JWK from the specified JSON object 
275             * representation.
276             *
277             * @param jsonObject The JSON object to parse. Must not be 
278             *                   {@code null}.
279             *
280             * @return The Elliptic Curve JWK.
281             *
282             * @throws ParseException If the JSON object couldn't be parsed to a 
283             *                        valid Elliptic Curve JWK.
284             */
285            public static ECKey parse(final JSONObject jsonObject)
286                    throws ParseException {
287                    
288                    // Parse the mandatory parameters first
289                    KeyType kty = KeyType.parse(JSONObjectUtils.getString(jsonObject, "kty"));
290                    Curve crv = Curve.parse(JSONObjectUtils.getString(jsonObject, "crv"));
291                    Base64URL x = new Base64URL(JSONObjectUtils.getString(jsonObject, "x"));
292                    Base64URL y = new Base64URL(JSONObjectUtils.getString(jsonObject, "y"));
293                    
294                    // Get optional key use
295                    Use use = JWK.parseKeyUse(jsonObject);
296    
297                    // Get optional intended algorithm
298                    Algorithm alg = JWK.parseAlgorithm(jsonObject);
299    
300                    // Get optional key ID
301                    String id = JWK.parseKeyID(jsonObject);
302                    
303                    // Check key type
304                    if (kty != KeyType.EC)
305                            throw new ParseException("The key type \"kty\" must be EC", 0);
306    
307                    return new ECKey(crv, x, y, use, alg, id);
308            }
309    }