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