001package com.nimbusds.jose.jwk;
002
003
004import java.net.URL;
005import java.util.List;
006import java.security.KeyFactory;
007import java.security.KeyPair;
008import java.security.NoSuchAlgorithmException;
009import java.security.interfaces.ECPrivateKey;
010import java.security.interfaces.ECPublicKey;
011import java.security.spec.ECParameterSpec;
012import java.security.spec.ECPoint;
013import java.security.spec.ECPrivateKeySpec;
014import java.security.spec.ECPublicKeySpec;
015import java.security.spec.InvalidKeySpecException;
016import java.text.ParseException;
017
018import net.jcip.annotations.Immutable;
019
020import net.minidev.json.JSONObject;
021
022import org.bouncycastle.jce.ECNamedCurveTable;
023import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec;
024import org.bouncycastle.jce.spec.ECNamedCurveSpec;
025
026import com.nimbusds.jose.Algorithm;
027import com.nimbusds.jose.crypto.BouncyCastleProviderSingleton;
028import com.nimbusds.jose.util.Base64;
029import com.nimbusds.jose.util.Base64URL;
030import com.nimbusds.jose.util.JSONObjectUtils;
031import com.nimbusds.jose.util.X509CertChainUtils;
032
033
034/**
035 * Public and private {@link KeyType#EC Elliptic Curve} JSON Web Key (JWK). 
036 * Uses the BouncyCastle.org provider for EC key import and export. This class
037 * is immutable.
038 *
039 * <p>Example JSON object representation of a public EC JWK:
040 * 
041 * <pre>
042 * {
043 *   "kty" : "EC",
044 *   "crv" : "P-256",
045 *   "x"   : "MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4",
046 *   "y"   : "4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM",
047 *   "use" : "enc",
048 *   "kid" : "1"
049 * }
050 * </pre>
051 *
052 * <p>Example JSON object representation of a public and private EC JWK:
053 *
054 * <pre>
055 * {
056 *   "kty" : "EC",
057 *   "crv" : "P-256",
058 *   "x"   : "MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4",
059 *   "y"   : "4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM",
060 *   "d"   : "870MB6gfuTJ4HtUnUvYMyJpr5eUZNP4Bk43bVdj3eAE",
061 *   "use" : "enc",
062 *   "kid" : "1"
063 * }
064 * </pre>
065 *
066 * <p>See http://en.wikipedia.org/wiki/Elliptic_curve_cryptography
067 *
068 * @author Vladimir Dzhuvinov
069 * @author Justin Richer
070 * @version $version$ (2013-10-16)
071 */
072@Immutable
073public final class ECKey extends JWK {
074
075
076        /**
077         * Cryptographic curve. This class is immutable.
078         *
079         * <p>Includes constants for the following standard cryptographic 
080         * curves:
081         *
082         * <ul>
083         *     <li>{@link #P_256}
084         *     <li>{@link #P_384}
085         *     <li>{@link #P_521}
086         * </ul>
087         *
088         * <p>See "Digital Signature Standard (DSS)", FIPS PUB 186-3, June 
089         * 2009, National Institute of Standards and Technology (NIST).
090         */
091        @Immutable
092        public static class Curve {
093
094
095                /**
096                 * P-256 curve (secp256r1).
097                 */
098                public static final Curve P_256 = new Curve("P-256", "secp256r1");
099
100
101                /**
102                 * P-384 curve (secp384r1).
103                 */
104                public static final Curve P_384 = new Curve("P-384", "secp384r1");
105
106
107                /**
108                 * P-521 curve (secp521r1).
109                 */
110                public static final Curve P_521 = new Curve("P-521", "secp521r1");
111
112
113                /**
114                 * The JOSE curve name.
115                 */
116                private final String name;
117
118
119                /**
120                 * The standard (JCA) curve name, {@code null} if not 
121                 * specified.
122                 */
123                private final String stdName;
124
125
126                /**
127                 * Creates a new cryptographic curve with the specified name.
128                 * The standard (JCA) curve name is not unspecified.
129                 *
130                 * @param name The name of the cryptographic curve. Must not be
131                 *             {@code null}.
132                 */
133                public Curve(final String name) {
134
135                        this(name, null);
136                }
137
138
139                /**
140                 * Creates a new cryptographic curve with the specified name.
141                 *
142                 * @param name    The JOSE name of the cryptographic curve. 
143                 *                Must not be {@code null}.
144                 * @param stdName The standard (JCA) name of the cryptographic
145                 *                curve, {@code null} if not specified.
146                 */
147                public Curve(final String name, final String stdName) {
148
149                        if (name == null) {
150                                throw new IllegalArgumentException("The cryptographic curve name must not be null");
151                        }
152
153                        this.name = name;
154
155                        this.stdName = stdName;
156                }
157
158
159                /**
160                 * Gets the name of this cryptographic curve.
161                 *
162                 * @return The name.
163                 */
164                public String getName() {
165
166                        return name;
167                }
168
169
170                /**
171                 * Gets the standard (JCA) name of this cryptographic curve.
172                 *
173                 * @return The standard (JCA) name.
174                 */
175                public String getStdName() {
176
177                        return stdName;
178                }
179
180
181                /**
182                 * Gets the Elliptic Curve parameter specification for this
183                 * cryptographic curve.
184                 *
185                 * @return The EC parameter specification, {@code null} if this
186                 *         cryptographic curve has no standard (JCA) name 
187                 *         specified or if lookup of the EC parameters failed.
188                 */
189                public ECParameterSpec toECParameterSpec() {
190
191                        if (stdName == null) {
192                                return null;
193                        }
194
195                        ECNamedCurveParameterSpec curveParams = 
196                                ECNamedCurveTable.getParameterSpec(stdName);
197
198                        if (curveParams == null) {
199                                return null;
200                        }
201
202                        return new ECNamedCurveSpec(curveParams.getName(),
203                                                    curveParams.getCurve(),
204                                                    curveParams.getG(),
205                                                    curveParams.getN());
206                }
207
208
209                /**
210                 * @see #getName
211                 */
212                @Override
213                public String toString() {
214
215                        return getName();
216                }
217
218
219                /**
220                 * Overrides {@code Object.equals()}.
221                 *
222                 * @param object The object to compare to.
223                 *
224                 * @return {@code true} if the objects have the same value,
225                 *         otherwise {@code false}.
226                 */
227                @Override
228                public boolean equals(final Object object) {
229
230                        return object != null && 
231                               object instanceof Curve && 
232                               this.toString().equals(object.toString());
233                }
234
235
236                /**
237                 * Parses a cryptographic curve from the specified string.
238                 *
239                 * @param s The string to parse. Must not be {@code null}.
240                 *
241                 * @return The cryptographic curve.
242                 *
243                 * @throws ParseException If the string couldn't be parsed.
244                 */
245                public static Curve parse(final String s) 
246                        throws ParseException {
247
248                        if (s == null) {
249                                throw new IllegalArgumentException("The cryptographic curve string must not be null");
250                        }
251
252                        if (s.equals(P_256.getName())) {
253                                return P_256;
254
255                        } else if (s.equals(P_384.getName())) {
256                                return P_384;
257
258                        } else if (s.equals(P_521.getName())) {
259                                return P_521;
260
261                        } else {
262                                return new Curve(s);
263                        }
264                }
265
266
267                /**
268                 * Gets the cryptographic curve for the specified standard 
269                 * (JCA) name.
270                 *
271                 * @param stdName The standard (JCA) name. Must not be 
272                 *                {@code null}.
273                 *
274                 * @throws IllegalArgumentException If no matching JOSE curve 
275                 *                                  constant could be found.
276                 */
277                public static Curve forStdName(final String stdName) {
278
279                        if (stdName.equals("secp256r1")) {
280                                return P_256;
281
282                        } else if (stdName.equals("secp384r1")) {
283                                return P_384;
284
285                        } else if (stdName.equals("secp521r1")) {
286                                return P_521;
287
288                        } else {
289                                throw new IllegalArgumentException("No matching curve constant for standard (JCA) name " + stdName);
290                        }
291                }
292        }
293
294
295        /**
296         * Implements a builder pattern for constructing Elliptic Curve JWKs.
297         *
298         * <p>Example use:
299         *
300         * <pre>
301         * ECKey key = new ECKey.Builder(Curve.P521, x, y).
302         *             setD(d).
303         *             setAlgorithm(JWSAlgorithm.ES512).
304         *             setKeyID("789").
305         *             build();
306         * </pre>
307         */
308        public static class Builder {
309
310
311                /**
312                 * The curve name.
313                 */
314                private final Curve crv;
315
316
317                /**
318                 * The public 'x' EC coordinate.
319                 */
320                private final Base64URL x;
321
322
323                /**
324                 * The public 'y' EC coordinate.
325                 */
326                private final Base64URL y;
327                
328
329                /**
330                 * The private 'd' EC coordinate, optional.
331                 */
332                private Base64URL d;
333
334
335                /**
336                 * The key use, optional.
337                 */
338                private Use use;
339
340
341                /**
342                 * The intended JOSE algorithm for the key, optional.
343                 */
344                private Algorithm alg;
345
346
347                /**
348                 * The key ID, optional.
349                 */
350                private String kid;
351
352
353                /**
354                 * X.509 certificate URL, optional.
355                 */
356                private URL x5u;
357
358
359                /**
360                 * X.509 certificate thumbprint, optional.
361                 */
362                private Base64URL x5t;
363
364
365                /**
366                 * The X.509 certificate chain, optional.
367                 */
368                private List<Base64> x5c;
369
370
371                /**
372                 * Creates a new Elliptic Curve JWK builder.
373                 *
374                 * @param crv The cryptographic curve. Must not be 
375                 *            {@code null}.
376                 * @param x   The public 'x' coordinate for the elliptic curve 
377                 *            point. It is represented as the Base64URL 
378                 *            encoding of the coordinate's big endian 
379                 *            representation. Must not be {@code null}.
380                 * @param y   The public 'y' coordinate for the elliptic curve 
381                 *            point. It is represented as the Base64URL 
382                 *            encoding of the coordinate's big endian 
383                 *            representation. Must not be {@code null}.
384                 */
385                public Builder(final Curve crv, final Base64URL x, final Base64URL y) {
386
387                        if (crv == null) {
388                                throw new IllegalArgumentException("The curve must not be null");
389                        }
390
391                        this.crv = crv;
392
393                        if (x == null) {
394                                throw new IllegalArgumentException("The 'x' coordinate must not be null");
395                        }
396
397                        this.x = x;
398
399                        if (y == null) {
400                                throw new IllegalArgumentException("The 'y' coordinate must not be null");
401                        }
402
403                        this.y = y;
404                }
405
406
407                /**
408                 * Creates a new Elliptic Curve JWK builder.
409                 *
410                 * @param crv The cryptographic curve. Must not be 
411                 *            {@code null}.
412                 * @param pub The public EC key to represent. Must not be 
413                 *            {@code null}.
414                 */
415                public Builder(final Curve crv, final ECPublicKey pub) {
416
417                        this(crv, 
418                             Base64URL.encode(pub.getW().getAffineX()), 
419                             Base64URL.encode(pub.getW().getAffineY()));
420                }
421
422
423                /**
424                 * Sets the private 'd' coordinate for the elliptic curve 
425                 * point. The alternative method is {@link #setPrivateKey}.
426                 *
427                 * @param d The 'd' coordinate. It is represented as the 
428                 *          Base64URL encoding of the coordinate's big endian 
429                 *          representation. {@code null} if not specified (for
430                 *          a public key).
431                 *
432                 * @return This builder.
433                 */
434                public Builder setD(final Base64URL d) {
435
436                        this.d = d;
437                        return this;
438                }
439
440
441                /**
442                 * Sets the private Elliptic Curve key. The alternative method 
443                 * is {@link #setD}.
444                 *
445                 * @param priv The private EC key, used to obtain the private
446                 *             'd' coordinate for the elliptic curve point.
447                 *             {@code null} if not specified (for a public 
448                 *             key).
449                 *
450                 * @return This builder.
451                 */
452                public Builder setPrivateKey(final ECPrivateKey priv) {
453
454                        if (priv != null) {
455                                this.d = Base64URL.encode(priv.getS()); 
456                        }
457                        
458                        return this;
459                }
460
461
462                /**
463                 * Sets the use ({@code use}) of the JWK.
464                 *
465                 * @param use The key use, {@code null} if not specified or if 
466                 *            the key is intended for signing as well as 
467                 *            encryption.
468                 *
469                 * @return This builder.
470                 */
471                public Builder setKeyUse(final Use use) {
472
473                        this.use = use;
474                        return this;
475                }
476
477
478                /**
479                 * Sets the intended JOSE algorithm ({@code alg}) for the JWK.
480                 *
481                 * @param alg The intended JOSE algorithm, {@code null} if not 
482                 *            specified.
483                 *
484                 * @return This builder.
485                 */
486                public Builder setAlgorithm(final Algorithm alg) {
487
488                        this.alg = alg;
489                        return this;
490                }
491
492                /**
493                 * Sets the ID ({@code kid}) of the JWK. The key ID can be used 
494                 * to match a specific key. This can be used, for instance, to 
495                 * choose a key within a {@link JWKSet} during key rollover. 
496                 * The key ID may also correspond to a JWS/JWE {@code kid} 
497                 * header parameter value.
498                 *
499                 * @param kid The key ID, {@code null} if not specified.
500                 *
501                 * @return This builder.
502                 */
503                public Builder setKeyID(final String kid) {
504
505                        this.kid = kid;
506                        return this;
507                }
508
509
510                /**
511                 * Sets the X.509 certificate URL ({@code x5u}) of the JWK.
512                 *
513                 * @param x5u The X.509 certificate URL, {@code null} if not 
514                 *            specified.
515                 *
516                 * @return This builder.
517                 */
518                public Builder setX509CertURL(final URL x5u) {
519
520                        this.x5u = x5u;
521                        return this;
522                }
523
524
525                /**
526                 * Sets the X.509 certificate thumbprint ({@code x5t}) of the
527                 * JWK.
528                 *
529                 * @param x5t The X.509 certificate thumbprint, {@code null} if 
530                 *            not specified.
531                 *
532                 * @return This builder.
533                 */
534                public Builder setX509CertThumbprint(final Base64URL x5t) {
535
536                        this.x5t = x5t;
537                        return this;
538                }
539
540                /**
541                 * Sets the X.509 certificate chain ({@code x5c}) of the JWK.
542                 *
543                 * @param x5c The X.509 certificate chain as a unmodifiable 
544                 *            list, {@code null} if not specified.
545                 *
546                 * @return This builder.
547                 */
548                public Builder setX509CertChain(final List<Base64> x5c) {
549
550                        this.x5c = x5c;
551                        return this;
552                }
553
554                /**
555                 * Builds a new octet sequence JWK.
556                 *
557                 * @return The octet sequence JWK.
558                 */
559                public ECKey build() {
560
561                        if (d == null) {
562                                // Public key
563                                return new ECKey(crv, x, y, use, alg, kid, x5u, x5t, x5c);
564                        }
565
566                        // Pair
567                        return new ECKey(crv, x, y, d, use, alg, kid, x5u, x5t, x5c);
568                }
569        }
570
571
572        /**
573         * The curve name.
574         */
575        private final Curve crv;
576
577
578        /**
579         * The public 'x' EC coordinate.
580         */
581        private final Base64URL x;
582
583
584        /**
585         * The public 'y' EC coordinate.
586         */
587        private final Base64URL y;
588        
589
590        /**
591         * The private 'd' EC coordinate
592         */
593        private final Base64URL d;
594
595
596        /**
597         * Creates a new public Elliptic Curve JSON Web Key (JWK) with the 
598         * specified parameters.
599         *
600         * @param crv The cryptographic curve. Must not be {@code null}.
601         * @param x   The public 'x' coordinate for the elliptic curve point.
602         *            It is represented as the Base64URL encoding of the 
603         *            coordinate's big endian representation. Must not be 
604         *            {@code null}.
605         * @param y   The public 'y' coordinate for the elliptic curve point. 
606         *            It is represented as the Base64URL encoding of the 
607         *            coordinate's big endian representation. Must not be 
608         *            {@code null}.
609         * @param use The key use, {@code null} if not specified.
610         * @param alg The intended JOSE algorithm for the key, {@code null} if
611         *            not specified.
612         * @param kid The key ID, {@code null} if not specified.
613         * @param x5u The X.509 certificate URL, {@code null} if not specified.
614         * @param x5t The X.509 certificate thumbprint, {@code null} if not
615         *            specified.
616         * @param x5c The X.509 certificate chain, {@code null} if not 
617         *            specified.
618         */
619        public ECKey(final Curve crv, final Base64URL x, final Base64URL y, 
620                     final Use use, final Algorithm alg, final String kid,
621                     final URL x5u, final Base64URL x5t, final List<Base64> x5c) {
622
623                super(KeyType.EC, use, alg, kid, x5u, x5t, x5c);
624
625                if (crv == null) {
626                        throw new IllegalArgumentException("The curve must not be null");
627                }
628
629                this.crv = crv;
630
631                if (x == null) {
632                        throw new IllegalArgumentException("The 'x' coordinate must not be null");
633                }
634
635                this.x = x;
636
637                if (y == null) {
638                        throw new IllegalArgumentException("The 'y' coordinate must not be null");
639                }
640
641                this.y = y;
642
643                this.d = null;
644        }
645
646
647        /**
648         * Creates a new public / private Elliptic Curve JSON Web Key (JWK) 
649         * with the specified parameters.
650         *
651         * @param crv The cryptographic curve. Must not be {@code null}.
652         * @param x   The public 'x' coordinate for the elliptic curve point.
653         *            It is represented as the Base64URL encoding of the 
654         *            coordinate's big endian representation. Must not be 
655         *            {@code null}.
656         * @param y   The public 'y' coordinate for the elliptic curve point. 
657         *            It is represented as the Base64URL encoding of the 
658         *            coordinate's big endian representation. Must not be 
659         *            {@code null}.
660         * @param d   The private 'd' coordinate for the elliptic curve point. 
661         *            It is represented as the Base64URL encoding of the 
662         *            coordinate's big endian representation. Must not be 
663         *            {@code null}.
664         * @param use The key use, {@code null} if not specified.
665         * @param alg The intended JOSE algorithm for the key, {@code null} if
666         *            not specified.
667         * @param kid The key ID, {@code null} if not specified.
668         * @param x5u The X.509 certificate URL, {@code null} if not specified.
669         * @param x5t The X.509 certificate thumbprint, {@code null} if not
670         *            specified.
671         * @param x5c The X.509 certificate chain, {@code null} if not 
672         *            specified.
673         */
674        public ECKey(final Curve crv, final Base64URL x, final Base64URL y, final Base64URL d,
675                     final Use use, final Algorithm alg, final String kid,
676                     final URL x5u, final Base64URL x5t, final List<Base64> x5c) {
677
678                super(KeyType.EC, use, alg, kid, x5u, x5t, x5c);
679
680                if (crv == null) {
681                        throw new IllegalArgumentException("The curve must not be null");
682                }
683
684                this.crv = crv;
685
686                if (x == null) {
687                        throw new IllegalArgumentException("The 'x' coordinate must not be null");
688                }
689
690                this.x = x;
691
692                if (y == null) {
693                        throw new IllegalArgumentException("The 'y' coordinate must not be null");
694                }
695
696                this.y = y;
697                
698                if (d == null) {
699                        throw new IllegalArgumentException("The 'd' coordinate must not be null");
700                }
701
702                this.d = d;
703        }
704
705
706        /**
707         * Creates a new public Elliptic Curve JSON Web Key (JWK) with the 
708         * specified parameters.
709         *
710         * @param crv The cryptographic curve. Must not be {@code null}.
711         * @param pub The public EC key to represent. Must not be {@code null}.
712         * @param use The key use, {@code null} if not specified.
713         * @param alg The intended JOSE algorithm for the key, {@code null} if
714         *            not specified.
715         * @param kid The key ID, {@code null} if not specified.
716         * @param x5u The X.509 certificate URL, {@code null} if not specified.
717         * @param x5t The X.509 certificate thumbprint, {@code null} if not
718         *            specified.
719         * @param x5c The X.509 certificate chain, {@code null} if not 
720         *            specified.
721         */
722        public ECKey(final Curve crv, final ECPublicKey pub, 
723                     final Use use, final Algorithm alg, final String kid,
724                     final URL x5u, final Base64URL x5t, final List<Base64> x5c) {
725
726                this(crv, 
727                     Base64URL.encode(pub.getW().getAffineX()), 
728                     Base64URL.encode(pub.getW().getAffineY()),
729                     use, alg, kid,
730                     x5u, x5t, x5c);
731        }
732
733
734        /**
735         * Creates a new public / private Elliptic Curve JSON Web Key (JWK) 
736         * with the specified parameters.
737         *
738         * @param crv  The cryptographic curve. Must not be {@code null}.
739         * @param pub  The public EC key to represent. Must not be 
740         *             {@code null}.
741         * @param priv The private EC key to represent. Must not be 
742         *             {@code null}.
743         * @param use  The key use, {@code null} if not specified.
744         * @param alg  The intended JOSE algorithm for the key, {@code null} if
745         *             not specified.
746         * @param kid  The key ID, {@code null} if not specified.
747         * @param x5u  The X.509 certificate URL, {@code null} if not 
748         *             specified.
749         * @param x5t  The X.509 certificate thumbprint, {@code null} if not
750         *             specified.
751         * @param x5c  The X.509 certificate chain, {@code null} if not 
752         *             specified.
753         */
754        public ECKey(final Curve crv, final ECPublicKey pub, final ECPrivateKey priv, 
755                     final Use use, final Algorithm alg, final String kid,
756                     final URL x5u, final Base64URL x5t, final List<Base64> x5c) {
757
758                this(crv,
759                     Base64URL.encode(pub.getW().getAffineX()), 
760                     Base64URL.encode(pub.getW().getAffineY()),
761                     Base64URL.encode(priv.getS()),
762                     use, alg, kid,
763                     x5u, x5t, x5c);
764        }
765
766
767        /**
768         * Gets the cryptographic curve.
769         *
770         * @return The cryptographic curve.
771         */
772        public Curve getCurve() {
773
774                return crv;
775        }
776
777
778        /**
779         * Gets the public 'x' coordinate for the elliptic curve point.
780         *
781         * @return The 'x' coordinate. It is represented as the Base64URL 
782         *         encoding of the coordinate's big endian representation.
783         */
784        public Base64URL getX() {
785
786                return x;
787        }
788
789
790        /**
791         * Gets the public 'y' coordinate for the elliptic curve point.
792         *
793         * @return The 'y' coordinate. It is represented as the Base64URL 
794         *         encoding of the coordinate's big endian representation.
795         */
796        public Base64URL getY() {
797
798                return y;
799        }
800
801        
802        /**
803         * Gets the private 'd' coordinate for the elliptic curve point. It is 
804         * represented as the Base64URL encoding of the coordinate's big endian 
805         * representation.
806         *
807         * @return The 'd' coordinate.  It is represented as the Base64URL 
808         *         encoding of the coordinate's big endian representation. 
809         *         {@code null} if not specified (for a public key).
810         */
811        public Base64URL getD() {
812
813                return d;
814        }
815
816
817        /**
818         * Gets a new BouncyCastle.org EC key factory.
819         *
820         * @return The EC key factory.
821         *
822         * @throws NoSuchAlgorithmException If a JCA provider or algorithm 
823         *                                  exception was encountered.
824         */
825        private static KeyFactory getECKeyFactory()
826                throws NoSuchAlgorithmException {
827
828                return KeyFactory.getInstance("EC", BouncyCastleProviderSingleton.getInstance());
829        }
830
831
832        /**
833         * Returns a standard {@code java.security.interfaces.ECPublicKey} 
834         * representation of this Elliptic Curve JWK.
835         * 
836         * @return The public Elliptic Curve key.
837         * 
838         * @throws NoSuchAlgorithmException If EC is not supported by the
839         *                                  underlying Java Cryptography (JCA)
840         *                                  provider.
841         * @throws InvalidKeySpecException  If the JWK key parameters are 
842         *                                  invalid for a public EC key.
843         */
844        public ECPublicKey toECPublicKey()
845                throws NoSuchAlgorithmException, InvalidKeySpecException {
846
847                ECParameterSpec spec = crv.toECParameterSpec();
848
849                if (spec == null) {
850                        throw new NoSuchAlgorithmException("Couldn't get EC parameter spec for curve " + crv);
851                }
852
853                ECPoint w = new ECPoint(x.decodeToBigInteger(), y.decodeToBigInteger());
854
855                ECPublicKeySpec publicKeySpec = new ECPublicKeySpec(w, spec);
856
857                KeyFactory keyFactory = getECKeyFactory();
858
859                return (ECPublicKey)keyFactory.generatePublic(publicKeySpec);
860        }
861        
862
863        /**
864         * Returns a standard {@code java.security.interfaces.ECPrivateKey} 
865         * representation of this Elliptic Curve JWK.
866         * 
867         * @return The private Elliptic Curve key, {@code null} if not 
868         *         specified by this JWK.
869         * 
870         * @throws NoSuchAlgorithmException If EC is not supported by the
871         *                                  underlying Java Cryptography (JCA)
872         *                                  provider.
873         * @throws InvalidKeySpecException  If the JWK key parameters are 
874         *                                  invalid for a private EC key.
875         */
876        public ECPrivateKey toECPrivateKey()
877                throws NoSuchAlgorithmException, InvalidKeySpecException {
878
879                if (d == null) {
880                        // No private 'd' param
881                        return null;
882                }
883
884                ECParameterSpec spec = crv.toECParameterSpec();
885
886                if (spec == null) {
887                        throw new NoSuchAlgorithmException("Couldn't get EC parameter spec for curve " + crv);
888                }
889
890                ECPrivateKeySpec privateKeySpec = new ECPrivateKeySpec(d.decodeToBigInteger(), spec);
891
892                KeyFactory keyFactory = getECKeyFactory();
893
894                return (ECPrivateKey)keyFactory.generatePrivate(privateKeySpec);
895        }
896        
897
898        /**
899         * Returns a standard {@code java.security.KeyPair} representation of 
900         * this Elliptic Curve JWK.
901         * 
902         * @return The Elliptic Curve key pair. The private Elliptic Curve key 
903         *         will be {@code null} if not specified.
904         * 
905         * @throws NoSuchAlgorithmException If EC is not supported by the
906         *                                  underlying Java Cryptography (JCA)
907         *                                  provider.
908         * @throws InvalidKeySpecException  If the JWK key parameters are 
909         *                                  invalid for a public and / or 
910         *                                  private EC key.
911         */
912        public KeyPair toKeyPair()
913                throws NoSuchAlgorithmException, InvalidKeySpecException {
914
915                return new KeyPair(toECPublicKey(), toECPrivateKey());          
916        }
917
918
919        @Override
920        public boolean isPrivate() {
921
922                if (d != null) {
923                        return true;
924                } else {
925                        return false;
926                }
927        }
928
929        
930        /**
931         * Returns a copy of this Elliptic Curve JWK with any private values 
932         * removed.
933         *
934         * @return The copied public Elliptic Curve JWK.
935         */
936        @Override
937        public ECKey toPublicJWK() {
938
939                return new ECKey(getCurve(), getX(), getY(), getKeyUse(), getAlgorithm(), getKeyID(),
940                                 getX509CertURL(), getX509CertThumbprint(), getX509CertChain());
941        }
942        
943
944        @Override
945        public JSONObject toJSONObject() {
946
947                JSONObject o = super.toJSONObject();
948
949                // Append EC specific attributes
950                o.put("crv", crv.toString());
951                o.put("x", x.toString());
952                o.put("y", y.toString());
953
954                if (d != null) {
955                        o.put("d", d.toString());
956                }
957                
958                return o;
959        }
960
961
962        /**
963         * Parses a public / private Elliptic Curve JWK from the specified JSON
964         * object string representation.
965         *
966         * @param s The JSON object string to parse. Must not be {@code null}.
967         *
968         * @return The public / private Elliptic Curve JWK.
969         *
970         * @throws ParseException If the string couldn't be parsed to an
971         *                        Elliptic Curve JWK.
972         */
973        public static ECKey parse(final String s)
974                throws ParseException {
975
976                return parse(JSONObjectUtils.parseJSONObject(s));
977        }
978
979
980        /**
981         * Parses a public / private Elliptic Curve JWK from the specified JSON
982         * object representation.
983         *
984         * @param jsonObject The JSON object to parse. Must not be 
985         *                   {@code null}.
986         *
987         * @return The public / private Elliptic Curve JWK.
988         *
989         * @throws ParseException If the JSON object couldn't be parsed to an 
990         *                        Elliptic Curve JWK.
991         */
992        public static ECKey parse(final JSONObject jsonObject)
993                throws ParseException {
994
995                // Parse the mandatory parameters first
996                Curve crv = Curve.parse(JSONObjectUtils.getString(jsonObject, "crv"));
997                Base64URL x = new Base64URL(JSONObjectUtils.getString(jsonObject, "x"));
998                Base64URL y = new Base64URL(JSONObjectUtils.getString(jsonObject, "y"));
999
1000                // Check key type
1001                KeyType kty = KeyType.parse(JSONObjectUtils.getString(jsonObject, "kty"));
1002
1003                if (kty != KeyType.EC) {
1004                        throw new ParseException("The key type \"kty\" must be EC", 0);
1005                }
1006                
1007                // optional private key
1008                Base64URL d = null;
1009                if (jsonObject.get("d") != null) {
1010                        d = new Base64URL(JSONObjectUtils.getString(jsonObject, "d"));
1011                }
1012                
1013                // Get optional key use
1014                Use use = null;
1015
1016                if (jsonObject.containsKey("use")) {
1017                        use = Use.parse(JSONObjectUtils.getString(jsonObject, "use"));
1018                }
1019
1020                // Get optional intended algorithm
1021                Algorithm alg = null;
1022
1023                if (jsonObject.containsKey("alg")) {
1024                        alg = new Algorithm(JSONObjectUtils.getString(jsonObject, "alg"));
1025                }
1026
1027                // Get optional key ID
1028                String kid = null;
1029
1030                if (jsonObject.containsKey("kid")) {
1031                        kid = JSONObjectUtils.getString(jsonObject, "kid");
1032                }
1033
1034                // Get optional X.509 cert URL
1035                URL x5u = null;
1036
1037                if (jsonObject.containsKey("x5u")) {
1038                        x5u = JSONObjectUtils.getURL(jsonObject, "x5u");        
1039                }
1040
1041                // Get optional X.509 cert thumbprint
1042                Base64URL x5t = null;
1043
1044                if (jsonObject.containsKey("x5t")) {
1045                        x5t = new Base64URL(JSONObjectUtils.getString(jsonObject, "x5t"));
1046                }
1047
1048                // Get optional X.509 cert chain
1049                List<Base64> x5c = null;
1050
1051                if (jsonObject.containsKey("x5c")) {
1052                        x5c = X509CertChainUtils.parseX509CertChain(JSONObjectUtils.getJSONArray(jsonObject, "x5c"));   
1053                }
1054
1055                if (d == null) {
1056                        // Public key
1057                        return new ECKey(crv, x, y, use, alg, kid, x5u, x5t, x5c);
1058
1059                } else {
1060                        // Key pair
1061                        return new ECKey(crv, x, y, d, use, alg, kid, x5u, x5t, x5c);
1062                }
1063        }
1064}