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