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