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