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}.
238                 *
239                 * @return The cryptographic curve.
240                 *
241                 * @throws ParseException If the string couldn't be parsed.
242                 */
243                public static Curve parse(final String s) 
244                        throws ParseException {
245
246                        if (s == null) {
247                                throw new IllegalArgumentException("The cryptographic curve string must not be null");
248                        }
249
250                        if (s.equals(P_256.getName())) {
251                                return P_256;
252
253                        } else if (s.equals(P_384.getName())) {
254                                return P_384;
255
256                        } else if (s.equals(P_521.getName())) {
257                                return P_521;
258
259                        } else {
260                                return new Curve(s);
261                        }
262                }
263
264
265                /**
266                 * Gets the cryptographic curve for the specified standard 
267                 * (JCA) name.
268                 *
269                 * @param stdName The standard (JCA) name. Must not be 
270                 *                {@code null}.
271                 *
272                 * @throws IllegalArgumentException If no matching JOSE curve 
273                 *                                  constant could be found.
274                 */
275                public static Curve forStdName(final String stdName) {
276
277                        if (stdName.equals("secp256r1")) {
278                                return P_256;
279                        } else if (stdName.equals("secp384r1")) {
280                                return P_384;
281                        } else if (stdName.equals("secp521r1")) {
282                                return P_521;
283                        } else {
284                                throw new IllegalArgumentException("No matching curve constant for standard (JCA) name " + stdName);
285                        }
286                }
287        }
288
289
290        /**
291         * Builder for constructing Elliptic Curve JWKs.
292         *
293         * <p>Example use:
294         *
295         * <pre>
296         * ECKey key = new ECKey.Builder(Curve.P521, x, y).
297         *             d(d).
298         *             algorithm(JWSAlgorithm.ES512).
299         *             keyID("789").
300         *             build();
301         * </pre>
302         */
303        public static class Builder {
304
305
306                /**
307                 * The curve name.
308                 */
309                private final Curve crv;
310
311
312                /**
313                 * The public 'x' EC coordinate.
314                 */
315                private final Base64URL x;
316
317
318                /**
319                 * The public 'y' EC coordinate.
320                 */
321                private final Base64URL y;
322                
323
324                /**
325                 * The private 'd' EC coordinate, optional.
326                 */
327                private Base64URL d;
328
329
330                /**
331                 * The key use, optional.
332                 */
333                private KeyUse use;
334
335
336                /**
337                 * The key operations, optional.
338                 */
339                private Set<KeyOperation> ops;
340
341
342                /**
343                 * The intended JOSE algorithm for the key, optional.
344                 */
345                private Algorithm alg;
346
347
348                /**
349                 * The key ID, optional.
350                 */
351                private String kid;
352
353
354                /**
355                 * X.509 certificate URL, optional.
356                 */
357                private URL x5u;
358
359
360                /**
361                 * X.509 certificate thumbprint, optional.
362                 */
363                private Base64URL x5t;
364
365
366                /**
367                 * The X.509 certificate chain, optional.
368                 */
369                private List<Base64> x5c;
370
371
372                /**
373                 * Creates a new Elliptic Curve JWK builder.
374                 *
375                 * @param crv The cryptographic curve. Must not be 
376                 *            {@code null}.
377                 * @param x   The public 'x' coordinate for the elliptic curve 
378                 *            point. It is represented as the Base64URL 
379                 *            encoding of the coordinate's big endian 
380                 *            representation. Must not be {@code null}.
381                 * @param y   The public 'y' coordinate for the elliptic curve 
382                 *            point. It is represented as the Base64URL 
383                 *            encoding of the coordinate's big endian 
384                 *            representation. Must not be {@code null}.
385                 */
386                public Builder(final Curve crv, final Base64URL x, final Base64URL y) {
387
388                        if (crv == null) {
389                                throw new IllegalArgumentException("The curve must not be null");
390                        }
391
392                        this.crv = crv;
393
394                        if (x == null) {
395                                throw new IllegalArgumentException("The 'x' coordinate must not be null");
396                        }
397
398                        this.x = x;
399
400                        if (y == null) {
401                                throw new IllegalArgumentException("The 'y' coordinate must not be null");
402                        }
403
404                        this.y = y;
405                }
406
407
408                /**
409                 * Creates a new Elliptic Curve JWK builder.
410                 *
411                 * @param crv The cryptographic curve. Must not be 
412                 *            {@code null}.
413                 * @param pub The public EC key to represent. Must not be 
414                 *            {@code null}.
415                 */
416                public Builder(final Curve crv, final ECPublicKey pub) {
417
418                        this(crv,
419                             encodeCoordinate(pub.getParams().getCurve().getField().getFieldSize(), pub.getW().getAffineX()),
420                             encodeCoordinate(pub.getParams().getCurve().getField().getFieldSize(), pub.getW().getAffineY()));
421                }
422
423
424                /**
425                 * Sets the private 'd' coordinate for the elliptic curve 
426                 * point. The alternative method is {@link #privateKey}.
427                 *
428                 * @param d The 'd' coordinate. It is represented as the 
429                 *          Base64URL encoding of the coordinate's big endian 
430                 *          representation. {@code null} if not specified (for
431                 *          a public key).
432                 *
433                 * @return This builder.
434                 */
435                public Builder d(final Base64URL d) {
436
437                        this.d = d;
438                        return this;
439                }
440
441
442                /**
443                 * Sets the private Elliptic Curve key. The alternative method 
444                 * is {@link #d}.
445                 *
446                 * @param priv The private EC key, used to obtain the private
447                 *             'd' coordinate for the elliptic curve point.
448                 *             {@code null} if not specified (for a public 
449                 *             key).
450                 *
451                 * @return This builder.
452                 */
453                public Builder privateKey(final ECPrivateKey priv) {
454
455                        if (priv != null) {
456                                this.d = encodeCoordinate(priv.getParams().getCurve().getField().getFieldSize(), priv.getS());
457                        }
458                        
459                        return this;
460                }
461
462
463                /**
464                 * Sets the use ({@code use}) of the JWK.
465                 *
466                 * @param use The key use, {@code null} if not specified or if 
467                 *            the key is intended for signing as well as 
468                 *            encryption.
469                 *
470                 * @return This builder.
471                 */
472                public Builder keyUse(final KeyUse use) {
473
474                        this.use = use;
475                        return this;
476                }
477
478
479                /**
480                 * Sets the operations ({@code key_ops}) of the JWK.
481                 *
482                 * @param ops The key operations, {@code null} if not
483                 *            specified.
484                 *
485                 * @return This builder.
486                 */
487                public Builder keyOperations(final Set<KeyOperation> ops) {
488
489                        this.ops = ops;
490                        return this;
491                }
492
493
494                /**
495                 * Sets the intended JOSE algorithm ({@code alg}) for the JWK.
496                 *
497                 * @param alg The intended JOSE algorithm, {@code null} if not 
498                 *            specified.
499                 *
500                 * @return This builder.
501                 */
502                public Builder algorithm(final Algorithm alg) {
503
504                        this.alg = alg;
505                        return this;
506                }
507
508                /**
509                 * Sets the ID ({@code kid}) of the JWK. The key ID can be used 
510                 * to match a specific key. This can be used, for instance, to 
511                 * choose a key within a {@link JWKSet} during key rollover. 
512                 * The key ID may also correspond to a JWS/JWE {@code kid} 
513                 * header parameter value.
514                 *
515                 * @param kid The key ID, {@code null} if not specified.
516                 *
517                 * @return This builder.
518                 */
519                public Builder keyID(final String kid) {
520
521                        this.kid = kid;
522                        return this;
523                }
524
525
526                /**
527                 * Sets the X.509 certificate URL ({@code x5u}) of the JWK.
528                 *
529                 * @param x5u The X.509 certificate URL, {@code null} if not 
530                 *            specified.
531                 *
532                 * @return This builder.
533                 */
534                public Builder x509CertURL(final URL x5u) {
535
536                        this.x5u = x5u;
537                        return this;
538                }
539
540
541                /**
542                 * Sets the X.509 certificate thumbprint ({@code x5t}) of the
543                 * JWK.
544                 *
545                 * @param x5t The X.509 certificate thumbprint, {@code null} if 
546                 *            not specified.
547                 *
548                 * @return This builder.
549                 */
550                public Builder x509CertThumbprint(final Base64URL x5t) {
551
552                        this.x5t = x5t;
553                        return this;
554                }
555
556
557                /**
558                 * Sets the X.509 certificate chain ({@code x5c}) of the JWK.
559                 *
560                 * @param x5c The X.509 certificate chain as a unmodifiable 
561                 *            list, {@code null} if not specified.
562                 *
563                 * @return This builder.
564                 */
565                public Builder x509CertChain(final List<Base64> x5c) {
566
567                        this.x5c = x5c;
568                        return this;
569                }
570
571
572                /**
573                 * Builds a new octet sequence JWK.
574                 *
575                 * @return The octet sequence JWK.
576                 *
577                 * @throws IllegalStateException If the JWK parameters were
578                 *                               inconsistently specified.
579                 */
580                public ECKey build() {
581
582                        try {
583                                if (d == null) {
584                                        // Public key
585                                        return new ECKey(crv, x, y, use, ops, alg, kid, x5u, x5t, x5c);
586                                }
587
588                                // Pair
589                                return new ECKey(crv, x, y, d, use, ops, alg, kid, x5u, x5t, x5c);
590
591                        } catch (IllegalArgumentException e) {
592
593                                throw new IllegalStateException(e.getMessage(), e);
594                        }
595                }
596        }
597
598
599        /**
600         * Returns the Base64URL encoding of the specified elliptic curve 'x',
601         * 'y' or 'd' coordinate, with leading zero padding up to the specified
602         * field size in bits.
603         *
604         * @param fieldSize  The field size in bits.
605         * @param coordinate The elliptic curve coordinate. Must not be
606         *                   {@code null}.
607         *
608         * @return The Base64URL-encoded coordinate, with leading zero padding
609         *         up to the curve's field size.
610         */
611        public static Base64URL encodeCoordinate(final int fieldSize, final BigInteger coordinate) {
612
613                byte[] unpadded = BigIntegerUtils.toBytesUnsigned(coordinate);
614
615                int bytesToOutput = (fieldSize + 7)/8;
616
617                if (unpadded.length >= bytesToOutput) {
618                        // Greater-than check to prevent exception on malformed
619                        // key below
620                        return Base64URL.encode(unpadded);
621                }
622
623                byte[] padded = new byte[bytesToOutput];
624
625                System.arraycopy(unpadded, 0, padded, bytesToOutput - unpadded.length, unpadded.length);
626
627                return Base64URL.encode(padded);
628        }
629
630
631        /**
632         * The curve name.
633         */
634        private final Curve crv;
635
636
637        /**
638         * The public 'x' EC coordinate.
639         */
640        private final Base64URL x;
641
642
643        /**
644         * The public 'y' EC coordinate.
645         */
646        private final Base64URL y;
647        
648
649        /**
650         * The private 'd' EC coordinate
651         */
652        private final Base64URL d;
653
654
655        /**
656         * Creates a new public Elliptic Curve JSON Web Key (JWK) with the 
657         * specified parameters.
658         *
659         * @param crv The cryptographic curve. Must not be {@code null}.
660         * @param x   The public 'x' 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 y   The public 'y' coordinate for the elliptic curve point. 
665         *            It is represented as the Base64URL encoding of the 
666         *            coordinate's big endian representation. Must not be 
667         *            {@code null}.
668         * @param use The key use, {@code null} if not specified or if the key
669         *            is intended for signing as well as encryption.
670         * @param ops The key operations, {@code null} if not specified.
671         * @param alg The intended JOSE algorithm for the key, {@code null} if
672         *            not specified.
673         * @param kid The key ID, {@code null} if not specified.
674         * @param x5u The X.509 certificate URL, {@code null} if not specified.
675         * @param x5t The X.509 certificate thumbprint, {@code null} if not
676         *            specified.
677         * @param x5c The X.509 certificate chain, {@code null} if not 
678         *            specified.
679         */
680        public ECKey(final Curve crv, final Base64URL x, final Base64URL y, 
681                     final KeyUse use, final Set<KeyOperation> ops, final Algorithm alg, final String kid,
682                     final URL x5u, final Base64URL x5t, final List<Base64> x5c) {
683
684                super(KeyType.EC, use, ops, alg, kid, x5u, x5t, x5c);
685
686                if (crv == null) {
687                        throw new IllegalArgumentException("The curve must not be null");
688                }
689
690                this.crv = crv;
691
692                if (x == null) {
693                        throw new IllegalArgumentException("The 'x' coordinate must not be null");
694                }
695
696                this.x = x;
697
698                if (y == null) {
699                        throw new IllegalArgumentException("The 'y' coordinate must not be null");
700                }
701
702                this.y = y;
703
704                this.d = null;
705        }
706
707
708        /**
709         * Creates a new public / private Elliptic Curve JSON Web Key (JWK) 
710         * with the specified parameters.
711         *
712         * @param crv The cryptographic curve. Must not be {@code null}.
713         * @param x   The public 'x' coordinate for the elliptic curve point.
714         *            It is represented as the Base64URL encoding of the 
715         *            coordinate's big endian representation. Must not be 
716         *            {@code null}.
717         * @param y   The public 'y' coordinate for the elliptic curve point. 
718         *            It is represented as the Base64URL encoding of the 
719         *            coordinate's big endian representation. Must not be 
720         *            {@code null}.
721         * @param d   The private 'd' coordinate for the elliptic curve point. 
722         *            It is represented as the Base64URL encoding of the 
723         *            coordinate's big endian representation. Must not be 
724         *            {@code null}.
725         * @param use The key use, {@code null} if not specified or if the key
726         *            is intended for signing as well as encryption.
727         * @param ops The key operations, {@code null} if not specified.
728         * @param alg The intended JOSE algorithm for the key, {@code null} if
729         *            not specified.
730         * @param kid The key ID, {@code null} if not specified.
731         * @param x5u The X.509 certificate URL, {@code null} if not specified.
732         * @param x5t The X.509 certificate thumbprint, {@code null} if not
733         *            specified.
734         * @param x5c The X.509 certificate chain, {@code null} if not 
735         *            specified.
736         */
737        public ECKey(final Curve crv, final Base64URL x, final Base64URL y, final Base64URL d,
738                     final KeyUse use, final Set<KeyOperation> ops, final Algorithm alg, final String kid,
739                     final URL x5u, final Base64URL x5t, final List<Base64> x5c) {
740
741                super(KeyType.EC, use, ops, alg, kid, x5u, x5t, x5c);
742
743                if (crv == null) {
744                        throw new IllegalArgumentException("The curve must not be null");
745                }
746
747                this.crv = crv;
748
749                if (x == null) {
750                        throw new IllegalArgumentException("The 'x' coordinate must not be null");
751                }
752
753                this.x = x;
754
755                if (y == null) {
756                        throw new IllegalArgumentException("The 'y' coordinate must not be null");
757                }
758
759                this.y = y;
760                
761                if (d == null) {
762                        throw new IllegalArgumentException("The 'd' coordinate must not be null");
763                }
764
765                this.d = d;
766        }
767
768
769        /**
770         * Creates a new public Elliptic Curve JSON Web Key (JWK) with the 
771         * specified parameters.
772         *
773         * @param crv The cryptographic curve. Must not be {@code null}.
774         * @param pub The public EC key to represent. Must not be {@code null}.
775         * @param use The key use, {@code null} if not specified or if the key
776         *            is intended for signing as well as encryption.
777         * @param ops The key operations, {@code null} if not specified.
778         * @param alg The intended JOSE algorithm for the key, {@code null} if
779         *            not specified.
780         * @param kid The key ID, {@code null} if not specified.
781         * @param x5u The X.509 certificate URL, {@code null} if not specified.
782         * @param x5t The X.509 certificate thumbprint, {@code null} if not
783         *            specified.
784         * @param x5c The X.509 certificate chain, {@code null} if not 
785         *            specified.
786         */
787        public ECKey(final Curve crv, final ECPublicKey pub, 
788                     final KeyUse use, final Set<KeyOperation> ops, final Algorithm alg, final String kid,
789                     final URL x5u, final Base64URL x5t, final List<Base64> x5c) {
790
791                this(crv, 
792                     encodeCoordinate(pub.getParams().getCurve().getField().getFieldSize(), pub.getW().getAffineX()),
793                     encodeCoordinate(pub.getParams().getCurve().getField().getFieldSize(), pub.getW().getAffineY()),
794                     use, ops, alg, kid,
795                     x5u, x5t, x5c);
796        }
797
798
799        /**
800         * Creates a new public / private Elliptic Curve JSON Web Key (JWK) 
801         * with the specified parameters.
802         *
803         * @param crv  The cryptographic curve. Must not be {@code null}.
804         * @param pub  The public EC key to represent. Must not be 
805         *             {@code null}.
806         * @param priv The private EC key to represent. Must not be 
807         *             {@code null}.
808         * @param use  The key use, {@code null} if not specified or if the key
809         *             is intended for signing as well as encryption.
810         * @param ops  The key operations, {@code null} if not specified.
811         * @param alg  The intended JOSE algorithm for the key, {@code null} if
812         *             not specified.
813         * @param kid  The key ID, {@code null} if not specified.
814         * @param x5u  The X.509 certificate URL, {@code null} if not 
815         *             specified.
816         * @param x5t  The X.509 certificate thumbprint, {@code null} if not
817         *             specified.
818         * @param x5c  The X.509 certificate chain, {@code null} if not 
819         *             specified.
820         */
821        public ECKey(final Curve crv, final ECPublicKey pub, final ECPrivateKey priv, 
822                     final KeyUse use, final Set<KeyOperation> ops, final Algorithm alg, final String kid,
823                     final URL x5u, final Base64URL x5t, final List<Base64> x5c) {
824
825                this(crv,
826                     encodeCoordinate(pub.getParams().getCurve().getField().getFieldSize(), pub.getW().getAffineX()),
827                     encodeCoordinate(pub.getParams().getCurve().getField().getFieldSize(), pub.getW().getAffineY()),
828                     encodeCoordinate(priv.getParams().getCurve().getField().getFieldSize(), priv.getS()),
829                     use, ops, alg, kid,
830                     x5u, x5t, x5c);
831        }
832
833
834        /**
835         * Gets the cryptographic curve.
836         *
837         * @return The cryptographic curve.
838         */
839        public Curve getCurve() {
840
841                return crv;
842        }
843
844
845        /**
846         * Gets the public 'x' coordinate for the elliptic curve point.
847         *
848         * @return The 'x' coordinate. It is represented as the Base64URL 
849         *         encoding of the coordinate's big endian representation.
850         */
851        public Base64URL getX() {
852
853                return x;
854        }
855
856
857        /**
858         * Gets the public 'y' coordinate for the elliptic curve point.
859         *
860         * @return The 'y' coordinate. It is represented as the Base64URL 
861         *         encoding of the coordinate's big endian representation.
862         */
863        public Base64URL getY() {
864
865                return y;
866        }
867
868        
869        /**
870         * Gets the private 'd' coordinate for the elliptic curve point. It is 
871         * represented as the Base64URL encoding of the coordinate's big endian 
872         * representation.
873         *
874         * @return The 'd' coordinate.  It is represented as the Base64URL 
875         *         encoding of the coordinate's big endian representation. 
876         *         {@code null} if not specified (for a public key).
877         */
878        public Base64URL getD() {
879
880                return d;
881        }
882
883
884        /**
885         * Gets a new BouncyCastle.org EC key factory.
886         *
887         * @return The EC key factory.
888         *
889         * @throws NoSuchAlgorithmException If a JCA provider or algorithm 
890         *                                  exception was encountered.
891         */
892        private static KeyFactory getECKeyFactory()
893                throws NoSuchAlgorithmException {
894
895                return KeyFactory.getInstance("EC", BouncyCastleProviderSingleton.getInstance());
896        }
897
898
899        /**
900         * Returns a standard {@code java.security.interfaces.ECPublicKey} 
901         * representation of this Elliptic Curve JWK.
902         * 
903         * @return The public Elliptic Curve key.
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 EC key.
910         */
911        public ECPublicKey toECPublicKey()
912                throws NoSuchAlgorithmException, InvalidKeySpecException {
913
914                ECParameterSpec spec = crv.toECParameterSpec();
915
916                if (spec == null) {
917                        throw new NoSuchAlgorithmException("Couldn't get EC parameter spec for curve " + crv);
918                }
919
920                ECPoint w = new ECPoint(x.decodeToBigInteger(), y.decodeToBigInteger());
921
922                ECPublicKeySpec publicKeySpec = new ECPublicKeySpec(w, spec);
923
924                KeyFactory keyFactory = getECKeyFactory();
925
926                return (ECPublicKey)keyFactory.generatePublic(publicKeySpec);
927        }
928        
929
930        /**
931         * Returns a standard {@code java.security.interfaces.ECPrivateKey} 
932         * representation of this Elliptic Curve JWK.
933         * 
934         * @return The private Elliptic Curve key, {@code null} if not 
935         *         specified by this JWK.
936         * 
937         * @throws NoSuchAlgorithmException If EC is not supported by the
938         *                                  underlying Java Cryptography (JCA)
939         *                                  provider.
940         * @throws InvalidKeySpecException  If the JWK key parameters are 
941         *                                  invalid for a private EC key.
942         */
943        public ECPrivateKey toECPrivateKey()
944                throws NoSuchAlgorithmException, InvalidKeySpecException {
945
946                if (d == null) {
947                        // No private 'd' param
948                        return null;
949                }
950
951                ECParameterSpec spec = crv.toECParameterSpec();
952
953                if (spec == null) {
954                        throw new NoSuchAlgorithmException("Couldn't get EC parameter spec for curve " + crv);
955                }
956
957                ECPrivateKeySpec privateKeySpec = new ECPrivateKeySpec(d.decodeToBigInteger(), spec);
958
959                KeyFactory keyFactory = getECKeyFactory();
960
961                return (ECPrivateKey)keyFactory.generatePrivate(privateKeySpec);
962        }
963        
964
965        /**
966         * Returns a standard {@code java.security.KeyPair} representation of 
967         * this Elliptic Curve JWK.
968         * 
969         * @return The Elliptic Curve key pair. The private Elliptic Curve key 
970         *         will be {@code null} if not specified.
971         * 
972         * @throws NoSuchAlgorithmException If EC is not supported by the
973         *                                  underlying Java Cryptography (JCA)
974         *                                  provider.
975         * @throws InvalidKeySpecException  If the JWK key parameters are 
976         *                                  invalid for a public and / or 
977         *                                  private EC key.
978         */
979        public KeyPair toKeyPair()
980                throws NoSuchAlgorithmException, InvalidKeySpecException {
981
982                return new KeyPair(toECPublicKey(), toECPrivateKey());          
983        }
984
985
986        @Override
987        public boolean isPrivate() {
988
989                return d != null;
990        }
991
992        
993        /**
994         * Returns a copy of this Elliptic Curve JWK with any private values 
995         * removed.
996         *
997         * @return The copied public Elliptic Curve JWK.
998         */
999        @Override
1000        public ECKey toPublicJWK() {
1001
1002                return new ECKey(getCurve(), getX(), getY(),
1003                                 getKeyUse(), getKeyOperations(), getAlgorithm(), getKeyID(),
1004                                 getX509CertURL(), getX509CertThumbprint(), getX509CertChain());
1005        }
1006        
1007
1008        @Override
1009        public JSONObject toJSONObject() {
1010
1011                JSONObject o = super.toJSONObject();
1012
1013                // Append EC specific attributes
1014                o.put("crv", crv.toString());
1015                o.put("x", x.toString());
1016                o.put("y", y.toString());
1017
1018                if (d != null) {
1019                        o.put("d", d.toString());
1020                }
1021                
1022                return o;
1023        }
1024
1025
1026        /**
1027         * Parses a public / private Elliptic Curve JWK from the specified JSON
1028         * object string representation.
1029         *
1030         * @param s The JSON object string to parse. Must not be {@code null}.
1031         *
1032         * @return The public / private Elliptic Curve JWK.
1033         *
1034         * @throws ParseException If the string couldn't be parsed to an
1035         *                        Elliptic Curve JWK.
1036         */
1037        public static ECKey parse(final String s)
1038                throws ParseException {
1039
1040                return parse(JSONObjectUtils.parseJSONObject(s));
1041        }
1042
1043
1044        /**
1045         * Parses a public / private Elliptic Curve JWK from the specified JSON
1046         * object representation.
1047         *
1048         * @param jsonObject The JSON object to parse. Must not be 
1049         *                   {@code null}.
1050         *
1051         * @return The public / private Elliptic Curve JWK.
1052         *
1053         * @throws ParseException If the JSON object couldn't be parsed to an 
1054         *                        Elliptic Curve JWK.
1055         */
1056        public static ECKey parse(final JSONObject jsonObject)
1057                throws ParseException {
1058
1059                // Parse the mandatory parameters first
1060                Curve crv = Curve.parse(JSONObjectUtils.getString(jsonObject, "crv"));
1061                Base64URL x = new Base64URL(JSONObjectUtils.getString(jsonObject, "x"));
1062                Base64URL y = new Base64URL(JSONObjectUtils.getString(jsonObject, "y"));
1063
1064                // Check key type
1065                KeyType kty = KeyType.parse(JSONObjectUtils.getString(jsonObject, "kty"));
1066
1067                if (kty != KeyType.EC) {
1068                        throw new ParseException("The key type \"kty\" must be EC", 0);
1069                }
1070                
1071                // Get optional private key
1072                Base64URL d = null;
1073                if (jsonObject.get("d") != null) {
1074                        d = new Base64URL(JSONObjectUtils.getString(jsonObject, "d"));
1075                }
1076                
1077                // Get optional key use
1078                KeyUse use = null;
1079
1080                if (jsonObject.containsKey("use")) {
1081                        use = KeyUse.parse(JSONObjectUtils.getString(jsonObject, "use"));
1082                }
1083
1084                // Get optional key operations
1085                Set<KeyOperation> ops = null;
1086
1087                if (jsonObject.containsKey("key_ops")) {
1088                        ops = KeyOperation.parse(JSONObjectUtils.getStringList(jsonObject, "key_ops"));
1089                }
1090
1091                // Get optional intended algorithm
1092                Algorithm alg = null;
1093
1094                if (jsonObject.containsKey("alg")) {
1095                        alg = new Algorithm(JSONObjectUtils.getString(jsonObject, "alg"));
1096                }
1097
1098                // Get optional key ID
1099                String kid = null;
1100
1101                if (jsonObject.containsKey("kid")) {
1102                        kid = JSONObjectUtils.getString(jsonObject, "kid");
1103                }
1104
1105                // Get optional X.509 cert URL
1106                URL x5u = null;
1107
1108                if (jsonObject.containsKey("x5u")) {
1109                        x5u = JSONObjectUtils.getURL(jsonObject, "x5u");        
1110                }
1111
1112                // Get optional X.509 cert thumbprint
1113                Base64URL x5t = null;
1114
1115                if (jsonObject.containsKey("x5t")) {
1116                        x5t = new Base64URL(JSONObjectUtils.getString(jsonObject, "x5t"));
1117                }
1118
1119                // Get optional X.509 cert chain
1120                List<Base64> x5c = null;
1121
1122                if (jsonObject.containsKey("x5c")) {
1123                        x5c = X509CertChainUtils.parseX509CertChain(JSONObjectUtils.getJSONArray(jsonObject, "x5c"));   
1124                }
1125
1126                try {
1127                        if (d == null) {
1128                                // Public key
1129                                return new ECKey(crv, x, y, use, ops, alg, kid, x5u, x5t, x5c);
1130
1131                        } else {
1132                                // Key pair
1133                                return new ECKey(crv, x, y, d, use, ops, alg, kid, x5u, x5t, x5c);
1134                        }
1135
1136                } catch (IllegalArgumentException ex) {
1137
1138                        // Conflicting 'use' and 'key_ops'
1139                        throw new ParseException(ex.getMessage(), 0);
1140                }
1141        }
1142}