001/*
002 * nimbus-jose-jwt
003 *
004 * Copyright 2012-2016, Connect2id Ltd.
005 *
006 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use
007 * this file except in compliance with the License. You may obtain a copy of the
008 * License at
009 *
010 *    http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software distributed
013 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
014 * CONDITIONS OF ANY KIND, either express or implied. See the License for the
015 * specific language governing permissions and limitations under the License.
016 */
017
018package com.nimbusds.jose.jwk;
019
020
021import java.math.BigInteger;
022import java.net.URI;
023import java.security.*;
024import java.security.cert.Certificate;
025import java.security.cert.CertificateEncodingException;
026import java.security.cert.X509Certificate;
027import java.security.interfaces.ECPrivateKey;
028import java.security.interfaces.ECPublicKey;
029import java.security.spec.*;
030import java.text.ParseException;
031import java.util.*;
032
033import net.jcip.annotations.Immutable;
034import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
035
036import com.nimbusds.jose.Algorithm;
037import com.nimbusds.jose.JOSEException;
038import com.nimbusds.jose.crypto.utils.ECChecks;
039import com.nimbusds.jose.util.Base64;
040import com.nimbusds.jose.util.Base64URL;
041import com.nimbusds.jose.util.BigIntegerUtils;
042import com.nimbusds.jose.util.JSONObjectUtils;
043
044
045/**
046 * Public and private {@link KeyType#EC Elliptic Curve} JSON Web Key (JWK). 
047 * This class is immutable.
048 *
049 * <p>Supported curves:
050 *
051 * <ul>
052 *     <li>{@link Curve#P_256 P-256}
053 *     <li>{@link Curve#SECP256K1 secp256k1}
054 *     <li>{@link Curve#P_384 P-384}
055 *     <li>{@link Curve#P_521 P-512}
056 * </ul>
057 *
058 * <p>Provides EC JWK import from / export to the following standard Java
059 * interfaces and classes:
060 *
061 * <ul>
062 *     <li>{@link java.security.interfaces.ECPublicKey}
063 *     <li>{@link java.security.interfaces.ECPrivateKey}
064 *     <li>{@link java.security.PrivateKey} for an EC key in a PKCS#11 store
065 *     <li>{@link java.security.KeyPair}
066 * </ul>
067 *
068 * <p>Example JSON object representation of a public EC JWK:
069 * 
070 * <pre>
071 * {
072 *   "kty" : "EC",
073 *   "crv" : "P-256",
074 *   "x"   : "MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4",
075 *   "y"   : "4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM",
076 *   "use" : "enc",
077 *   "kid" : "1"
078 * }
079 * </pre>
080 *
081 * <p>Example JSON object representation of a private EC JWK:
082 *
083 * <pre>
084 * {
085 *   "kty" : "EC",
086 *   "crv" : "P-256",
087 *   "x"   : "MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4",
088 *   "y"   : "4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM",
089 *   "d"   : "870MB6gfuTJ4HtUnUvYMyJpr5eUZNP4Bk43bVdj3eAE",
090 *   "use" : "enc",
091 *   "kid" : "1"
092 * }
093 * </pre>
094 *
095 * <p>Use the builder to create a new EC JWK:
096 *
097 * <pre>
098 * ECKey key = new ECKey.Builder(Curve.P_256, x, y)
099 *      .keyUse(KeyUse.SIGNATURE)
100 *      .keyID("1")
101 *      .build();
102 * </pre>
103 *
104 * <p>See http://en.wikipedia.org/wiki/Elliptic_curve_cryptography
105 *
106 * @author Vladimir Dzhuvinov
107 * @author Justin Richer
108 * @version 2020-06-03
109 */
110@Immutable
111public final class ECKey extends JWK implements AsymmetricJWK, CurveBasedJWK {
112
113
114        private static final long serialVersionUID = 1L;
115        
116        
117        /**
118         * Supported EC curves.
119         */
120        public static final Set<Curve> SUPPORTED_CURVES = Collections.unmodifiableSet(
121                new HashSet<>(Arrays.asList(Curve.P_256, Curve.SECP256K1, Curve.P_384, Curve.P_521))
122        );
123
124
125        /**
126         * Builder for constructing Elliptic Curve JWKs.
127         *
128         * <p>Example usage:
129         *
130         * <pre>
131         * ECKey key = new ECKey.Builder(Curve.P521, x, y)
132         *     .d(d)
133         *     .algorithm(JWSAlgorithm.ES512)
134         *     .keyID("1")
135         *     .build();
136         * </pre>
137         */
138        public static class Builder {
139
140
141                /**
142                 * The curve name.
143                 */
144                private final Curve crv;
145
146
147                /**
148                 * The public 'x' EC coordinate.
149                 */
150                private final Base64URL x;
151
152
153                /**
154                 * The public 'y' EC coordinate.
155                 */
156                private final Base64URL y;
157                
158
159                /**
160                 * The private 'd' EC coordinate, optional.
161                 */
162                private Base64URL d;
163                
164                
165                /**
166                 * The private EC key, as PKCS#11 handle, optional.
167                 */
168                private PrivateKey priv;
169
170
171                /**
172                 * The key use, optional.
173                 */
174                private KeyUse use;
175
176
177                /**
178                 * The key operations, optional.
179                 */
180                private Set<KeyOperation> ops;
181
182
183                /**
184                 * The intended JOSE algorithm for the key, optional.
185                 */
186                private Algorithm alg;
187
188
189                /**
190                 * The key ID, optional.
191                 */
192                private String kid;
193
194
195                /**
196                 * X.509 certificate URL, optional.
197                 */
198                private URI x5u;
199
200
201                /**
202                 * X.509 certificate SHA-1 thumbprint, optional.
203                 */
204                @Deprecated
205                private Base64URL x5t;
206                
207                
208                /**
209                 * X.509 certificate SHA-256 thumbprint, optional.
210                 */
211                private Base64URL x5t256;
212
213
214                /**
215                 * The X.509 certificate chain, optional.
216                 */
217                private List<Base64> x5c;
218                
219                
220                /**
221                 * Reference to the underlying key store, {@code null} if none.
222                 */
223                private KeyStore ks;
224
225
226                /**
227                 * Creates a new Elliptic Curve JWK builder.
228                 *
229                 * @param crv The cryptographic curve. Must not be 
230                 *            {@code null}.
231                 * @param x   The public 'x' coordinate for the elliptic curve 
232                 *            point. It is represented as the Base64URL 
233                 *            encoding of the coordinate's big endian 
234                 *            representation. Must not be {@code null}.
235                 * @param y   The public 'y' coordinate for the elliptic curve 
236                 *            point. It is represented as the Base64URL 
237                 *            encoding of the coordinate's big endian 
238                 *            representation. Must not be {@code null}.
239                 */
240                public Builder(final Curve crv, final Base64URL x, final Base64URL y) {
241
242                        if (crv == null) {
243                                throw new IllegalArgumentException("The curve must not be null");
244                        }
245
246                        this.crv = crv;
247
248                        if (x == null) {
249                                throw new IllegalArgumentException("The 'x' coordinate must not be null");
250                        }
251
252                        this.x = x;
253
254                        if (y == null) {
255                                throw new IllegalArgumentException("The 'y' coordinate must not be null");
256                        }
257
258                        this.y = y;
259                }
260
261
262                /**
263                 * Creates a new Elliptic Curve JWK builder.
264                 *
265                 * @param crv The cryptographic curve. Must not be 
266                 *            {@code null}.
267                 * @param pub The public EC key to represent. Must not be 
268                 *            {@code null}.
269                 */
270                public Builder(final Curve crv, final ECPublicKey pub) {
271
272                        this(crv,
273                             encodeCoordinate(pub.getParams().getCurve().getField().getFieldSize(), pub.getW().getAffineX()),
274                             encodeCoordinate(pub.getParams().getCurve().getField().getFieldSize(), pub.getW().getAffineY()));
275                }
276                
277                
278                /**
279                 * Creates a new Elliptic Curve JWK builder.
280                 *
281                 * @param ecJWK The EC JWK to start with. Must not be
282                 *              {@code null}.
283                 */
284                public Builder(final ECKey ecJWK) {
285                        
286                        crv = ecJWK.crv;
287                        x = ecJWK.x;
288                        y = ecJWK.y;
289                        d = ecJWK.d;
290                        priv = ecJWK.privateKey;
291                        use = ecJWK.getKeyUse();
292                        ops = ecJWK.getKeyOperations();
293                        alg = ecJWK.getAlgorithm();
294                        kid = ecJWK.getKeyID();
295                        x5u = ecJWK.getX509CertURL();
296                        x5t = ecJWK.getX509CertThumbprint();
297                        x5t256 = ecJWK.getX509CertSHA256Thumbprint();
298                        x5c = ecJWK.getX509CertChain();
299                        ks = ecJWK.getKeyStore();
300                }
301
302
303                /**
304                 * Sets the private 'd' coordinate for the elliptic curve 
305                 * point. The alternative method is {@link #privateKey}.
306                 *
307                 * @param d The private 'd' coordinate. It is represented as
308                 *          the Base64URL encoding of the coordinate's big
309                 *          endian representation. {@code null} if not
310                 *          specified (for a public key).
311                 *
312                 * @return This builder.
313                 */
314                public Builder d(final Base64URL d) {
315
316                        this.d = d;
317                        return this;
318                }
319
320
321                /**
322                 * Sets the private Elliptic Curve key. The alternative method 
323                 * is {@link #d}.
324                 *
325                 * @param priv The private EC key, used to obtain the private
326                 *             'd' coordinate for the elliptic curve point.
327                 *             {@code null} if not specified (for a public 
328                 *             key).
329                 *
330                 * @return This builder.
331                 */
332                public Builder privateKey(final ECPrivateKey priv) {
333
334                        if (priv != null) {
335                                this.d = encodeCoordinate(priv.getParams().getCurve().getField().getFieldSize(), priv.getS());
336                        }
337                        
338                        return this;
339                }
340                
341                
342                /**
343                 * Sets the private EC key, typically for a key located in a
344                 * PKCS#11 store that doesn't expose the private key parameters
345                 * (such as a smart card or HSM).
346                 *
347                 * @param priv The private EC key reference. Its algorithm must
348                 *             be "EC". Must not be {@code null}.
349                 *
350                 * @return This builder.
351                 */
352                public Builder privateKey(final PrivateKey priv) {
353
354                        if (priv instanceof ECPrivateKey) {
355                                return privateKey((ECPrivateKey) priv);
356                        }
357
358                        if (! "EC".equalsIgnoreCase(priv.getAlgorithm())) {
359                                throw new IllegalArgumentException("The private key algorithm must be EC");
360                        }
361                        
362                        this.priv = priv;
363                        return this;
364                }
365
366
367                /**
368                 * Sets the use ({@code use}) of the JWK.
369                 *
370                 * @param use The key use, {@code null} if not specified or if 
371                 *            the key is intended for signing as well as 
372                 *            encryption.
373                 *
374                 * @return This builder.
375                 */
376                public Builder keyUse(final KeyUse use) {
377
378                        this.use = use;
379                        return this;
380                }
381
382
383                /**
384                 * Sets the operations ({@code key_ops}) of the JWK.
385                 *
386                 * @param ops The key operations, {@code null} if not
387                 *            specified.
388                 *
389                 * @return This builder.
390                 */
391                public Builder keyOperations(final Set<KeyOperation> ops) {
392
393                        this.ops = ops;
394                        return this;
395                }
396
397
398                /**
399                 * Sets the intended JOSE algorithm ({@code alg}) for the JWK.
400                 *
401                 * @param alg The intended JOSE algorithm, {@code null} if not 
402                 *            specified.
403                 *
404                 * @return This builder.
405                 */
406                public Builder algorithm(final Algorithm alg) {
407
408                        this.alg = alg;
409                        return this;
410                }
411
412                /**
413                 * Sets the ID ({@code kid}) of the JWK. The key ID can be used 
414                 * to match a specific key. This can be used, for instance, to 
415                 * choose a key within a {@link JWKSet} during key rollover. 
416                 * The key ID may also correspond to a JWS/JWE {@code kid} 
417                 * header parameter value.
418                 *
419                 * @param kid The key ID, {@code null} if not specified.
420                 *
421                 * @return This builder.
422                 */
423                public Builder keyID(final String kid) {
424
425                        this.kid = kid;
426                        return this;
427                }
428
429
430                /**
431                 * Sets the ID ({@code kid}) of the JWK to its SHA-256 JWK
432                 * thumbprint (RFC 7638). The key ID can be used to match a
433                 * specific key. This can be used, for instance, to choose a
434                 * key within a {@link JWKSet} during key rollover. The key ID
435                 * may also correspond to a JWS/JWE {@code kid} header
436                 * parameter value.
437                 *
438                 * @return This builder.
439                 *
440                 * @throws JOSEException If the SHA-256 hash algorithm is not
441                 *                       supported.
442                 */
443                public Builder keyIDFromThumbprint()
444                        throws JOSEException {
445
446                        return keyIDFromThumbprint("SHA-256");
447                }
448
449
450                /**
451                 * Sets the ID ({@code kid}) of the JWK to its JWK thumbprint
452                 * (RFC 7638). The key ID can be used to match a specific key.
453                 * This can be used, for instance, to choose a key within a
454                 * {@link JWKSet} during key rollover. The key ID may also
455                 * correspond to a JWS/JWE {@code kid} header parameter value.
456                 *
457                 * @param hashAlg The hash algorithm for the JWK thumbprint
458                 *                computation. Must not be {@code null}.
459                 *
460                 * @return This builder.
461                 *
462                 * @throws JOSEException If the hash algorithm is not
463                 *                       supported.
464                 */
465                public Builder keyIDFromThumbprint(final String hashAlg)
466                        throws JOSEException {
467
468                        // Put mandatory params in sorted order
469                        LinkedHashMap<String,String> requiredParams = new LinkedHashMap<>();
470                        requiredParams.put("crv", crv.toString());
471                        requiredParams.put("kty", KeyType.EC.getValue());
472                        requiredParams.put("x", x.toString());
473                        requiredParams.put("y", y.toString());
474                        this.kid = ThumbprintUtils.compute(hashAlg, requiredParams).toString();
475                        return this;
476                }
477
478
479                /**
480                 * Sets the X.509 certificate URL ({@code x5u}) of the JWK.
481                 *
482                 * @param x5u The X.509 certificate URL, {@code null} if not 
483                 *            specified.
484                 *
485                 * @return This builder.
486                 */
487                public Builder x509CertURL(final URI x5u) {
488
489                        this.x5u = x5u;
490                        return this;
491                }
492
493
494                /**
495                 * Sets the X.509 certificate SHA-1 thumbprint ({@code x5t}) of
496                 * the JWK.
497                 *
498                 * @param x5t The X.509 certificate SHA-1 thumbprint,
499                 *            {@code null} if not specified.
500                 *
501                 * @return This builder.
502                 */
503                @Deprecated
504                public Builder x509CertThumbprint(final Base64URL x5t) {
505
506                        this.x5t = x5t;
507                        return this;
508                }
509
510
511                /**
512                 * Sets the X.509 certificate SHA-256 thumbprint
513                 * ({@code x5t#S256}) of the JWK.
514                 *
515                 * @param x5t256 The X.509 certificate SHA-256 thumbprint,
516                 *               {@code null} if not specified.
517                 *
518                 * @return This builder.
519                 */
520                public Builder x509CertSHA256Thumbprint(final Base64URL x5t256) {
521
522                        this.x5t256 = x5t256;
523                        return this;
524                }
525
526
527                /**
528                 * Sets the X.509 certificate chain ({@code x5c}) of the JWK.
529                 *
530                 * @param x5c The X.509 certificate chain as a unmodifiable 
531                 *            list, {@code null} if not specified.
532                 *
533                 * @return This builder.
534                 */
535                public Builder x509CertChain(final List<Base64> x5c) {
536
537                        this.x5c = x5c;
538                        return this;
539                }
540                
541                
542                /**
543                 * Sets the underlying key store.
544                 *
545                 * @param keyStore Reference to the underlying key store,
546                 *                 {@code null} if none.
547                 *
548                 * @return This builder.
549                 */
550                public Builder keyStore(final KeyStore keyStore) {
551                        
552                        this.ks = keyStore;
553                        return this;
554                }
555
556
557                /**
558                 * Builds a new Elliptic Curve JWK.
559                 *
560                 * @return The Elliptic Curve JWK.
561                 *
562                 * @throws IllegalStateException If the JWK parameters were
563                 *                               inconsistently specified.
564                 */
565                public ECKey build() {
566
567                        try {
568                                if (d == null && priv == null) {
569                                        // Public key
570                                        return new ECKey(crv, x, y, use, ops, alg, kid, x5u, x5t, x5t256, x5c, ks);
571                                }
572                                
573                                if (priv != null) {
574                                        // PKCS#11 reference to private key
575                                        return new ECKey(crv, x, y, priv, use, ops, alg, kid, x5u, x5t, x5t256, x5c, ks);
576                                }
577
578                                // Public / private key pair with 'd'
579                                return new ECKey(crv, x, y, d, use, ops, alg, kid, x5u, x5t, x5t256, x5c, ks);
580
581                        } catch (IllegalArgumentException e) {
582                                throw new IllegalStateException(e.getMessage(), e);
583                        }
584                }
585        }
586
587
588        /**
589         * Returns the Base64URL encoding of the specified elliptic curve 'x',
590         * 'y' or 'd' coordinate, with leading zero padding up to the specified
591         * field size in bits.
592         *
593         * @param fieldSize  The field size in bits.
594         * @param coordinate The elliptic curve coordinate. Must not be
595         *                   {@code null}.
596         *
597         * @return The Base64URL-encoded coordinate, with leading zero padding
598         *         up to the curve's field size.
599         */
600        public static Base64URL encodeCoordinate(final int fieldSize, final BigInteger coordinate) {
601
602                final byte[] notPadded = BigIntegerUtils.toBytesUnsigned(coordinate);
603
604                int bytesToOutput = (fieldSize + 7)/8;
605
606                if (notPadded.length >= bytesToOutput) {
607                        // Greater-than check to prevent exception on malformed
608                        // key below
609                        return Base64URL.encode(notPadded);
610                }
611
612                final byte[] padded = new byte[bytesToOutput];
613
614                System.arraycopy(notPadded, 0, padded, bytesToOutput - notPadded.length, notPadded.length);
615
616                return Base64URL.encode(padded);
617        }
618
619
620        /**
621         * The curve name.
622         */
623        private final Curve crv;
624
625
626        /**
627         * The public 'x' EC coordinate.
628         */
629        private final Base64URL x;
630
631
632        /**
633         * The public 'y' EC coordinate.
634         */
635        private final Base64URL y;
636        
637
638        /**
639         * The private 'd' EC coordinate.
640         */
641        private final Base64URL d;
642        
643        
644        /**
645         * Private PKCS#11 key handle.
646         */
647        private final PrivateKey privateKey;
648        
649        
650        /**
651         * Ensures the specified 'x' and 'y' public coordinates are on the
652         * given curve.
653         *
654         * @param crv The curve. Must not be {@code null}.
655         * @param x   The public 'x' coordinate. Must not be {@code null}.
656         * @param y   The public 'y' coordinate. Must not be {@code null}.
657         */
658        private static void ensurePublicCoordinatesOnCurve(final Curve crv, final Base64URL x, final Base64URL y) {
659                
660                if (! SUPPORTED_CURVES.contains(crv)) {
661                        throw new IllegalArgumentException("Unknown / unsupported curve: " + crv);
662                }
663                
664                if (! ECChecks.isPointOnCurve(x.decodeToBigInteger(), y.decodeToBigInteger(), crv.toECParameterSpec())) {
665                        throw new IllegalArgumentException("Invalid EC JWK: The 'x' and 'y' public coordinates are not on the " + crv + " curve");
666                }
667        }
668
669
670        /**
671         * Creates a new public Elliptic Curve JSON Web Key (JWK) with the 
672         * specified parameters.
673         *
674         * @param crv    The cryptographic curve. Must not be {@code null}.
675         * @param x      The public 'x' coordinate for the elliptic curve
676         *               point. It is represented as the Base64URL encoding of
677         *               the coordinate's big endian representation. Must not
678         *               be {@code null}.
679         * @param y      The public 'y' coordinate for the elliptic curve
680         *               point. It is represented as the Base64URL encoding of
681         *               the coordinate's big endian representation. Must not
682         *               be {@code null}.
683         * @param use    The key use, {@code null} if not specified or if the
684         *               key is intended for signing as well as encryption.
685         * @param ops    The key operations, {@code null} if not specified.
686         * @param alg    The intended JOSE algorithm for the key, {@code null}
687         *               if not specified.
688         * @param kid    The key ID, {@code null} if not specified.
689         * @param x5u    The X.509 certificate URL, {@code null} if not
690         *               specified.
691         * @param x5t    The X.509 certificate SHA-1 thumbprint, {@code null}
692         *               if not specified.
693         * @param x5t256 The X.509 certificate SHA-256 thumbprint, {@code null}
694         *               if not specified.
695         * @param x5c    The X.509 certificate chain, {@code null} if not
696         *               specified.
697         * @param ks     Reference to the underlying key store, {@code null} if
698         *               not specified.
699         */
700        public ECKey(final Curve crv, final Base64URL x, final Base64URL y, 
701                     final KeyUse use, final Set<KeyOperation> ops, final Algorithm alg, final String kid,
702                     final URI x5u, final Base64URL x5t, final Base64URL x5t256, final List<Base64> x5c,
703                     final KeyStore ks) {
704
705                super(KeyType.EC, use, ops, alg, kid, x5u, x5t, x5t256, x5c, ks);
706
707                if (crv == null) {
708                        throw new IllegalArgumentException("The curve must not be null");
709                }
710
711                this.crv = crv;
712
713                if (x == null) {
714                        throw new IllegalArgumentException("The 'x' coordinate must not be null");
715                }
716
717                this.x = x;
718
719                if (y == null) {
720                        throw new IllegalArgumentException("The 'y' coordinate must not be null");
721                }
722
723                this.y = y;
724                
725                ensurePublicCoordinatesOnCurve(crv, x, y);
726                
727                ensureMatches(getParsedX509CertChain());
728
729                this.d = null;
730                
731                this.privateKey = null;
732        }
733
734
735        /**
736         * Creates a new public / private Elliptic Curve JSON Web Key (JWK) 
737         * with the specified parameters.
738         *
739         * @param crv    The cryptographic curve. Must not be {@code null}.
740         * @param x      The public 'x' coordinate for the elliptic curve
741         *               point. It is represented as the Base64URL encoding of
742         *               the coordinate's big endian representation. Must not
743         *               be {@code null}.
744         * @param y      The public 'y' coordinate for the elliptic curve
745         *               point. It is represented as the Base64URL encoding of
746         *               the coordinate's big endian representation. Must not
747         *               be {@code null}.
748         * @param d      The private 'd' coordinate for the elliptic curve
749         *               point. It is represented as the Base64URL encoding of
750         *               the coordinate's big endian representation. Must not
751         *               be {@code null}.
752         * @param use    The key use, {@code null} if not specified or if the
753         *               key is intended for signing as well as encryption.
754         * @param ops    The key operations, {@code null} if not specified.
755         * @param alg    The intended JOSE algorithm for the key, {@code null}
756         *               if not specified.
757         * @param kid    The key ID, {@code null} if not specified.
758         * @param x5u    The X.509 certificate URL, {@code null} if not
759         *               specified.
760         * @param x5t    The X.509 certificate SHA-1 thumbprint, {@code null}
761         *               if not specified.
762         * @param x5t256 The X.509 certificate SHA-256 thumbprint, {@code null}
763         *               if not specified.
764         * @param x5c    The X.509 certificate chain, {@code null} if not
765         *               specified.
766         * @param ks     Reference to the underlying key store, {@code null} if
767         *               not specified.
768         */
769        public ECKey(final Curve crv, final Base64URL x, final Base64URL y, final Base64URL d,
770                     final KeyUse use, final Set<KeyOperation> ops, final Algorithm alg, final String kid,
771                     final URI x5u, final Base64URL x5t, final Base64URL x5t256, final List<Base64> x5c,
772                     final KeyStore ks) {
773
774                super(KeyType.EC, use, ops, alg, kid, x5u, x5t, x5t256, x5c, ks);
775                
776                if (crv == null) {
777                        throw new IllegalArgumentException("The curve must not be null");
778                }
779
780                this.crv = crv;
781
782                if (x == null) {
783                        throw new IllegalArgumentException("The 'x' coordinate must not be null");
784                }
785
786                this.x = x;
787
788                if (y == null) {
789                        throw new IllegalArgumentException("The 'y' coordinate must not be null");
790                }
791
792                this.y = y;
793                
794                ensurePublicCoordinatesOnCurve(crv, x, y);
795                
796                ensureMatches(getParsedX509CertChain());
797                
798                if (d == null) {
799                        throw new IllegalArgumentException("The 'd' coordinate must not be null");
800                }
801
802                this.d = d;
803                
804                this.privateKey = null;
805        }
806
807
808        /**
809         * Creates a new public / private Elliptic Curve JSON Web Key (JWK)
810         * with the specified parameters. The private key is specified by its
811         * PKCS#11 handle.
812         *
813         * @param crv    The cryptographic curve. Must not be {@code null}.
814         * @param x      The public 'x' coordinate for the elliptic curve
815         *               point. It is represented as the Base64URL encoding of
816         *               the coordinate's big endian representation. Must not
817         *               be {@code null}.
818         * @param y      The public 'y' coordinate for the elliptic curve
819         *               point. It is represented as the Base64URL encoding of
820         *               the coordinate's big endian representation. Must not
821         *               be {@code null}.
822         * @param priv   The private key as a PKCS#11 handle, {@code null} if
823         *               not specified.
824         * @param use    The key use, {@code null} if not specified or if the
825         *               key is intended for signing as well as encryption.
826         * @param ops    The key operations, {@code null} if not specified.
827         * @param alg    The intended JOSE algorithm for the key, {@code null}
828         *               if not specified.
829         * @param kid    The key ID, {@code null} if not specified.
830         * @param x5u    The X.509 certificate URL, {@code null} if not
831         *               specified.
832         * @param x5t    The X.509 certificate SHA-1 thumbprint, {@code null}
833         *               if not specified.
834         * @param x5t256 The X.509 certificate SHA-256 thumbprint, {@code null}
835         *               if not specified.
836         * @param x5c    The X.509 certificate chain, {@code null} if not
837         *               specified.
838         */
839        public ECKey(final Curve crv, final Base64URL x, final Base64URL y, final PrivateKey priv,
840                     final KeyUse use, final Set<KeyOperation> ops, final Algorithm alg, final String kid,
841                     final URI x5u, final Base64URL x5t, final Base64URL x5t256, final List<Base64> x5c,
842                     final KeyStore ks) {
843
844                super(KeyType.EC, use, ops, alg, kid, x5u, x5t, x5t256, x5c, ks);
845
846                if (crv == null) {
847                        throw new IllegalArgumentException("The curve must not be null");
848                }
849
850                this.crv = crv;
851
852                if (x == null) {
853                        throw new IllegalArgumentException("The 'x' coordinate must not be null");
854                }
855
856                this.x = x;
857
858                if (y == null) {
859                        throw new IllegalArgumentException("The 'y' coordinate must not be null");
860                }
861
862                this.y = y;
863                
864                ensurePublicCoordinatesOnCurve(crv, x, y);
865                
866                ensureMatches(getParsedX509CertChain());
867                
868                d = null;
869                
870                this.privateKey = priv;
871        }
872
873
874        /**
875         * Creates a new public Elliptic Curve JSON Web Key (JWK) with the 
876         * specified parameters.
877         *
878         * @param crv    The cryptographic curve. Must not be {@code null}.
879         * @param pub    The public EC key to represent. Must not be
880         *               {@code null}.
881         * @param use    The key use, {@code null} if not specified or if the
882         *               key is intended for signing as well as encryption.
883         * @param ops    The key operations, {@code null} if not specified.
884         * @param alg    The intended JOSE algorithm for the key, {@code null}
885         *               if not specified.
886         * @param kid    The key ID, {@code null} if not specified.
887         * @param x5u    The X.509 certificate URL, {@code null} if not
888         *               specified.
889         * @param x5t    The X.509 certificate SHA-1 thumbprint, {@code null}
890         *               if not specified.
891         * @param x5t256 The X.509 certificate SHA-256 thumbprint, {@code null}
892         *               if not specified.
893         * @param x5c    The X.509 certificate chain, {@code null} if not
894         *               specified.
895         * @param ks     Reference to the underlying key store, {@code null} if
896         *               not specified.
897         */
898        public ECKey(final Curve crv, final ECPublicKey pub, 
899                     final KeyUse use, final Set<KeyOperation> ops, final Algorithm alg, final String kid,
900                     final URI x5u, final Base64URL x5t, final Base64URL x5t256, final List<Base64> x5c,
901                     final KeyStore ks) {
902
903                this(crv, 
904                     encodeCoordinate(pub.getParams().getCurve().getField().getFieldSize(), pub.getW().getAffineX()),
905                     encodeCoordinate(pub.getParams().getCurve().getField().getFieldSize(), pub.getW().getAffineY()),
906                     use, ops, alg, kid,
907                     x5u, x5t, x5t256, x5c,
908                     ks);
909        }
910
911
912        /**
913         * Creates a new public / private Elliptic Curve JSON Web Key (JWK) 
914         * with the specified parameters.
915         *
916         * @param crv    The cryptographic curve. Must not be {@code null}.
917         * @param pub    The public EC key to represent. Must not be
918         *               {@code null}.
919         * @param priv   The private EC key to represent. Must not be
920         *               {@code null}.
921         * @param use    The key use, {@code null} if not specified or if the
922         *               key is intended for signing as well as encryption.
923         * @param ops    The key operations, {@code null} if not specified.
924         * @param alg    The intended JOSE algorithm for the key, {@code null}
925         *               if not specified.
926         * @param kid    The key ID, {@code null} if not specified.
927         * @param x5u    The X.509 certificate URL, {@code null} if not
928         *               specified.
929         * @param x5t    The X.509 certificate SHA-1 thumbprint, {@code null}
930         *               if not specified.
931         * @param x5t256 The X.509 certificate SHA-256 thumbprint, {@code null}
932         *               if not specified.
933         * @param x5c    The X.509 certificate chain, {@code null} if not
934         *               specified.
935         * @param ks     Reference to the underlying key store, {@code null} if
936         *               not specified.
937         */
938        public ECKey(final Curve crv, final ECPublicKey pub, final ECPrivateKey priv, 
939                     final KeyUse use, final Set<KeyOperation> ops, final Algorithm alg, final String kid,
940                     final URI x5u, final Base64URL x5t, final Base64URL x5t256, final List<Base64> x5c,
941                     final KeyStore ks) {
942
943                this(crv,
944                     encodeCoordinate(pub.getParams().getCurve().getField().getFieldSize(), pub.getW().getAffineX()),
945                     encodeCoordinate(pub.getParams().getCurve().getField().getFieldSize(), pub.getW().getAffineY()),
946                     encodeCoordinate(priv.getParams().getCurve().getField().getFieldSize(), priv.getS()),
947                     use, ops, alg, kid,
948                     x5u, x5t, x5t256, x5c,
949                     ks);
950        }
951
952
953        /**
954         * Creates a new public / private Elliptic Curve JSON Web Key (JWK)
955         * with the specified parameters. The private key is specified by its
956         * PKCS#11 handle.
957         *
958         * @param crv    The cryptographic curve. Must not be {@code null}.
959         * @param pub    The public EC key to represent. Must not be
960         *               {@code null}.
961         * @param priv   The private key as a PKCS#11 handle, {@code null} if
962         *               not specified.
963         * @param use    The key use, {@code null} if not specified or if the
964         *               key is intended for signing as well as encryption.
965         * @param ops    The key operations, {@code null} if not specified.
966         * @param alg    The intended JOSE algorithm for the key, {@code null}
967         *               if not specified.
968         * @param kid    The key ID, {@code null} if not specified.
969         * @param x5u    The X.509 certificate URL, {@code null} if not
970         *               specified.
971         * @param x5t    The X.509 certificate SHA-1 thumbprint, {@code null}
972         *               if not specified.
973         * @param x5t256 The X.509 certificate SHA-256 thumbprint, {@code null}
974         *               if not specified.
975         * @param x5c    The X.509 certificate chain, {@code null} if not
976         *               specified.
977         * @param ks     Reference to the underlying key store, {@code null} if
978         *               not specified.
979         */
980        public ECKey(final Curve crv, final ECPublicKey pub, final PrivateKey priv,
981                     final KeyUse use, final Set<KeyOperation> ops, final Algorithm alg, final String kid,
982                     final URI x5u, final Base64URL x5t, final Base64URL x5t256, final List<Base64> x5c,
983                     final KeyStore ks) {
984                
985                this(
986                        crv,
987                        encodeCoordinate(pub.getParams().getCurve().getField().getFieldSize(), pub.getW().getAffineX()),
988                        encodeCoordinate(pub.getParams().getCurve().getField().getFieldSize(), pub.getW().getAffineY()),
989                        priv,
990                        use, ops, alg, kid, x5u, x5t, x5t256, x5c,
991                        ks);
992        }
993
994
995        @Override
996        public Curve getCurve() {
997
998                return crv;
999        }
1000
1001
1002        /**
1003         * Gets the public 'x' coordinate for the elliptic curve point.
1004         *
1005         * @return The 'x' coordinate. It is represented as the Base64URL 
1006         *         encoding of the coordinate's big endian representation.
1007         */
1008        public Base64URL getX() {
1009
1010                return x;
1011        }
1012
1013
1014        /**
1015         * Gets the public 'y' coordinate for the elliptic curve point.
1016         *
1017         * @return The 'y' coordinate. It is represented as the Base64URL 
1018         *         encoding of the coordinate's big endian representation.
1019         */
1020        public Base64URL getY() {
1021
1022                return y;
1023        }
1024
1025        
1026        /**
1027         * Gets the private 'd' coordinate for the elliptic curve point. It is 
1028         * represented as the Base64URL encoding of the coordinate's big endian 
1029         * representation.
1030         *
1031         * @return The 'd' coordinate.  It is represented as the Base64URL 
1032         *         encoding of the coordinate's big endian representation. 
1033         *         {@code null} if not specified (for a public key).
1034         */
1035        public Base64URL getD() {
1036
1037                return d;
1038        }
1039
1040
1041        /**
1042         * Returns a standard {@code java.security.interfaces.ECPublicKey} 
1043         * representation of this Elliptic Curve JWK. Uses the default JCA
1044         * provider.
1045         * 
1046         * @return The public Elliptic Curve key.
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 EC key.
1051         */
1052        public ECPublicKey toECPublicKey()
1053                throws JOSEException {
1054
1055                return toECPublicKey(null);
1056        }
1057
1058
1059        /**
1060         * Returns a standard {@code java.security.interfaces.ECPublicKey}
1061         * representation of this Elliptic Curve JWK.
1062         *
1063         * @param provider The specific JCA provider to use, {@code null}
1064         *                 implies the default one.
1065         *
1066         * @return The public Elliptic Curve key.
1067         *
1068         * @throws JOSEException If EC is not supported by the underlying Java
1069         *                       Cryptography (JCA) provider or if the JWK
1070         *                       parameters are invalid for a public EC key.
1071         */
1072        public ECPublicKey toECPublicKey(final Provider provider)
1073                throws JOSEException {
1074
1075                ECParameterSpec spec = crv.toECParameterSpec();
1076
1077                if (spec == null) {
1078                        throw new JOSEException("Couldn't get EC parameter spec for curve " + crv);
1079                }
1080
1081                ECPoint w = new ECPoint(x.decodeToBigInteger(), y.decodeToBigInteger());
1082
1083                ECPublicKeySpec publicKeySpec = new ECPublicKeySpec(w, spec);
1084
1085                try {
1086                        KeyFactory keyFactory;
1087
1088                        if (provider == null) {
1089                                keyFactory = KeyFactory.getInstance("EC");
1090                        } else {
1091                                keyFactory = KeyFactory.getInstance("EC", provider);
1092                        }
1093
1094                        return (ECPublicKey) keyFactory.generatePublic(publicKeySpec);
1095
1096                } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
1097
1098                        throw new JOSEException(e.getMessage(), e);
1099                }
1100        }
1101        
1102
1103        /**
1104         * Returns a standard {@code java.security.interfaces.ECPrivateKey} 
1105         * representation of this Elliptic Curve JWK. Uses the default JCA
1106         * provider.
1107         * 
1108         * @return The private Elliptic Curve key, {@code null} if not 
1109         *         specified by this JWK.
1110         * 
1111         * @throws JOSEException If EC is not supported by the underlying Java
1112         *                       Cryptography (JCA) provider or if the JWK
1113         *                       parameters are invalid for a private EC key.
1114         */
1115        public ECPrivateKey toECPrivateKey()
1116                throws JOSEException {
1117
1118                return toECPrivateKey(null);
1119        }
1120
1121
1122        /**
1123         * Returns a standard {@code java.security.interfaces.ECPrivateKey}
1124         * representation of this Elliptic Curve JWK.
1125         *
1126         * @param provider The specific JCA provider to use, {@code null}
1127         *                 implies the default one.
1128         *
1129         * @return The private Elliptic Curve key, {@code null} if not
1130         *         specified by this JWK.
1131         *
1132         * @throws JOSEException If EC is not supported by the underlying Java
1133         *                       Cryptography (JCA) provider or if the JWK
1134         *                       parameters are invalid for a private EC key.
1135         */
1136        public ECPrivateKey toECPrivateKey(final Provider provider)
1137                throws JOSEException {
1138
1139                if (d == null) {
1140                        // No private 'd' param
1141                        return null;
1142                }
1143
1144                ECParameterSpec spec = crv.toECParameterSpec();
1145
1146                if (spec == null) {
1147                        throw new JOSEException("Couldn't get EC parameter spec for curve " + crv);
1148                }
1149
1150                ECPrivateKeySpec privateKeySpec = new ECPrivateKeySpec(d.decodeToBigInteger(), spec);
1151
1152                try {
1153                        KeyFactory keyFactory;
1154
1155                        if (provider == null) {
1156                                keyFactory = KeyFactory.getInstance("EC");
1157                        } else {
1158                                keyFactory = KeyFactory.getInstance("EC", provider);
1159                        }
1160
1161                        return (ECPrivateKey) keyFactory.generatePrivate(privateKeySpec);
1162
1163                } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
1164
1165                        throw new JOSEException(e.getMessage(), e);
1166                }
1167        }
1168
1169
1170        @Override
1171        public PublicKey toPublicKey()
1172                throws JOSEException {
1173
1174                return toECPublicKey();
1175        }
1176
1177
1178        @Override
1179        public PrivateKey toPrivateKey()
1180                throws JOSEException {
1181                
1182                PrivateKey prv = toECPrivateKey();
1183                
1184                if (prv != null) {
1185                        // Return private EC key with key material
1186                        return prv;
1187                }
1188                
1189                // Return private EC key as PKCS#11 handle, or null
1190                return privateKey;
1191        }
1192        
1193
1194        /**
1195         * Returns a standard {@code java.security.KeyPair} representation of 
1196         * this Elliptic Curve JWK. Uses the default JCA provider.
1197         * 
1198         * @return The Elliptic Curve key pair. The private Elliptic Curve key 
1199         *         will be {@code null} if not specified.
1200         * 
1201         * @throws JOSEException If EC is not supported by the underlying Java
1202         *                       Cryptography (JCA) provider or if the JWK
1203         *                       parameters are invalid for a public and / or
1204         *                       private EC key.
1205         */
1206        @Override
1207        public KeyPair toKeyPair()
1208                throws JOSEException {
1209
1210                return toKeyPair(null);
1211        }
1212
1213
1214        /**
1215         * Returns a standard {@code java.security.KeyPair} representation of
1216         * this Elliptic Curve JWK.
1217         *
1218         * @param provider The specific JCA provider to use, {@code null}
1219         *                 implies the default one.
1220         *
1221         * @return The Elliptic Curve key pair. The private Elliptic Curve key
1222         *         will be {@code null} if not specified.
1223         *
1224         * @throws JOSEException If EC is not supported by the underlying Java
1225         *                       Cryptography (JCA) provider or if the JWK
1226         *                       parameters are invalid for a public and / or
1227         *                       private EC key.
1228         */
1229        public KeyPair toKeyPair(final Provider provider)
1230                throws JOSEException {
1231
1232                if (privateKey != null) {
1233                        // Private key as PKCS#11 handle
1234                        return new KeyPair(toECPublicKey(provider), privateKey);
1235                } else {
1236                        return new KeyPair(toECPublicKey(provider), toECPrivateKey(provider));
1237                }
1238        }
1239        
1240        
1241        @Override
1242        public boolean matches(final X509Certificate cert) {
1243                
1244                ECPublicKey certECKey;
1245                try {
1246                        certECKey = (ECPublicKey) getParsedX509CertChain().get(0).getPublicKey();
1247                } catch (ClassCastException ex) {
1248                        return false;
1249                }
1250                // Compare Big Ints, base64url encoding may have padding!
1251                // https://tools.ietf.org/html/rfc7518#section-6.2.1.2
1252                if (! getX().decodeToBigInteger().equals(certECKey.getW().getAffineX())) {
1253                        return false;
1254                }
1255                return getY().decodeToBigInteger().equals(certECKey.getW().getAffineY());
1256        }
1257        
1258        
1259        /**
1260         * Calls {@link #matches(X509Certificate)} for the first X.509
1261         * certificate in the specified chain.
1262         *
1263         * @param chain The X.509 certificate chain, {@code null} if not
1264         *              specified.
1265         *
1266         * @throws IllegalArgumentException If a certificate chain is specified
1267         *                                  and the first certificate in it
1268         *                                  doesn't match.
1269         */
1270        private void ensureMatches(final List<X509Certificate> chain) {
1271                
1272                if (chain == null)
1273                        return;
1274                
1275                if (! matches(chain.get(0)))
1276                        throw new IllegalArgumentException("The public subject key info of the first X.509 certificate in the chain must match the JWK type and public parameters");
1277        }
1278        
1279        
1280        @Override
1281        public LinkedHashMap<String,?> getRequiredParams() {
1282
1283                // Put mandatory params in sorted order
1284                LinkedHashMap<String,String> requiredParams = new LinkedHashMap<>();
1285                requiredParams.put("crv", crv.toString());
1286                requiredParams.put("kty", getKeyType().getValue());
1287                requiredParams.put("x", x.toString());
1288                requiredParams.put("y", y.toString());
1289                return requiredParams;
1290        }
1291
1292
1293        @Override
1294        public boolean isPrivate() {
1295
1296                return d != null || privateKey != null;
1297        }
1298
1299
1300        @Override
1301        public int size() {
1302
1303                ECParameterSpec ecParameterSpec = crv.toECParameterSpec();
1304
1305                if (ecParameterSpec == null) {
1306                        throw new UnsupportedOperationException("Couldn't determine field size for curve " + crv.getName());
1307                }
1308
1309                return ecParameterSpec.getCurve().getField().getFieldSize();
1310        }
1311
1312        
1313        /**
1314         * Returns a copy of this Elliptic Curve JWK with any private values 
1315         * removed.
1316         *
1317         * @return The copied public Elliptic Curve JWK.
1318         */
1319        @Override
1320        public ECKey toPublicJWK() {
1321
1322                return new ECKey(
1323                        getCurve(), getX(), getY(),
1324                        getKeyUse(), getKeyOperations(), getAlgorithm(), getKeyID(),
1325                        getX509CertURL(), getX509CertThumbprint(), getX509CertSHA256Thumbprint(), getX509CertChain(),
1326                        getKeyStore());
1327        }
1328        
1329
1330        @Override
1331        public Map<String, Object> toJSONObject() {
1332
1333                Map<String, Object> o = super.toJSONObject();
1334
1335                // Append EC specific attributes
1336                o.put("crv", crv.toString());
1337                o.put("x", x.toString());
1338                o.put("y", y.toString());
1339
1340                if (d != null) {
1341                        o.put("d", d.toString());
1342                }
1343                
1344                return o;
1345        }
1346
1347
1348        /**
1349         * Parses a public / private Elliptic Curve JWK from the specified JSON
1350         * object string representation.
1351         *
1352         * @param s The JSON object string to parse. Must not be {@code null}.
1353         *
1354         * @return The public / private Elliptic Curve JWK.
1355         *
1356         * @throws ParseException If the string couldn't be parsed to an
1357         *                        Elliptic Curve JWK.
1358         */
1359        public static ECKey parse(final String s)
1360                throws ParseException {
1361
1362                return parse(JSONObjectUtils.parse(s));
1363        }
1364
1365
1366        /**
1367         * Parses a public / private Elliptic Curve JWK from the specified JSON
1368         * object representation.
1369         *
1370         * @param jsonObject The JSON object to parse. Must not be 
1371         *                   {@code null}.
1372         *
1373         * @return The public / private Elliptic Curve JWK.
1374         *
1375         * @throws ParseException If the JSON object couldn't be parsed to an 
1376         *                        Elliptic Curve JWK.
1377         */
1378        public static ECKey parse(final Map<String, Object> jsonObject)
1379                throws ParseException {
1380
1381                // Check key type
1382                if (! KeyType.EC.equals(JWKMetadata.parseKeyType(jsonObject))) {
1383                        throw new ParseException("The key type \"kty\" must be EC", 0);
1384                }
1385                
1386                // Parse the mandatory public key parameters
1387                Curve crv;
1388                try {
1389                        crv = Curve.parse(JSONObjectUtils.getString(jsonObject, "crv"));
1390                } catch (IllegalArgumentException e) {
1391                        throw new ParseException(e.getMessage(), 0);
1392                }
1393                
1394                Base64URL x = JSONObjectUtils.getBase64URL(jsonObject, "x");
1395                Base64URL y = JSONObjectUtils.getBase64URL(jsonObject, "y");
1396
1397                // Get optional private key
1398                Base64URL d = JSONObjectUtils.getBase64URL(jsonObject, "d");
1399                
1400                try {
1401                        if (d == null) {
1402                                // Public key
1403                                return new ECKey(crv, x, y,
1404                                        JWKMetadata.parseKeyUse(jsonObject),
1405                                        JWKMetadata.parseKeyOperations(jsonObject),
1406                                        JWKMetadata.parseAlgorithm(jsonObject),
1407                                        JWKMetadata.parseKeyID(jsonObject),
1408                                        JWKMetadata.parseX509CertURL(jsonObject),
1409                                        JWKMetadata.parseX509CertThumbprint(jsonObject),
1410                                        JWKMetadata.parseX509CertSHA256Thumbprint(jsonObject),
1411                                        JWKMetadata.parseX509CertChain(jsonObject),
1412                                        null);
1413
1414                        } else {
1415                                // Key pair
1416                                return new ECKey(crv, x, y, d,
1417                                        JWKMetadata.parseKeyUse(jsonObject),
1418                                        JWKMetadata.parseKeyOperations(jsonObject),
1419                                        JWKMetadata.parseAlgorithm(jsonObject),
1420                                        JWKMetadata.parseKeyID(jsonObject),
1421                                        JWKMetadata.parseX509CertURL(jsonObject),
1422                                        JWKMetadata.parseX509CertThumbprint(jsonObject),
1423                                        JWKMetadata.parseX509CertSHA256Thumbprint(jsonObject),
1424                                        JWKMetadata.parseX509CertChain(jsonObject),
1425                                        null);
1426                        }
1427
1428                } catch (IllegalArgumentException ex) {
1429
1430                        // Missing x or y, conflicting 'use' and 'key_ops'
1431                        throw new ParseException(ex.getMessage(), 0);
1432                }
1433        }
1434        
1435        
1436        /**
1437         * Parses a public Elliptic Curve JWK from the specified X.509
1438         * certificate. Requires BouncyCastle.
1439         *
1440         * <p><strong>Important:</strong> The X.509 certificate is not
1441         * validated!
1442         *
1443         * <p>Sets the following JWK parameters:
1444         *
1445         * <ul>
1446         *     <li>The curve is obtained from the subject public key info
1447         *         algorithm parameters.
1448         *     <li>The JWK use inferred by {@link KeyUse#from}.
1449         *     <li>The JWK ID from the X.509 serial number (in base 10).
1450         *     <li>The JWK X.509 certificate chain (this certificate only).
1451         *     <li>The JWK X.509 certificate SHA-256 thumbprint.
1452         * </ul>
1453         *
1454         * @param cert The X.509 certificate. Must not be {@code null}.
1455         *
1456         * @return The public Elliptic Curve JWK.
1457         *
1458         * @throws JOSEException If parsing failed.
1459         */
1460        public static ECKey parse(final X509Certificate cert)
1461                throws JOSEException {
1462                
1463                if (! (cert.getPublicKey() instanceof ECPublicKey)) {
1464                        throw new JOSEException("The public key of the X.509 certificate is not EC");
1465                }
1466                
1467                ECPublicKey publicKey = (ECPublicKey) cert.getPublicKey();
1468                
1469                try {
1470                        JcaX509CertificateHolder certHolder = new JcaX509CertificateHolder(cert);
1471                        
1472                        String oid = certHolder.getSubjectPublicKeyInfo().getAlgorithm().getParameters().toString();
1473                        
1474                        Curve crv = Curve.forOID(oid);
1475                        
1476                        if (crv == null) {
1477                                throw new JOSEException("Couldn't determine EC JWK curve for OID " + oid);
1478                        }
1479                        
1480                        MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
1481                        
1482                        return new ECKey.Builder(crv, publicKey)
1483                                .keyUse(KeyUse.from(cert))
1484                                .keyID(cert.getSerialNumber().toString(10))
1485                                .x509CertChain(Collections.singletonList(Base64.encode(cert.getEncoded())))
1486                                .x509CertSHA256Thumbprint(Base64URL.encode(sha256.digest(cert.getEncoded())))
1487                                .build();
1488                } catch (NoSuchAlgorithmException e) {
1489                        throw new JOSEException("Couldn't encode x5t parameter: " + e.getMessage(), e);
1490                } catch (CertificateEncodingException e) {
1491                        throw new JOSEException("Couldn't encode x5c parameter: " + e.getMessage(), e);
1492                }
1493        }
1494        
1495        
1496        /**
1497         * Loads a public / private Elliptic Curve JWK from the specified JCA
1498         * key store. Requires BouncyCastle.
1499         *
1500         * <p><strong>Important:</strong> The X.509 certificate is not
1501         * validated!
1502         *
1503         * @param keyStore The key store. Must not be {@code null}.
1504         * @param alias    The alias. Must not be {@code null}.
1505         * @param pin      The pin to unlock the private key if any, empty or
1506         *                 {@code null} if not required.
1507         *
1508         * @return The public / private Elliptic Curve JWK., {@code null} if no
1509         *         key with the specified alias was found.
1510         *
1511         * @throws KeyStoreException On a key store exception.
1512         * @throws JOSEException     If EC key loading failed.
1513         */
1514        public static ECKey load(final KeyStore keyStore,
1515                                 final String alias,
1516                                 final char[] pin)
1517                throws KeyStoreException, JOSEException {
1518                
1519                Certificate cert = keyStore.getCertificate(alias);
1520                
1521                if (!(cert instanceof X509Certificate)) {
1522                        return null;
1523                }
1524                
1525                X509Certificate x509Cert = (X509Certificate)cert;
1526                
1527                if (! (x509Cert.getPublicKey() instanceof ECPublicKey)) {
1528                        throw new JOSEException("Couldn't load EC JWK: The key algorithm is not EC");
1529                }
1530                        
1531                ECKey ecJWK = ECKey.parse(x509Cert);
1532                
1533                // Let kid=alias
1534                ecJWK = new ECKey.Builder(ecJWK).keyID(alias).keyStore(keyStore).build();
1535                
1536                // Check for private counterpart
1537                Key key;
1538                try {
1539                        key = keyStore.getKey(alias, pin);
1540                } catch (UnrecoverableKeyException | NoSuchAlgorithmException e) {
1541                        throw new JOSEException("Couldn't retrieve private EC key (bad pin?): " + e.getMessage(), e);
1542                }
1543                        
1544                if (key instanceof ECPrivateKey) {
1545                        // Simple file based key store
1546                        return new ECKey.Builder(ecJWK)
1547                                .privateKey((ECPrivateKey)key)
1548                                .build();
1549                } else if (key instanceof PrivateKey && "EC".equalsIgnoreCase(key.getAlgorithm())) {
1550                        // PKCS#11 store
1551                        return new ECKey.Builder(ecJWK)
1552                                .privateKey((PrivateKey)key)
1553                                .build();
1554                } else {
1555                        return ecJWK;
1556                }
1557        }
1558
1559        
1560        @Override
1561        public boolean equals(Object o) {
1562                if (this == o) return true;
1563                if (!(o instanceof ECKey)) return false;
1564                if (!super.equals(o)) return false;
1565                ECKey ecKey = (ECKey) o;
1566                return Objects.equals(crv, ecKey.crv) &&
1567                                Objects.equals(x, ecKey.x) &&
1568                                Objects.equals(y, ecKey.y) &&
1569                                Objects.equals(d, ecKey.d) &&
1570                                Objects.equals(privateKey, ecKey.privateKey);
1571        }
1572
1573        
1574        @Override
1575        public int hashCode() {
1576                return Objects.hash(super.hashCode(), crv, x, y, d, privateKey);
1577        }
1578}