001/*
002 * The contents of this file are subject to the terms of the Common Development and
003 * Distribution License (the License). You may not use this file except in compliance with the
004 * License.
005 *
006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
007 * specific language governing permission and limitations under the License.
008 *
009 * When distributing Covered Software, include this CDDL Header Notice in each file and include
010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
011 * Header, with the fields enclosed by brackets [] replaced by your own identifying
012 * information: "Portions Copyrighted [year] [name of copyright owner]".
013 *
014 * Copyright 2013-2015 ForgeRock AS.
015 */
016
017package org.forgerock.json.jose.jwk;
018
019import java.math.BigInteger;
020import java.security.KeyFactory;
021import java.security.KeyPair;
022import java.security.interfaces.RSAPrivateCrtKey;
023import java.security.interfaces.RSAPrivateKey;
024import java.security.interfaces.RSAPublicKey;
025import java.security.spec.RSAMultiPrimePrivateCrtKeySpec;
026import java.security.spec.RSAOtherPrimeInfo;
027import java.security.spec.RSAPrivateCrtKeySpec;
028import java.security.spec.RSAPrivateKeySpec;
029import java.security.spec.RSAPublicKeySpec;
030import java.util.ArrayList;
031import java.util.Collections;
032import java.util.HashMap;
033import java.util.List;
034
035import org.forgerock.json.JsonException;
036import org.forgerock.json.JsonValue;
037import org.forgerock.util.encode.Base64;
038import org.forgerock.util.encode.Base64url;
039
040/**
041 * Implements a RsaJWK.
042 */
043public class RsaJWK extends JWK {
044
045    private static final int BIG_INTEGER_POSITIVE = 1;
046
047    /**
048     * Holds the other prime factors.
049     */
050    public static class OtherFactors extends JsonValue {
051        /**
052         * The R key value.
053         */
054        private final static String R = "r";
055
056        /**
057         * The D key value.
058         */
059        private final static String D = "d";
060
061        /**
062         * The T key value.
063         */
064        private final static String T = "t";
065
066        /**
067         * Creates the other prime factors.
068         * @param r r value
069         * @param d d value
070         * @param t t value
071         */
072        public OtherFactors(String r, String d, String t) {
073            super(new HashMap<>());
074            put(R, r);
075            put(D, d);
076            put(T, t);
077        }
078
079        /**
080         * Create other prime factors.
081         * @param info RSAOtherPrimeInfo used to create the other prime factors object.
082         */
083        public OtherFactors(RSAOtherPrimeInfo info) {
084            super(new HashMap<>());
085            put(R, Base64url.encode(info.getPrime().toByteArray()));
086            put(D, Base64url.encode(info.getExponent().toByteArray()));
087            put(T, Base64url.encode(info.getCrtCoefficient().toByteArray()));
088        }
089
090        /**
091         * Get the R value.
092         * @return the R value
093         */
094        public String getFactor() {
095            return get(R).asString();
096        }
097
098        /**
099         * Get the D value.
100         * @return the D value.
101         */
102        public String getCRTExponent() {
103            return get(D).asString();
104        }
105
106        /**
107         * Get the T value.
108         * @return the T value
109         */
110        public String getCRTCoefficient() {
111            return get(T).asString();
112        }
113    }
114
115    /**
116     * The N key.
117     */
118    private final static String N = "n";
119
120    /**
121     * The E key.
122     */
123    private final static String E = "e";
124
125    /**
126     * The D key.
127     */
128    private final static String D = "d";
129
130    /**
131     * The P key.
132     */
133    private final static String P = "p";
134
135    /**
136     * The Q key.
137     */
138    private final static String Q = "q";
139
140    /**
141     * The DP key.
142     */
143    private final static String DP = "dp";
144
145    /**
146     * The DQ key.
147     */
148    private final static String DQ = "dq";
149
150    /**
151     * The QI key.
152     */
153    private final static String QI = "qi";
154
155    /**
156     * The factors key.
157     */
158    private final static String FACTORS = "factors";
159
160    /**
161     * Creates a RsaJWK.
162     * @param use the use of the JWK
163     * @param alg the alg of the JWK
164     * @param kid the key id of the JWK
165     * @param n the modulus of the JWK
166     * @param e the public exponent JWK
167     * @param x5u the x509 url for the key
168     * @param x5t the x509 thumbnail for the key
169     * @param x5c the x509 chain
170     */
171    public RsaJWK(KeyUse use, String alg, String kid, String n, String e, String x5u, String x5t, List<Base64> x5c) {
172        this (use, alg, kid, n, e, null, null, null, null, null, null, null, x5u, x5t, x5c);
173    }
174
175    /**
176     * Creates a RsaJWK.
177     * @param use the use of the JWK
178     * @param alg the alg of the JWK
179     * @param kid the key id of the JWK
180     * @param n the modulus of the JWK
181     * @param e the public exponent JWK
182     * @param d the private exponent JWK
183     * @param x5u the x509 url for the key
184     * @param x5t the x509 thumbnail for the key
185     * @param x5c the x509 chain
186     */
187    public RsaJWK(KeyUse use, String alg, String kid, String n, String e, String d, String x5u, String x5t,
188                  List<Base64> x5c) {
189        this (use, alg, kid, n, e, d, null, null, null, null, null, null, x5u, x5t, x5c);
190    }
191
192    /**
193     * Creates a RsaJWK.
194     * @param use the use of the JWK
195     * @param alg the alg of the JWK
196     * @param kid the key id of the JWK
197     * @param n the modulus of the JWK
198     * @param e the public exponent JWK
199     * @param p the first prime factor of the JWK
200     * @param q the second prime factor of the JWK
201     * @param dp the first factor exponent of the JWK
202     * @param dq the second factor exponent of the JWK
203     * @param qi the first CRT Coefficient of the JWK
204     * @param x5u the x509 url for the key
205     * @param x5t the x509 thumbnail for the key
206     * @param x5c the x509 chain
207     */
208    public RsaJWK(KeyUse use, String alg, String kid, String n, String e, String p, String q, String dp,
209                  String dq, String qi, String x5u, String x5t, List<Base64> x5c) {
210        this (use, alg, kid, n, e, null, p, q, dp, dq, qi, null, x5u, x5t, x5c);
211    }
212
213    /**
214     * Creates a RsaJWK.
215     * @param use the use of the JWK
216     * @param alg the alg of the JWK
217     * @param kid the key id of the JWK
218     * @param n the modulus of the JWK
219     * @param e the public exponent JWK
220     * @param d the private exponent JWK
221     * @param p the first prime factor of the JWK
222     * @param q the second prime factor of the JWK
223     * @param dp the first factor exponent of the JWK
224     * @param dq the second factor exponent of the JWK
225     * @param qi the first CRT Coefficient of the JWK
226     * @param factors the extra factors of the JWK
227     * @param x5u the x509 url for the key
228     * @param x5t the x509 thumbnail for the key
229     * @param x5c the x509 chain
230     */
231    public RsaJWK(KeyUse use, String alg, String kid, String n, String e, String d, String p, String q,
232                  String dp, String dq, String qi, List<OtherFactors> factors,
233                  String x5u, String x5t, List<Base64> x5c) {
234        super(KeyType.RSA, use, alg, kid, x5u, x5t, x5c);
235        if (n != null && !n.isEmpty()) {
236            put(N, n);
237        }
238        if (e != null && !e.isEmpty()) {
239            put(E, e);
240        }
241        if (d != null && !d.isEmpty()) {
242            put(D, d);
243        }
244        if (p != null && !p.isEmpty()) {
245            put(P, p);
246        }
247        if (q != null && !q.isEmpty()) {
248            put(Q, q);
249        }
250        if (dp != null && !dp.isEmpty()) {
251            put(DP, dp);
252        }
253        if (dq != null && !dq.isEmpty()) {
254            put(DQ, dq);
255        }
256        if (qi != null && !qi.isEmpty()) {
257            put(QI, qi);
258        }
259        if (factors == null) {
260            put(FACTORS, Collections.EMPTY_LIST);
261        } else {
262            put(FACTORS, factors);
263        }
264        if (x5u != null && !x5u.isEmpty()) {
265            put(X5U, x5u);
266        }
267        if (x5t != null && !x5t.isEmpty()) {
268            put(X5T, x5t);
269        }
270        if (x5c != null && !x5c.isEmpty()) {
271            put(X5C, x5c);
272        }
273
274
275    }
276
277    /**
278     * Creates a RsaJWK.
279     * @param use the use of the JWK
280     * @param alg the alg of the JWK
281     * @param kid the key id of the JWK
282     * @param key the RSAPublicKey to use
283     * @param x5u the x509 url for the key
284     * @param x5t the x509 thumbnail for the key
285     * @param x5c the x509 chain
286     */
287    public RsaJWK(RSAPublicKey key, KeyUse use, String alg, String kid, String x5u, String x5t, List<Base64> x5c) {
288        this(use, alg, kid,
289                Base64url.encode(key.getModulus().toByteArray()),
290                Base64url.encode(key.getPublicExponent().toByteArray()),
291                x5u, x5t, x5c);
292    }
293    /**
294     * Creates a RsaJWK.
295     * @param use the use of the JWK
296     * @param alg the alg of the JWK
297     * @param kid the key id of the JWK
298     * @param pubKey the RSAPublicKey to use
299     * @param privKey the RSAPrivateKey to use
300     * @param x5u the x509 url for the key
301     * @param x5t the x509 thumbnail for the key
302     * @param x5c the x509 chain
303     */
304    public RsaJWK(RSAPublicKey pubKey, RSAPrivateKey privKey, KeyUse use, String alg, String kid,
305                  String x5u, String x5t, List<Base64> x5c) {
306        this(use, alg, kid,
307                Base64url.encode(pubKey.getModulus().toByteArray()),
308                Base64url.encode(pubKey.getPublicExponent().toByteArray()),
309                Base64url.encode(privKey.getPrivateExponent().toByteArray()),
310                x5u, x5t, x5c);
311    }
312    /**
313     * Creates a RsaJWK.
314     * @param use the use of the JWK
315     * @param alg the alg of the JWK
316     * @param kid the key id of the JWK
317     * @param pubKey the RSAPublicKey to use
318     * @param privCert the RSAPrivateCrtKey to use
319     * @param x5u the x509 url for the key
320     * @param x5t the x509 thumbnail for the key
321     * @param x5c the x509 chain
322     */
323    public RsaJWK(RSAPublicKey pubKey, RSAPrivateCrtKey privCert, KeyUse use, String alg, String kid,
324                  String x5u, String x5t, List<Base64> x5c) {
325        this(use, alg, kid,
326                Base64url.encode(pubKey.getModulus().toByteArray()),
327                Base64url.encode(pubKey.getPublicExponent().toByteArray()),
328                Base64url.encode(privCert.getPrivateExponent().toByteArray()),
329                Base64url.encode(privCert.getPrimeP().toByteArray()),
330                Base64url.encode(privCert.getPrimeQ().toByteArray()),
331                Base64url.encode(privCert.getPrimeExponentP().toByteArray()),
332                Base64url.encode(privCert.getPrimeExponentQ().toByteArray()),
333                Base64url.encode(privCert.getCrtCoefficient().toByteArray()),
334                null, x5u, x5t, x5c);
335
336    }
337
338    /**
339     * Get the RSA modulus value.
340     * @return a Base64url modulus value
341     */
342    public String getModulus() {
343        return get(N).asString();
344    }
345
346    /**
347     * Get the RSA Public Exponent.
348     * @return a Base64url Public Exponent value
349     */
350    public String getPublicExponent() {
351        return get(E).asString();
352    }
353
354    /**
355     * Get the RSA Private Exponent value.
356     * @return a Base64url Private Exponent value
357     */
358    public String getPrivateExponent() {
359        return get(D).asString();
360    }
361
362    /**
363     * Get the RSA First Prime Factor value.
364     * @return a Base64url First Prime Factor value
365     */
366    public String getPrimeP() {
367        return get(P).asString();
368    }
369
370    /**
371     * Get the RSA Second Prime Factor value.
372     * @return a Base64url Second Prime Factor value
373     */
374    public String getPrimeQ() {
375        return get(Q).asString();
376    }
377
378    /**
379     * Get the RSA First Factor CRT Exponent value.
380     * @return a Base64url First Factor CRT Exponent value
381     */
382    public String getPrimePExponent() {
383        return get(DP).asString();
384    }
385
386    /**
387     * Get the RSA Second factor CRT Exponent value.
388     * @return a Base64url Second factor CRT Exponent value
389     */
390    public String getPrimeQExponent() {
391        return get(DQ).asString();
392    }
393
394    /**
395     * Get the RSA First CRT Coefficient value.
396     * @return a Base64url First CRT Coefficient value
397     */
398    public String getCRTCoefficient() {
399        return get(QI).asString();
400    }
401
402    /**
403     * Get the RSA other factors value.
404     * @return a Base64url other factors value
405     */
406    public List<Object> getOtherFactors() {
407        return get(FACTORS).asList();
408    }
409
410    /**
411     * Creates a RSAPublicKey from the JWK.
412     * @return a RSAPublicKey
413     */
414    public RSAPublicKey toRSAPublicKey() {
415        try {
416            RSAPublicKeySpec spec = new RSAPublicKeySpec(asPositiveBigInteger(getModulus()),
417                    asPositiveBigInteger(getPublicExponent()));
418            KeyFactory factory = KeyFactory.getInstance("RSA");
419            return (RSAPublicKey) factory.generatePublic(spec);
420        } catch (Exception e) {
421            throw new JsonException("Unable to create RSA Public Key", e);
422        }
423    }
424
425    /**
426     * Creates a RSAPrivateKey from the JWK.
427     * @return a RSAPrivateKey
428     */
429    public RSAPrivateKey toRSAPrivateKey() {
430
431        if (getPrivateExponent() == null) {
432            return null;
433        }
434
435        BigInteger modulus = asPositiveBigInteger(getModulus());
436        BigInteger privateExponent = asPositiveBigInteger(getPrivateExponent());
437
438        RSAPrivateKeySpec spec;
439
440        if (getPrimeP() == null) {
441
442            spec = new RSAPrivateKeySpec(modulus, privateExponent);
443
444        } else {
445
446            BigInteger publicExponent = asPositiveBigInteger(getPublicExponent());
447            BigInteger p = asPositiveBigInteger(getPrimeP());
448            BigInteger q = asPositiveBigInteger(getPrimeQ());
449            BigInteger dp = asPositiveBigInteger(getCRTCoefficient());
450            BigInteger dq = asPositiveBigInteger(getPrimeQExponent());
451            BigInteger qi = asPositiveBigInteger(getCRTCoefficient());
452
453            if (getOtherFactors() != null && !getOtherFactors().isEmpty()) {
454
455                RSAOtherPrimeInfo[] otherInfo = new RSAOtherPrimeInfo[getOtherFactors().size()];
456
457                for (int i = 0; i < getOtherFactors().size(); i++) {
458
459                    OtherFactors factor = (OtherFactors) getOtherFactors().get(i);
460
461                    BigInteger factorR = asPositiveBigInteger(factor.getFactor());
462                    BigInteger factorD = asPositiveBigInteger(factor.getCRTExponent());
463                    BigInteger factorT = asPositiveBigInteger(factor.getCRTCoefficient());
464
465                    otherInfo[i] = new RSAOtherPrimeInfo(factorR, factorD, factorT);
466                }
467
468                spec = new RSAMultiPrimePrivateCrtKeySpec(modulus, publicExponent, privateExponent, p, q, dp, dq, qi,
469                        otherInfo);
470            } else {
471                spec = new RSAPrivateCrtKeySpec(modulus, publicExponent, privateExponent, p, q, dp, dq, qi);
472            }
473        }
474
475        try {
476            KeyFactory factory = KeyFactory.getInstance("RSA");
477            RSAPrivateKey priv = (RSAPrivateKey) factory.generatePrivate(spec);
478            return priv;
479        } catch (Exception e) {
480            throw new JsonException("Unable to create private RSA Key", e);
481        }
482    }
483
484    /**
485     * Create a KeyPair using the JWK.
486     * @return a KeyPair
487     */
488    public KeyPair toKeyPair() {
489        return new KeyPair(toRSAPublicKey(), toRSAPrivateKey());
490    }
491
492    /**
493     * Parses a RsaJWK from a json string.
494     * @param json a string json object
495     * @return a RsaJWK
496     */
497    public static RsaJWK parse(String json) {
498        JsonValue jwk = new JsonValue(toJsonValue(json));
499        return parse(jwk);
500    }
501
502    /**
503     * Parses a RsaJWK from a jsonValue Object.
504     * @param json a jsonValue object
505     * @return a RsaJWK
506     */
507    public static RsaJWK parse(JsonValue json) {
508
509        String n = null, e = null, d = null, p = null, q = null, dq = null, dp = null, qi = null;
510        String x5u = null, x5t = null;
511        List<Base64> x5c = null;
512        List<Object> factors = null;
513        List<OtherFactors> listOfFactors = null;
514
515        KeyType kty = null;
516        KeyUse use = null;
517        String alg = null, kid = null;
518
519        kty = KeyType.getKeyType(json.get(KTY).asString());
520        if (!kty.equals(KeyType.RSA)) {
521            throw new JsonException("Unable to parse RSA JWK; Not an RSA type");
522        }
523
524        use = KeyUse.getKeyUse(json.get(USE).asString());
525        alg = json.get(ALG).asString();
526        kid = json.get(KID).asString();
527
528        n = json.get(N).asString();
529        e = json.get(E).asString();
530        d = json.get(D).asString();
531        p = json.get(P).asString();
532        q = json.get(Q).asString();
533        dp = json.get(DP).asString();
534        dq = json.get(DQ).asString();
535        qi = json.get(QI).asString();
536        factors = json.get(FACTORS).asList();
537        x5u = json.get(X5U).asString();
538        x5t = json.get(X5T).asString();
539        x5c = json.get(X5C).asList(Base64.class);
540        if (factors != null && !factors.isEmpty()) {
541            listOfFactors = new ArrayList<>(factors.size());
542            for (Object factor : factors) {
543                String r = null, dd = null, t = null;
544                r = ((JsonValue) factor).get("r").asString();
545                dd = ((JsonValue) factor).get("d").asString();
546                t = ((JsonValue) factor).get("t").asString();
547                OtherFactors of = new OtherFactors(r, dd, t);
548                listOfFactors.add(of);
549            }
550        }
551
552        return new RsaJWK(use, alg, kid, n, e, d, p, q, dp, dq, qi, listOfFactors, x5u, x5t, x5c);
553    }
554
555    /**
556     * Prints the RsaJWK object as a json string.
557     * @return json string
558     */
559    public String toJsonString() {
560        return super.toString();
561    }
562
563    /**
564     * Base64 decodes the string, and then returns its positive BigInteger representation.
565     * @return a Base64 decoded, positively-forced BigInteger representation of the provided String.
566     */
567    private BigInteger asPositiveBigInteger(String toConvert) {
568        return new BigInteger(BIG_INTEGER_POSITIVE, Base64url.decode(toConvert));
569    }
570}