View Javadoc
1   /*
2    * The contents of this file are subject to the terms of the Common Development and
3    * Distribution License (the License). You may not use this file except in compliance with the
4    * License.
5    *
6    * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
7    * specific language governing permission and limitations under the License.
8    *
9    * When distributing Covered Software, include this CDDL Header Notice in each file and include
10   * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
11   * Header, with the fields enclosed by brackets [] replaced by your own identifying
12   * information: "Portions Copyrighted [year] [name of copyright owner]".
13   *
14   * Copyright 2013-2016 ForgeRock AS.
15   */
16  
17  package org.forgerock.json.jose.jwk;
18  
19  import java.math.BigInteger;
20  import java.security.KeyFactory;
21  import java.security.KeyPair;
22  import java.security.interfaces.RSAPrivateCrtKey;
23  import java.security.interfaces.RSAPrivateKey;
24  import java.security.interfaces.RSAPublicKey;
25  import java.security.spec.RSAMultiPrimePrivateCrtKeySpec;
26  import java.security.spec.RSAOtherPrimeInfo;
27  import java.security.spec.RSAPrivateCrtKeySpec;
28  import java.security.spec.RSAPrivateKeySpec;
29  import java.security.spec.RSAPublicKeySpec;
30  import java.util.ArrayList;
31  import java.util.Collections;
32  import java.util.HashMap;
33  import java.util.List;
34  
35  import org.forgerock.json.JsonException;
36  import org.forgerock.json.JsonValue;
37  import org.forgerock.util.encode.Base64url;
38  
39  /**
40   * Implements a RsaJWK.
41   */
42  public class RsaJWK extends JWK {
43  
44      private static final int BIG_INTEGER_POSITIVE = 1;
45  
46      /**
47       * Holds the other prime factors.
48       */
49      public static class OtherFactors extends JsonValue {
50          /**
51           * The R key value.
52           */
53          private final static String R = "r";
54  
55          /**
56           * The D key value.
57           */
58          private final static String D = "d";
59  
60          /**
61           * The T key value.
62           */
63          private final static String T = "t";
64  
65          /**
66           * Creates the other prime factors.
67           * @param r r value
68           * @param d d value
69           * @param t t value
70           */
71          public OtherFactors(String r, String d, String t) {
72              super(new HashMap<>());
73              put(R, r);
74              put(D, d);
75              put(T, t);
76          }
77  
78          /**
79           * Create other prime factors.
80           * @param info RSAOtherPrimeInfo used to create the other prime factors object.
81           */
82          public OtherFactors(RSAOtherPrimeInfo info) {
83              super(new HashMap<>());
84              put(R, Base64url.encode(info.getPrime().toByteArray()));
85              put(D, Base64url.encode(info.getExponent().toByteArray()));
86              put(T, Base64url.encode(info.getCrtCoefficient().toByteArray()));
87          }
88  
89          /**
90           * Get the R value.
91           * @return the R value
92           */
93          public String getFactor() {
94              return get(R).asString();
95          }
96  
97          /**
98           * Get the D value.
99           * @return the D value.
100          */
101         public String getCRTExponent() {
102             return get(D).asString();
103         }
104 
105         /**
106          * Get the T value.
107          * @return the T value
108          */
109         public String getCRTCoefficient() {
110             return get(T).asString();
111         }
112     }
113 
114     /**
115      * The N key.
116      */
117     private final static String N = "n";
118 
119     /**
120      * The E key.
121      */
122     private final static String E = "e";
123 
124     /**
125      * The D key.
126      */
127     private final static String D = "d";
128 
129     /**
130      * The P key.
131      */
132     private final static String P = "p";
133 
134     /**
135      * The Q key.
136      */
137     private final static String Q = "q";
138 
139     /**
140      * The DP key.
141      */
142     private final static String DP = "dp";
143 
144     /**
145      * The DQ key.
146      */
147     private final static String DQ = "dq";
148 
149     /**
150      * The QI key.
151      */
152     private final static String QI = "qi";
153 
154     /**
155      * The factors key.
156      */
157     private final static String FACTORS = "factors";
158 
159     /**
160      * Creates a RsaJWK.
161      * @param use the use of the JWK
162      * @param alg the alg of the JWK
163      * @param kid the key id of the JWK
164      * @param n the modulus of the JWK
165      * @param e the public exponent JWK
166      * @param x5u the x509 url for the key
167      * @param x5t the x509 thumbnail for the key
168      * @param x5c the x509 chain as a list of Base64 encoded strings
169      */
170     public RsaJWK(KeyUse use, String alg, String kid, String n, String e, String x5u, String x5t, List<String> x5c) {
171         this (use, alg, kid, n, e, null, null, null, null, null, null, null, x5u, x5t, x5c);
172     }
173 
174     /**
175      * Creates a RsaJWK.
176      * @param use the use of the JWK
177      * @param alg the alg of the JWK
178      * @param kid the key id of the JWK
179      * @param n the modulus of the JWK
180      * @param e the public exponent JWK
181      * @param d the private exponent JWK
182      * @param x5u the x509 url for the key
183      * @param x5t the x509 thumbnail for the key
184      * @param x5c the x509 chain as a list of Base64 encoded strings
185      */
186     public RsaJWK(KeyUse use, String alg, String kid, String n, String e, String d, String x5u, String x5t,
187                   List<String> x5c) {
188         this (use, alg, kid, n, e, d, null, null, null, null, null, null, x5u, x5t, x5c);
189     }
190 
191     /**
192      * Creates a RsaJWK.
193      * @param use the use of the JWK
194      * @param alg the alg of the JWK
195      * @param kid the key id of the JWK
196      * @param n the modulus of the JWK
197      * @param e the public exponent JWK
198      * @param p the first prime factor of the JWK
199      * @param q the second prime factor of the JWK
200      * @param dp the first factor exponent of the JWK
201      * @param dq the second factor exponent of the JWK
202      * @param qi the first CRT Coefficient of the JWK
203      * @param x5u the x509 url for the key
204      * @param x5t the x509 thumbnail for the key
205      * @param x5c the x509 chain as a list of Base64 encoded strings
206      */
207     public RsaJWK(KeyUse use, String alg, String kid, String n, String e, String p, String q, String dp,
208                   String dq, String qi, String x5u, String x5t, List<String> x5c) {
209         this (use, alg, kid, n, e, null, p, q, dp, dq, qi, null, x5u, x5t, x5c);
210     }
211 
212     /**
213      * Creates a RsaJWK.
214      * @param use the use of the JWK
215      * @param alg the alg of the JWK
216      * @param kid the key id of the JWK
217      * @param n the modulus of the JWK
218      * @param e the public exponent JWK
219      * @param d the private exponent JWK
220      * @param p the first prime factor of the JWK
221      * @param q the second prime factor of the JWK
222      * @param dp the first factor exponent of the JWK
223      * @param dq the second factor exponent of the JWK
224      * @param qi the first CRT Coefficient of the JWK
225      * @param factors the extra factors of the JWK
226      * @param x5u the x509 url for the key
227      * @param x5t the x509 thumbnail for the key
228      * @param x5c the x509 chain as a list of Base64 encoded strings
229      */
230     public RsaJWK(KeyUse use, String alg, String kid, String n, String e, String d, String p, String q,
231                   String dp, String dq, String qi, List<OtherFactors> factors,
232                   String x5u, String x5t, List<String> x5c) {
233         super(KeyType.RSA, use, alg, kid, x5u, x5t, x5c);
234         if (n != null && !n.isEmpty()) {
235             put(N, n);
236         }
237         if (e != null && !e.isEmpty()) {
238             put(E, e);
239         }
240         if (d != null && !d.isEmpty()) {
241             put(D, d);
242         }
243         if (p != null && !p.isEmpty()) {
244             put(P, p);
245         }
246         if (q != null && !q.isEmpty()) {
247             put(Q, q);
248         }
249         if (dp != null && !dp.isEmpty()) {
250             put(DP, dp);
251         }
252         if (dq != null && !dq.isEmpty()) {
253             put(DQ, dq);
254         }
255         if (qi != null && !qi.isEmpty()) {
256             put(QI, qi);
257         }
258         if (factors == null) {
259             put(FACTORS, Collections.EMPTY_LIST);
260         } else {
261             put(FACTORS, factors);
262         }
263         if (x5u != null && !x5u.isEmpty()) {
264             put(X5U, x5u);
265         }
266         if (x5t != null && !x5t.isEmpty()) {
267             put(X5T, x5t);
268         }
269         if (x5c != null && !x5c.isEmpty()) {
270             put(X5C, x5c);
271         }
272 
273 
274     }
275 
276     /**
277      * Creates a RsaJWK.
278      * @param use the use of the JWK
279      * @param alg the alg of the JWK
280      * @param kid the key id of the JWK
281      * @param key the RSAPublicKey to use
282      * @param x5u the x509 url for the key
283      * @param x5t the x509 thumbnail for the key
284      * @param x5c the x509 chain as a list of Base64 encoded strings
285      */
286     public RsaJWK(RSAPublicKey key, KeyUse use, String alg, String kid, String x5u, String x5t, List<String> x5c) {
287         this(use, alg, kid,
288                 Base64url.encode(key.getModulus().toByteArray()),
289                 Base64url.encode(key.getPublicExponent().toByteArray()),
290                 x5u, x5t, x5c);
291     }
292     /**
293      * Creates a RsaJWK.
294      * @param use the use of the JWK
295      * @param alg the alg of the JWK
296      * @param kid the key id of the JWK
297      * @param pubKey the RSAPublicKey to use
298      * @param privKey the RSAPrivateKey to use
299      * @param x5u the x509 url for the key
300      * @param x5t the x509 thumbnail for the key
301      * @param x5c the x509 chain as a list of Base64 encoded strings
302      */
303     public RsaJWK(RSAPublicKey pubKey, RSAPrivateKey privKey, KeyUse use, String alg, String kid,
304                   String x5u, String x5t, List<String> x5c) {
305         this(use, alg, kid,
306                 Base64url.encode(pubKey.getModulus().toByteArray()),
307                 Base64url.encode(pubKey.getPublicExponent().toByteArray()),
308                 Base64url.encode(privKey.getPrivateExponent().toByteArray()),
309                 x5u, x5t, x5c);
310     }
311     /**
312      * Creates a RsaJWK.
313      * @param use the use of the JWK
314      * @param alg the alg of the JWK
315      * @param kid the key id of the JWK
316      * @param pubKey the RSAPublicKey to use
317      * @param privCert the RSAPrivateCrtKey to use
318      * @param x5u the x509 url for the key
319      * @param x5t the x509 thumbnail for the key
320      * @param x5c the x509 chain as a list of Base64 encoded strings
321      */
322     public RsaJWK(RSAPublicKey pubKey, RSAPrivateCrtKey privCert, KeyUse use, String alg, String kid,
323                   String x5u, String x5t, List<String> x5c) {
324         this(use, alg, kid,
325                 Base64url.encode(pubKey.getModulus().toByteArray()),
326                 Base64url.encode(pubKey.getPublicExponent().toByteArray()),
327                 Base64url.encode(privCert.getPrivateExponent().toByteArray()),
328                 Base64url.encode(privCert.getPrimeP().toByteArray()),
329                 Base64url.encode(privCert.getPrimeQ().toByteArray()),
330                 Base64url.encode(privCert.getPrimeExponentP().toByteArray()),
331                 Base64url.encode(privCert.getPrimeExponentQ().toByteArray()),
332                 Base64url.encode(privCert.getCrtCoefficient().toByteArray()),
333                 null, x5u, x5t, x5c);
334 
335     }
336 
337     /**
338      * Get the RSA modulus value.
339      * @return a Base64url modulus value
340      */
341     public String getModulus() {
342         return get(N).asString();
343     }
344 
345     /**
346      * Get the RSA Public Exponent.
347      * @return a Base64url Public Exponent value
348      */
349     public String getPublicExponent() {
350         return get(E).asString();
351     }
352 
353     /**
354      * Get the RSA Private Exponent value.
355      * @return a Base64url Private Exponent value
356      */
357     public String getPrivateExponent() {
358         return get(D).asString();
359     }
360 
361     /**
362      * Get the RSA First Prime Factor value.
363      * @return a Base64url First Prime Factor value
364      */
365     public String getPrimeP() {
366         return get(P).asString();
367     }
368 
369     /**
370      * Get the RSA Second Prime Factor value.
371      * @return a Base64url Second Prime Factor value
372      */
373     public String getPrimeQ() {
374         return get(Q).asString();
375     }
376 
377     /**
378      * Get the RSA First Factor CRT Exponent value.
379      * @return a Base64url First Factor CRT Exponent value
380      */
381     public String getPrimePExponent() {
382         return get(DP).asString();
383     }
384 
385     /**
386      * Get the RSA Second factor CRT Exponent value.
387      * @return a Base64url Second factor CRT Exponent value
388      */
389     public String getPrimeQExponent() {
390         return get(DQ).asString();
391     }
392 
393     /**
394      * Get the RSA First CRT Coefficient value.
395      * @return a Base64url First CRT Coefficient value
396      */
397     public String getCRTCoefficient() {
398         return get(QI).asString();
399     }
400 
401     /**
402      * Get the RSA other factors value.
403      * @return a Base64url other factors value
404      */
405     public List<Object> getOtherFactors() {
406         return get(FACTORS).asList();
407     }
408 
409     /**
410      * Creates a RSAPublicKey from the JWK.
411      * @return a RSAPublicKey
412      */
413     public RSAPublicKey toRSAPublicKey() {
414         try {
415             RSAPublicKeySpec spec = new RSAPublicKeySpec(asPositiveBigInteger(getModulus()),
416                     asPositiveBigInteger(getPublicExponent()));
417             KeyFactory factory = KeyFactory.getInstance("RSA");
418             return (RSAPublicKey) factory.generatePublic(spec);
419         } catch (Exception e) {
420             throw new JsonException("Unable to create RSA Public Key", e);
421         }
422     }
423 
424     /**
425      * Creates a RSAPrivateKey from the JWK.
426      * @return a RSAPrivateKey
427      */
428     public RSAPrivateKey toRSAPrivateKey() {
429 
430         if (getPrivateExponent() == null) {
431             return null;
432         }
433 
434         BigInteger modulus = asPositiveBigInteger(getModulus());
435         BigInteger privateExponent = asPositiveBigInteger(getPrivateExponent());
436 
437         RSAPrivateKeySpec spec;
438 
439         if (getPrimeP() == null) {
440 
441             spec = new RSAPrivateKeySpec(modulus, privateExponent);
442 
443         } else {
444 
445             BigInteger publicExponent = asPositiveBigInteger(getPublicExponent());
446             BigInteger p = asPositiveBigInteger(getPrimeP());
447             BigInteger q = asPositiveBigInteger(getPrimeQ());
448             BigInteger dp = asPositiveBigInteger(getPrimePExponent());
449             BigInteger dq = asPositiveBigInteger(getPrimeQExponent());
450             BigInteger qi = asPositiveBigInteger(getCRTCoefficient());
451 
452             if (getOtherFactors() != null && !getOtherFactors().isEmpty()) {
453 
454                 RSAOtherPrimeInfo[] otherInfo = new RSAOtherPrimeInfo[getOtherFactors().size()];
455 
456                 for (int i = 0; i < getOtherFactors().size(); i++) {
457 
458                     OtherFactors factor = (OtherFactors) getOtherFactors().get(i);
459 
460                     BigInteger factorR = asPositiveBigInteger(factor.getFactor());
461                     BigInteger factorD = asPositiveBigInteger(factor.getCRTExponent());
462                     BigInteger factorT = asPositiveBigInteger(factor.getCRTCoefficient());
463 
464                     otherInfo[i] = new RSAOtherPrimeInfo(factorR, factorD, factorT);
465                 }
466 
467                 spec = new RSAMultiPrimePrivateCrtKeySpec(modulus, publicExponent, privateExponent, p, q, dp, dq, qi,
468                         otherInfo);
469             } else {
470                 spec = new RSAPrivateCrtKeySpec(modulus, publicExponent, privateExponent, p, q, dp, dq, qi);
471             }
472         }
473 
474         try {
475             KeyFactory factory = KeyFactory.getInstance("RSA");
476             RSAPrivateKey priv = (RSAPrivateKey) factory.generatePrivate(spec);
477             return priv;
478         } catch (Exception e) {
479             throw new JsonException("Unable to create private RSA Key", e);
480         }
481     }
482 
483     /**
484      * Create a KeyPair using the JWK.
485      * @return a KeyPair
486      */
487     public KeyPair toKeyPair() {
488         return new KeyPair(toRSAPublicKey(), toRSAPrivateKey());
489     }
490 
491     /**
492      * Parses a RsaJWK from a json string.
493      * @param json a string json object
494      * @return a RsaJWK
495      */
496     public static RsaJWK parse(String json) {
497         JsonValue jwk = new JsonValue(toJsonValue(json));
498         return parse(jwk);
499     }
500 
501     /**
502      * Parses a RsaJWK from a jsonValue Object.
503      * @param json a jsonValue object
504      * @return a RsaJWK
505      */
506     public static RsaJWK parse(JsonValue json) {
507 
508         String n = null, e = null, d = null, p = null, q = null, dq = null, dp = null, qi = null;
509         String x5u = null, x5t = null;
510         List<String> x5c = null;
511         List<Object> factors = null;
512         List<OtherFactors> listOfFactors = null;
513 
514         KeyType kty = null;
515         KeyUse use = null;
516         String alg = null, kid = null;
517 
518         kty = KeyType.getKeyType(json.get(KTY).asString());
519         if (!kty.equals(KeyType.RSA)) {
520             throw new JsonException("Unable to parse RSA JWK; Not an RSA type");
521         }
522 
523         use = KeyUse.getKeyUse(json.get(USE).asString());
524         alg = json.get(ALG).asString();
525         kid = json.get(KID).asString();
526 
527         n = json.get(N).asString();
528         e = json.get(E).asString();
529         d = json.get(D).asString();
530         p = json.get(P).asString();
531         q = json.get(Q).asString();
532         dp = json.get(DP).asString();
533         dq = json.get(DQ).asString();
534         qi = json.get(QI).asString();
535         factors = json.get(FACTORS).asList();
536         x5u = json.get(X5U).asString();
537         x5t = json.get(X5T).asString();
538         x5c = json.get(X5C).asList(String.class);
539         if (factors != null && !factors.isEmpty()) {
540             listOfFactors = new ArrayList<>(factors.size());
541             for (Object factor : factors) {
542                 String r = null, dd = null, t = null;
543                 r = ((JsonValue) factor).get("r").asString();
544                 dd = ((JsonValue) factor).get("d").asString();
545                 t = ((JsonValue) factor).get("t").asString();
546                 OtherFactors of = new OtherFactors(r, dd, t);
547                 listOfFactors.add(of);
548             }
549         }
550 
551         return new RsaJWK(use, alg, kid, n, e, d, p, q, dp, dq, qi, listOfFactors, x5u, x5t, x5c);
552     }
553 
554     /**
555      * Prints the RsaJWK object as a json string.
556      * @return json string
557      */
558     public String toJsonString() {
559         return super.toString();
560     }
561 
562     /**
563      * Base64 decodes the string, and then returns its positive BigInteger representation.
564      * @return a Base64 decoded, positively-forced BigInteger representation of the provided String.
565      */
566     private BigInteger asPositiveBigInteger(String toConvert) {
567         return new BigInteger(BIG_INTEGER_POSITIVE, Base64url.decode(toConvert));
568     }
569 }