View Javadoc
1   // Copyright (c) Microsoft Corporation. All rights reserved.
2   // Licensed under the MIT License.
3   
4   package com.microsoft.azure.keyvault.webkey;
5   
6   import java.io.IOException;
7   import java.math.BigInteger;
8   import java.security.GeneralSecurityException;
9   import java.security.KeyFactory;
10  import java.security.KeyPair;
11  import java.security.KeyPairGenerator;
12  import java.security.NoSuchAlgorithmException;
13  import java.security.PrivateKey;
14  import java.security.Provider;
15  import java.security.PublicKey;
16  import java.security.Security;
17  import java.security.interfaces.ECPrivateKey;
18  import java.security.interfaces.ECPublicKey;
19  import java.security.interfaces.RSAPrivateCrtKey;
20  import java.security.interfaces.RSAPublicKey;
21  import java.security.spec.ECGenParameterSpec;
22  import java.security.spec.ECParameterSpec;
23  import java.security.spec.ECPoint;
24  import java.security.spec.ECPrivateKeySpec;
25  import java.security.spec.ECPublicKeySpec;
26  import java.security.spec.EllipticCurve;
27  import java.security.spec.RSAPrivateCrtKeySpec;
28  import java.security.spec.RSAPrivateKeySpec;
29  import java.security.spec.RSAPublicKeySpec;
30  import java.util.Arrays;
31  import java.util.HashSet;
32  import java.util.List;
33  import java.util.Map;
34  import java.util.Set;
35  
36  import javax.crypto.SecretKey;
37  import javax.crypto.spec.SecretKeySpec;
38  
39  import com.fasterxml.jackson.annotation.JsonAutoDetect;
40  import com.fasterxml.jackson.annotation.JsonIgnore;
41  import com.fasterxml.jackson.annotation.JsonProperty;
42  import com.fasterxml.jackson.core.JsonGenerationException;
43  import com.fasterxml.jackson.databind.JsonMappingException;
44  import com.fasterxml.jackson.databind.ObjectMapper;
45  import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
46  import com.fasterxml.jackson.databind.annotation.JsonSerialize;
47  import com.google.common.base.Objects;
48  import com.google.common.collect.ImmutableMap;
49  
50  /**
51   * As of http://tools.ietf.org/html/draft-ietf-jose-json-web-key-18.
52   */
53  @JsonAutoDetect(getterVisibility = JsonAutoDetect.Visibility.PUBLIC_ONLY, setterVisibility = JsonAutoDetect.Visibility.PUBLIC_ONLY)
54  public class JsonWebKey {
55  
56      /**
57       * Key Identifier.
58       */
59      @JsonProperty(value = "kid")
60      private String kid;
61  
62      /**
63       * JsonWebKey key type (kty). Possible values include: 'EC', 'EC-HSM', 'RSA',
64       * 'RSA-HSM', 'oct'.
65       */
66      @JsonProperty(value = "kty")
67      private JsonWebKeyType kty;
68  
69      /**
70       * The keyOps property.
71       */
72      @JsonProperty(value = "key_ops")
73      private List<JsonWebKeyOperation> keyOps;
74  
75      /**
76       * RSA modulus.
77       */
78      @JsonProperty(value = "n")
79      private byte[] n;
80  
81      /**
82       * RSA public exponent.
83       */
84      @JsonProperty(value = "e")
85      private byte[] e;
86  
87      /**
88       * RSA private exponent, or the D component of an EC private key.
89       */
90      @JsonProperty(value = "d")
91      private byte[] d;
92  
93      /**
94       * RSA Private Key Parameter.
95       */
96      @JsonProperty(value = "dp")
97      private byte[] dp;
98  
99      /**
100      * RSA Private Key Parameter.
101      */
102     @JsonProperty(value = "dq")
103     private byte[] dq;
104 
105     /**
106      * RSA Private Key Parameter.
107      */
108     @JsonProperty(value = "qi")
109     private byte[] qi;
110 
111     /**
112      * RSA secret prime.
113      */
114     @JsonProperty(value = "p")
115     private byte[] p;
116 
117     /**
118      * RSA secret prime, with p & q.
119      */
120     @JsonProperty(value = "q")
121     private byte[] q;
122 
123     /**
124      * Symmetric key.
125      */
126     @JsonProperty(value = "k")
127     private byte[] k;
128 
129     /**
130      * HSM Token, used with Bring Your Own Key.
131      */
132     @JsonProperty(value = "key_hsm")
133     private byte[] t;
134 
135     /**
136      * Elliptic curve name. For valid values, see JsonWebKeyCurveName. Possible
137      * values include: 'P-256', 'P-384', 'P-521', 'SECP256K1'.
138      */
139     @JsonProperty(value = "crv")
140     private JsonWebKeyCurveName crv;
141 
142     /**
143      * X component of an EC public key.
144      */
145     @JsonProperty(value = "x")
146     private byte[] x;
147 
148     /**
149      * Y component of an EC public key.
150      */
151     @JsonProperty(value = "y")
152     private byte[] y;
153 
154     /**
155      * Get the kid value.
156      *
157      * @return the kid value
158      */
159     @JsonProperty("kid")
160     public String kid() {
161         return this.kid;
162     }
163 
164     /**
165      * Set the key identifier value.
166      *
167      * @param kid
168      *            the kid value to set
169      * @return the JsonWebKey object itself.
170      */
171     public JsonWebKey withKid(String kid) {
172         this.kid = kid;
173         return this;
174     }
175 
176     /**
177      * Get the kty value.
178      *
179      * @return the kty value
180      */
181     @JsonProperty("kty")
182     public JsonWebKeyType kty() {
183         return this.kty;
184     }
185 
186     /**
187      * Set the key type value.
188      *
189      * @param kty
190      *            the key type
191      * @return the JsonWebKey object itself.
192      */
193     public JsonWebKey withKty(JsonWebKeyType kty) {
194         this.kty = kty;
195         return this;
196     }
197 
198     /**
199      * Get the keyOps value.
200      *
201      * @return the keyOps value
202      */
203     @JsonProperty("key_ops")
204     public List<JsonWebKeyOperation> keyOps() {
205         return this.keyOps;
206     }
207 
208     /**
209      * Set the keyOps value.
210      *
211      * @param keyOps
212      *            the keyOps value to set
213      * @return the JsonWebKey object itself.
214      */
215     public JsonWebKey withKeyOps(List<JsonWebKeyOperation> keyOps) {
216         this.keyOps = keyOps;
217         return this;
218     }
219 
220     /**
221      * Get the n value.
222      *
223      * @return the n value
224      */
225     @JsonProperty("n")
226     @JsonSerialize(using = Base64UrlJsonSerializer.class)
227     @JsonDeserialize(using = Base64UrlJsonDeserializer.class)
228     public byte[] n() {
229         return ByteExtensions.clone(this.n);
230     }
231 
232     /**
233      * Set the n value.
234      *
235      * @param n
236      *            the n value to set
237      * @return the JsonWebKey object itself.
238      */
239     public JsonWebKey withN(byte[] n) {
240         this.n = ByteExtensions.clone(n);
241         return this;
242     }
243 
244     /**
245      * Get the e value.
246      *
247      * @return the e value
248      */
249     @JsonProperty("e")
250     @JsonSerialize(using = Base64UrlJsonSerializer.class)
251     @JsonDeserialize(using = Base64UrlJsonDeserializer.class)
252     public byte[] e() {
253         return ByteExtensions.clone(this.e);
254     }
255 
256     /**
257      * Set the e value.
258      *
259      * @param e
260      *            the e value to set
261      * @return the JsonWebKey object itself.
262      */
263     public JsonWebKey withE(byte[] e) {
264         this.e = ByteExtensions.clone(e);
265         return this;
266     }
267 
268     /**
269      * Get the d value.
270      *
271      * @return the d value
272      */
273     @JsonProperty("d")
274     @JsonSerialize(using = Base64UrlJsonSerializer.class)
275     @JsonDeserialize(using = Base64UrlJsonDeserializer.class)
276     public byte[] d() {
277         return ByteExtensions.clone(this.d);
278     }
279 
280     /**
281      * Set the d value.
282      *
283      * @param d
284      *            the d value to set
285      * @return the JsonWebKey object itself.
286      */
287     public JsonWebKey withD(byte[] d) {
288         this.d = ByteExtensions.clone(d);
289         return this;
290     }
291 
292     /**
293      * Get the RSA Private Key Parameter value.
294      *
295      * @return the RSA Private Key Parameter value.
296      */
297     @JsonProperty("dp")
298     @JsonSerialize(using = Base64UrlJsonSerializer.class)
299     @JsonDeserialize(using = Base64UrlJsonDeserializer.class)
300     public byte[] dp() {
301         return ByteExtensions.clone(this.dp);
302     }
303 
304     /**
305      * Set RSA Private Key Parameter value.
306      *
307      * @param dp
308      *            the RSA Private Key Parameter value to set.
309      * @return the JsonWebKey object itself.
310      */
311     public JsonWebKey withDp(byte[] dp) {
312         this.dp = ByteExtensions.clone(dp);
313         return this;
314     }
315 
316     /**
317      * Get the RSA Private Key Parameter value.
318      *
319      * @return the RSA Private Key Parameter value.
320      */
321     @JsonProperty("dq")
322     @JsonSerialize(using = Base64UrlJsonSerializer.class)
323     @JsonDeserialize(using = Base64UrlJsonDeserializer.class)
324     public byte[] dq() {
325         return ByteExtensions.clone(this.dq);
326     }
327 
328     /**
329      * Set RSA Private Key Parameter value .
330      *
331      * @param dq
332      *            the RSA Private Key Parameter value to set.
333      * @return the JsonWebKey object itself.
334      */
335     public JsonWebKey withDq(byte[] dq) {
336         this.dq = ByteExtensions.clone(dq);
337         return this;
338     }
339 
340     /**
341      * Get the RSA Private Key Parameter value.
342      *
343      * @return the RSA Private Key Parameter value.
344      */
345     @JsonProperty("qi")
346     @JsonSerialize(using = Base64UrlJsonSerializer.class)
347     @JsonDeserialize(using = Base64UrlJsonDeserializer.class)
348     public byte[] qi() {
349         return ByteExtensions.clone(this.qi);
350     }
351 
352     /**
353      * Set RSA Private Key Parameter value.
354      *
355      * @param qi
356      *            the RSA Private Key Parameter value to set.
357      * @return the JsonWebKey object itself.
358      */
359     public JsonWebKey withQi(byte[] qi) {
360         this.qi = ByteExtensions.clone(qi);
361         return this;
362     }
363 
364     /**
365      * Get the RSA secret prime value.
366      *
367      * @return the RSA secret prime value.
368      */
369     @JsonProperty("p")
370     @JsonSerialize(using = Base64UrlJsonSerializer.class)
371     @JsonDeserialize(using = Base64UrlJsonDeserializer.class)
372     public byte[] p() {
373         return ByteExtensions.clone(this.p);
374     }
375 
376     /**
377      * Set the RSA secret prime value.
378      *
379      * @param p
380      *            the RSA secret prime value.
381      * @return the JsonWebKey object itself.
382      */
383     public JsonWebKey withP(byte[] p) {
384         this.p = ByteExtensions.clone(p);
385         return this;
386     }
387 
388     /**
389      * Get RSA secret prime, with p &lt; q value.
390      *
391      * @return the RSA secret prime, with p &lt; q value.
392      */
393     @JsonProperty("q")
394     @JsonSerialize(using = Base64UrlJsonSerializer.class)
395     @JsonDeserialize(using = Base64UrlJsonDeserializer.class)
396     public byte[] q() {
397         return ByteExtensions.clone(this.q);
398     }
399 
400     /**
401      * Set the RSA secret prime, with p &lt; q value.
402      *
403      * @param q
404      *            the the RSA secret prime, with p &lt; q value to be set.
405      * @return the JsonWebKey object itself.
406      */
407     public JsonWebKey withQ(byte[] q) {
408         this.q = ByteExtensions.clone(q);
409         return this;
410     }
411 
412     /**
413      * Get Symmetric key value.
414      *
415      * @return the symmetric key value.
416      */
417     @JsonProperty("k")
418     @JsonSerialize(using = Base64UrlJsonSerializer.class)
419     @JsonDeserialize(using = Base64UrlJsonDeserializer.class)
420     public byte[] k() {
421         return ByteExtensions.clone(this.k);
422     }
423 
424     /**
425      * Set the Symmetric key value.
426      *
427      * @param k
428      *            the symmetric key value to set.
429      * @return the JsonWebKey object itself.
430      */
431     public JsonWebKey withK(byte[] k) {
432         this.k = ByteExtensions.clone(k);
433         return this;
434     }
435 
436     /**
437      * Get HSM Token value, used with Bring Your Own Key.
438      *
439      * @return HSM Token, used with Bring Your Own Key.
440      */
441     @JsonProperty("key_hsm")
442     @JsonSerialize(using = Base64UrlJsonSerializer.class)
443     @JsonDeserialize(using = Base64UrlJsonDeserializer.class)
444     public byte[] t() {
445         return ByteExtensions.clone(this.t);
446     }
447 
448     /**
449      * Set HSM Token value, used with Bring Your Own Key.
450      *
451      * @param t
452      *            HSM Token value to set, used with Bring Your Own Key
453      * @return the JsonWebKey object itself.
454      */
455     public JsonWebKey withT(byte[] t) {
456         this.t = ByteExtensions.clone(t);
457         return this;
458     }
459 
460     @Override
461     public String toString() {
462         ObjectMapper mapper = new ObjectMapper();
463         try {
464             return mapper.writeValueAsString(this);
465         } catch (JsonGenerationException e) {
466             throw new IllegalStateException(e);
467         } catch (JsonMappingException e) {
468             throw new IllegalStateException(e);
469         } catch (IOException e) {
470             throw new IllegalStateException(e);
471         }
472     }
473 
474     /**
475      * Get the crv value.
476      *
477      * @return the crv value
478      */
479     @JsonProperty("crv")
480     public JsonWebKeyCurveName crv() {
481         return this.crv;
482     }
483 
484     /**
485      * Set the crv value.
486      *
487      * @param crv
488      *            the crv value to set
489      * @return the JsonWebKey object itself.
490      */
491     public JsonWebKey withCrv(JsonWebKeyCurveName crv) {
492         this.crv = crv;
493         return this;
494     }
495 
496     /**
497      * Get the x value.
498      *
499      * @return the x value
500      */
501     @JsonProperty("x")
502     @JsonSerialize(using = Base64UrlJsonSerializer.class)
503     @JsonDeserialize(using = Base64UrlJsonDeserializer.class)
504     public byte[] x() {
505         return ByteExtensions.clone(this.x);
506     }
507 
508     /**
509      * Set the x value.
510      *
511      * @param x
512      *            the x value to set
513      * @return the JsonWebKey object itself.
514      */
515     public JsonWebKey withX(byte[] x) {
516         this.x = ByteExtensions.clone(x);
517         return this;
518     }
519 
520     /**
521      * Get the y value.
522      *
523      * @return the y value
524      */
525     @JsonProperty("y")
526     @JsonSerialize(using = Base64UrlJsonSerializer.class)
527     @JsonDeserialize(using = Base64UrlJsonDeserializer.class)
528     public byte[] y() {
529         return ByteExtensions.clone(this.y);
530     }
531 
532     /**
533      * Set the y value.
534      *
535      * @param y
536      *            the y value to set
537      * @return the JsonWebKey object itself.
538      */
539     public JsonWebKey withY(byte[] y) {
540         this.y = ByteExtensions.clone(y);
541         return this;
542     }
543 
544     /**
545      * Get the RSA public key spec value.
546      *
547      * @return the RSA public key spec value
548      */
549     private RSAPublicKeySpec getRSAPublicKeySpec() {
550 
551         return new RSAPublicKeySpec(toBigInteger(n), toBigInteger(e));
552     }
553 
554     /**
555      * Get the RSA private key spec value.
556      *
557      * @return the RSA private key spec value
558      */
559     private RSAPrivateKeySpec getRSAPrivateKeySpec() {
560 
561         return new RSAPrivateCrtKeySpec(toBigInteger(n), toBigInteger(e), toBigInteger(d), toBigInteger(p),
562                 toBigInteger(q), toBigInteger(dp), toBigInteger(dq), toBigInteger(qi));
563     }
564 
565     /**
566      * Get the RSA public key value.
567      *
568      * @param provider
569      *            the Java security provider.
570      * @return the RSA public key value
571      */
572     private PublicKey getRSAPublicKey(Provider provider) {
573 
574         try {
575             RSAPublicKeySpec publicKeySpec = getRSAPublicKeySpec();
576             KeyFactory factory = provider != null ? KeyFactory.getInstance("RSA", provider)
577                     : KeyFactory.getInstance("RSA");
578 
579             return factory.generatePublic(publicKeySpec);
580         } catch (GeneralSecurityException e) {
581             throw new IllegalStateException(e);
582         }
583     }
584 
585     /**
586      * Get the RSA private key value.
587      *
588      * @param provider
589      *            the Java security provider.
590      * @return the RSA private key value
591      */
592     private PrivateKey getRSAPrivateKey(Provider provider) {
593 
594         try {
595             RSAPrivateKeySpec privateKeySpec = getRSAPrivateKeySpec();
596             KeyFactory factory = provider != null ? KeyFactory.getInstance("RSA", provider)
597                     : KeyFactory.getInstance("RSA");
598 
599             return factory.generatePrivate(privateKeySpec);
600         } catch (GeneralSecurityException e) {
601             throw new IllegalStateException(e);
602         }
603     }
604 
605     private static PublicKey getECPublicKey(ECPoint ecPoint, ECParameterSpec curveSpec, Provider provider) {
606         // Create public key spec with given point
607         try {
608             ECPublicKeySpec pubSpec = new ECPublicKeySpec(ecPoint, curveSpec);
609             KeyFactory kf = provider != null ? KeyFactory.getInstance("EC", provider)
610                     : KeyFactory.getInstance("EC", "SunEC");
611             return (ECPublicKey) kf.generatePublic(pubSpec);
612         } catch (GeneralSecurityException e) {
613             throw new IllegalStateException(e);
614         }
615     }
616 
617     private static PrivateKey getECPrivateKey(byte[] d, ECParameterSpec curveSpec, Provider provider) {
618         try {
619             ECPrivateKeySpec priSpec = new ECPrivateKeySpec(new BigInteger(1, d), curveSpec);
620             KeyFactory kf = provider != null ? KeyFactory.getInstance("EC", provider)
621                     : KeyFactory.getInstance("EC", "SunEC");
622             return (ECPrivateKey) kf.generatePrivate(priSpec);
623         } catch (GeneralSecurityException e) {
624             throw new IllegalStateException(e);
625         }
626     }
627 
628     /**
629      * Verifies if the key is an RSA key.
630      */
631     private void checkRSACompatible() {
632         if (!JsonWebKeyType.RSA.equals(kty) && !JsonWebKeyType.RSA_HSM.equals(kty)) {
633             throw new UnsupportedOperationException("Not an RSA key");
634         }
635     }
636 
637     private static byte[] toByteArray(BigInteger n) {
638         byte[] result = n.toByteArray();
639         if (result[0] == 0) {
640             // The leading zero is used to let the number positive. Since RSA
641             // parameters are always positive, we remove it.
642             return Arrays.copyOfRange(result, 1, result.length);
643         }
644         return result;
645     }
646 
647     private static BigInteger toBigInteger(byte[] b) {
648         if (b[0] < 0) {
649             // RSA parameters are always positive numbers, so if the first byte
650             // is negative, we need to add a leading zero
651             // to make the entire BigInteger positive.
652             byte[] temp = new byte[1 + b.length];
653             System.arraycopy(b, 0, temp, 1, b.length);
654             b = temp;
655         }
656         return new BigInteger(b);
657     }
658 
659     /**
660      * Converts RSA key pair to JSON web key.
661      *
662      * @param keyPair
663      *            RSA key pair
664      * @return the JSON web key, converted from RSA key pair.
665      */
666     public static JsonWebKey fromRSA(KeyPair keyPair) {
667 
668         RSAPrivateCrtKey privateKey = (RSAPrivateCrtKey) keyPair.getPrivate();
669         JsonWebKey key = null;
670 
671         if (privateKey != null) {
672 
673             key = new JsonWebKey().withKty(JsonWebKeyType.RSA).withN(toByteArray(privateKey.getModulus()))
674                     .withE(toByteArray(privateKey.getPublicExponent()))
675                     .withD(toByteArray(privateKey.getPrivateExponent())).withP(toByteArray(privateKey.getPrimeP()))
676                     .withQ(toByteArray(privateKey.getPrimeQ())).withDp(toByteArray(privateKey.getPrimeExponentP()))
677                     .withDq(toByteArray(privateKey.getPrimeExponentQ()))
678                     .withQi(toByteArray(privateKey.getCrtCoefficient()));
679         } else {
680 
681             RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
682 
683             key = new JsonWebKey().withKty(JsonWebKeyType.RSA).withN(toByteArray(publicKey.getModulus()))
684                     .withE(toByteArray(publicKey.getPublicExponent())).withD(null).withP(null).withQ(null).withDp(null)
685                     .withDq(null).withQi(null);
686         }
687 
688         return key;
689     }
690 
691     /**
692      * Converts JSON web key to RSA key pair.
693      *
694      * @return RSA key pair
695      */
696     public KeyPair toRSA() {
697         return this.toRSA(false);
698     }
699 
700     /**
701      * Converts JSON web key to RSA key pair and include the private key if set to
702      * true.
703      *
704      * @param includePrivateParameters
705      *            true if the RSA key pair should include the private key. False
706      *            otherwise.
707      * @return RSA key pair
708      */
709     public KeyPair toRSA(boolean includePrivateParameters) {
710         return toRSA(includePrivateParameters, null);
711     }
712 
713     /**
714      * Converts JSON web key to RSA key pair and include the private key if set to
715      * true.
716      *
717      * @param provider
718      *            the Java security provider.
719      * @param includePrivateParameters
720      *            true if the RSA key pair should include the private key. False
721      *            otherwise.
722      * @return RSA key pair
723      */
724     public KeyPair toRSA(boolean includePrivateParameters, Provider provider) {
725 
726         // Must be RSA
727         checkRSACompatible();
728 
729         if (includePrivateParameters) {
730             return new KeyPair(getRSAPublicKey(provider), getRSAPrivateKey(provider));
731         } else {
732             return new KeyPair(getRSAPublicKey(provider), null);
733         }
734     }
735 
736     /**
737      * Converts JSON web key to EC key pair and include the private key if set to
738      * true.
739      *
740      * @return EC key pair
741      */
742     public KeyPair toEC() {
743         return toEC(false, null);
744     }
745 
746     /**
747      * Converts JSON web key to EC key pair and include the private key if set to
748      * true.
749      *
750      * @param includePrivateParameters
751      *            true if the EC key pair should include the private key. False
752      *            otherwise.
753      * @return EC key pair
754      */
755     public KeyPair toEC(boolean includePrivateParameters) {
756         return toEC(includePrivateParameters, null);
757     }
758 
759     /**
760      * Converts JSON web key to EC key pair and include the private key if set to
761      * true.
762      *
763      * @param includePrivateParameters
764      *            true if the EC key pair should include the private key. False
765      *            otherwise.
766      * @param provider
767      *            Java security provider
768      * @return EC key pair
769      */
770     public KeyPair toEC(boolean includePrivateParameters, Provider provider) {
771 
772         if (provider == null) {
773             // Our default provider for this class
774             provider = Security.getProvider("SunEC");
775         }
776 
777         if (!JsonWebKeyType.EC.equals(kty) && !JsonWebKeyType.EC_HSM.equals(kty)) {
778             throw new IllegalArgumentException("Not an EC key.");
779         }
780 
781         try {
782             KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC", provider);
783 
784             ECGenParameterSpec gps = new ECGenParameterSpec(CURVE_TO_SPEC_NAME.get(crv));
785             kpg.initialize(gps);
786 
787             // Generate dummy keypair to get parameter spec.
788             KeyPair apair = kpg.generateKeyPair();
789             ECPublicKey apub = (ECPublicKey) apair.getPublic();
790             ECParameterSpec aspec = apub.getParams();
791 
792             ECPoint ecPoint = new ECPoint(new BigInteger(1, x), new BigInteger(1, y));
793 
794             KeyPair realKeyPair;
795 
796             if (includePrivateParameters) {
797                 realKeyPair = new KeyPair(getECPublicKey(ecPoint, aspec, provider),
798                         getECPrivateKey(d, aspec, provider));
799             } else {
800                 realKeyPair = new KeyPair(getECPublicKey(ecPoint, aspec, provider), null);
801             }
802 
803             return realKeyPair;
804         } catch (GeneralSecurityException e) {
805             throw new IllegalStateException(e);
806         }
807     }
808 
809     /**
810      * Converts EC key pair to JSON web key.
811      *
812      * @param keyPair
813      *            EC key pair
814      * @param provider
815      *            Java security provider
816      * @return the JSON web key, converted from EC key pair.
817      */
818     public static JsonWebKey fromEC(KeyPair keyPair, Provider provider) {
819 
820         ECPublicKey apub = (ECPublicKey) keyPair.getPublic();
821         ECPoint point = apub.getW();
822         ECPrivateKey apriv = (ECPrivateKey) keyPair.getPrivate();
823 
824         if (apriv != null) {
825             return new JsonWebKey().withKty(JsonWebKeyType.EC).withCrv(getCurveFromKeyPair(keyPair, provider))
826                     .withX(point.getAffineX().toByteArray()).withY(point.getAffineY().toByteArray())
827                     .withD(apriv.getS().toByteArray()).withKty(JsonWebKeyType.EC);
828         } else {
829             return new JsonWebKey().withKty(JsonWebKeyType.EC).withCrv(getCurveFromKeyPair(keyPair, provider))
830                     .withX(point.getAffineX().toByteArray()).withY(point.getAffineY().toByteArray())
831                     .withKty(JsonWebKeyType.EC);
832         }
833     }
834 
835     // Matches the curve of the keyPair to supported curves.
836     private static JsonWebKeyCurveName getCurveFromKeyPair(KeyPair keyPair, Provider provider) {
837 
838         try {
839             ECPublicKey key = (ECPublicKey) keyPair.getPublic();
840             ECParameterSpec spec = key.getParams();
841             EllipticCurve crv = spec.getCurve();
842 
843             List<JsonWebKeyCurveName> curveList = Arrays.asList(JsonWebKeyCurveName.P_256, JsonWebKeyCurveName.P_384,
844                     JsonWebKeyCurveName.P_521, JsonWebKeyCurveName.P_256K);
845 
846             for (JsonWebKeyCurveName curve : curveList) {
847                 ECGenParameterSpec gps = new ECGenParameterSpec(CURVE_TO_SPEC_NAME.get(curve));
848                 KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC", provider);
849                 kpg.initialize(gps);
850 
851                 // Generate dummy keypair to get parameter spec.
852                 KeyPair apair = kpg.generateKeyPair();
853                 ECPublicKey apub = (ECPublicKey) apair.getPublic();
854                 ECParameterSpec aspec = apub.getParams();
855                 EllipticCurve acurve = aspec.getCurve();
856 
857                 // Matches the parameter spec
858                 if (acurve.equals(crv)) {
859                     return curve;
860                 }
861             }
862 
863             // Did not find a supported curve.
864             throw new NoSuchAlgorithmException("Curve not supported.");
865         } catch (GeneralSecurityException e) {
866             throw new IllegalStateException(e);
867         }
868     }
869 
870     /**
871      * Converts AES key to JSON web key.
872      *
873      * @param secretKey
874      *            AES key
875      * @return the JSON web key, converted from AES key.
876      */
877     public static JsonWebKey fromAes(SecretKey secretKey) {
878         if (secretKey == null) {
879             return null;
880         }
881 
882         return new JsonWebKey().withK(secretKey.getEncoded()).withKty(JsonWebKeyType.OCT);
883     }
884 
885     /**
886      * Converts JSON web key to AES key.
887      *
888      * @return AES key
889      */
890     public SecretKey toAes() {
891         if (k == null) {
892             return null;
893         }
894 
895         SecretKey secretKey = new SecretKeySpec(k, "AES");
896         return secretKey;
897     }
898 
899     @Override
900     public boolean equals(Object obj) {
901         if (obj == this) {
902             return true;
903         }
904         if (obj instanceof JsonWebKey) {
905             return this.equals((JsonWebKey) obj);
906         }
907         return super.equals(obj);
908     }
909 
910     /**
911      * Indicates whether some other {@link JsonWebKey} is "equal to" this one.
912      *
913      * @param jwk
914      *            the other {@link JsonWebKey} to compare with.
915      * @return true if this {@link JsonWebKey} is the same as the jwk argument;
916      *         false otherwise.
917      */
918     public boolean equals(JsonWebKey jwk) {
919         if (jwk == null) {
920             return false;
921         }
922 
923         if (!Objects.equal(kid, jwk.kid)) {
924             return false;
925         }
926 
927         if (!Objects.equal(kty, jwk.kty)) {
928             return false;
929         }
930 
931         if (!Objects.equal(keyOps, jwk.keyOps)) {
932             return false;
933         }
934 
935         if (!Objects.equal(crv, jwk.crv)) {
936             return false;
937         }
938 
939         if (!Arrays.equals(k, jwk.k)) {
940             return false;
941         }
942 
943         // Public parameters
944         if (!Arrays.equals(n, jwk.n)) {
945             return false;
946         }
947         if (!Arrays.equals(e, jwk.e)) {
948             return false;
949         }
950 
951         // Private parameters
952         if (!Arrays.equals(d, jwk.d)) {
953             return false;
954         }
955         if (!Arrays.equals(dp, jwk.dp)) {
956             return false;
957         }
958         if (!Arrays.equals(dq, jwk.dq)) {
959             return false;
960         }
961         if (!Arrays.equals(qi, jwk.qi)) {
962             return false;
963         }
964         if (!Arrays.equals(p, jwk.p)) {
965             return false;
966         }
967         if (!Arrays.equals(q, jwk.q)) {
968             return false;
969         }
970         if (!Arrays.equals(x, jwk.x)) {
971             return false;
972         }
973         if (!Arrays.equals(y, jwk.y)) {
974             return false;
975         }
976 
977         // HSM token
978         if (!Arrays.equals(t, jwk.t)) {
979             return false;
980         }
981 
982         return true;
983     }
984 
985     /**
986      * Verifies whether the {@link JsonWebKey} has private key.
987      *
988      * @return true if the {@link JsonWebKey} has private key; false otherwise.
989      */
990     public boolean hasPrivateKey() {
991 
992         if (JsonWebKeyType.OCT.equals(kty)) {
993             return k != null;
994         } else if (JsonWebKeyType.RSA.equals(kty) || JsonWebKeyType.RSA_HSM.equals(kty)) {
995             return (d != null && dp != null && dq != null && qi != null && p != null && q != null);
996         } else if (JsonWebKeyType.EC.equals(kty) || JsonWebKeyType.EC_HSM.equals(kty)) {
997             return (d != null);
998         }
999 
1000         return false;
1001     }
1002 
1003     /**
1004      * Verifies whether the {@link JsonWebKey} is valid.
1005      *
1006      * @return true if the {@link JsonWebKey} is valid; false otherwise.
1007      */
1008     @JsonIgnore
1009     public boolean isValid() {
1010         if (kty == null) {
1011             return false;
1012         }
1013 
1014         if (keyOps != null) {
1015             final Set<JsonWebKeyOperation> set = new HashSet<JsonWebKeyOperation>(JsonWebKeyOperation.ALL_OPERATIONS);
1016             for (int i = 0; i < keyOps.size(); i++) {
1017                 if (!set.contains(keyOps.get(i))) {
1018                     return false;
1019                 }
1020             }
1021         }
1022 
1023         if (JsonWebKeyType.OCT.equals(kty)) {
1024             return isValidOctet();
1025         } else if (JsonWebKeyType.RSA.equals(kty)) {
1026             return isValidRsa();
1027         } else if (JsonWebKeyType.RSA_HSM.equals(kty)) {
1028             return isValidRsaHsm();
1029         } else if (JsonWebKeyType.EC.equals(kty)) {
1030             return isValidEc();
1031         } else if (JsonWebKeyType.EC_HSM.equals(kty)) {
1032             return isValidEcHsm();
1033         }
1034 
1035         return false;
1036     }
1037 
1038     private boolean isValidOctet() {
1039         if (k != null) {
1040             return true;
1041         }
1042         return false;
1043     }
1044 
1045     private boolean isValidRsa() {
1046         if (n == null || e == null) {
1047             return false;
1048         }
1049 
1050         return hasPrivateKey() || (d == null && dp == null && dq == null && qi == null && p == null && q == null);
1051     }
1052 
1053     private boolean isValidRsaHsm() {
1054         // MAY have public key parameters
1055         if ((n == null && e != null) || (n != null && e == null)) {
1056             return false;
1057         }
1058 
1059         // no private key
1060         if (hasPrivateKey()) {
1061             return false;
1062         }
1063 
1064         // MUST have ( T || ( N && E ) )
1065         boolean tokenParameters = t != null;
1066         boolean publicParameters = (n != null && e != null);
1067 
1068         if (tokenParameters && publicParameters) {
1069             return false;
1070         }
1071 
1072         return (tokenParameters || publicParameters);
1073     }
1074 
1075     private boolean isValidEc() {
1076         boolean ecPointParameters = (x != null && y != null);
1077         if (!ecPointParameters || crv == null) {
1078             return false;
1079         }
1080 
1081         return hasPrivateKey() || (d == null);
1082     }
1083 
1084     private boolean isValidEcHsm() {
1085         // MAY have public key parameters
1086         boolean ecPointParameters = (x != null && y != null);
1087         if ((ecPointParameters && crv == null) || (!ecPointParameters && crv != null)) {
1088             return false;
1089         }
1090 
1091         // no private key
1092         if (hasPrivateKey()) {
1093             return false;
1094         }
1095 
1096         // MUST have (T || (ecPointParameters && crv))
1097         boolean publicParameters = (ecPointParameters && crv != null);
1098         boolean tokenParameters = t != null;
1099 
1100         if (tokenParameters && publicParameters) {
1101             return false;
1102         }
1103 
1104         return (tokenParameters || publicParameters);
1105     }
1106 
1107     /**
1108      * Clear key materials.
1109      */
1110     public void clearMemory() {
1111         zeroArray(k);
1112         k = null;
1113         zeroArray(n);
1114         n = null;
1115         zeroArray(e);
1116         e = null;
1117         zeroArray(d);
1118         d = null;
1119         zeroArray(dp);
1120         dp = null;
1121         zeroArray(dq);
1122         dq = null;
1123         zeroArray(qi);
1124         qi = null;
1125         zeroArray(p);
1126         p = null;
1127         zeroArray(q);
1128         q = null;
1129         zeroArray(t);
1130         t = null;
1131         zeroArray(x);
1132         x = null;
1133         zeroArray(y);
1134         y = null;
1135     }
1136 
1137     private static void zeroArray(byte[] bytes) {
1138         if (bytes != null) {
1139             Arrays.fill(bytes, (byte) 0);
1140         }
1141     }
1142 
1143     @Override
1144     public int hashCode() {
1145         int hashCode = 48313; // setting it to a random prime number
1146         if (kid != null) {
1147             hashCode += kid.hashCode();
1148         }
1149 
1150         if (JsonWebKeyType.OCT.equals(kty)) {
1151             hashCode += hashCode(k);
1152         } else if (JsonWebKeyType.RSA.equals(kty)) {
1153             hashCode += hashCode(n);
1154         } else if (JsonWebKeyType.EC.equals(kty)) {
1155             hashCode += hashCode(x);
1156             hashCode += hashCode(y);
1157             hashCode += crv.hashCode();
1158         } else if (JsonWebKeyType.RSA_HSM.equals(kty) || JsonWebKeyType.EC_HSM.equals(kty)) {
1159             hashCode += hashCode(t);
1160         }
1161 
1162         return hashCode;
1163     }
1164 
1165     private static int hashCode(byte[] obj) {
1166         int hashCode = 0;
1167 
1168         if (obj == null || obj.length == 0) {
1169             return 0;
1170         }
1171 
1172         for (int i = 0; i < obj.length; i++) {
1173             hashCode = (hashCode << 3) | (hashCode >> 29) ^ obj[i];
1174         }
1175         return hashCode;
1176     }
1177 
1178     private static final Map<JsonWebKeyCurveName, String> CURVE_TO_SPEC_NAME = ImmutableMap
1179             .<JsonWebKeyCurveName, String>builder().put(JsonWebKeyCurveName.P_256, "secp256r1")
1180             .put(JsonWebKeyCurveName.P_384, "secp384r1").put(JsonWebKeyCurveName.P_521, "secp521r1")
1181             .put(JsonWebKeyCurveName.P_256K, "secp256k1").build();
1182 }
1183