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