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-2016 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.Base64url;
038
039/**
040 * Implements a RsaJWK.
041 */
042public class RsaJWK extends JWK {
043
044    private static final int BIG_INTEGER_POSITIVE = 1;
045
046    /**
047     * Holds the other prime factors.
048     */
049    public static class OtherFactors extends JsonValue {
050        /**
051         * The R key value.
052         */
053        private final static String R = "r";
054
055        /**
056         * The D key value.
057         */
058        private final static String D = "d";
059
060        /**
061         * The T key value.
062         */
063        private final static String T = "t";
064
065        /**
066         * Creates the other prime factors.
067         * @param r r value
068         * @param d d value
069         * @param t t value
070         */
071        public OtherFactors(String r, String d, String t) {
072            super(new HashMap<>());
073            put(R, r);
074            put(D, d);
075            put(T, t);
076        }
077
078        /**
079         * Create other prime factors.
080         * @param info RSAOtherPrimeInfo used to create the other prime factors object.
081         */
082        public OtherFactors(RSAOtherPrimeInfo info) {
083            super(new HashMap<>());
084            put(R, Base64url.encode(info.getPrime().toByteArray()));
085            put(D, Base64url.encode(info.getExponent().toByteArray()));
086            put(T, Base64url.encode(info.getCrtCoefficient().toByteArray()));
087        }
088
089        /**
090         * Get the R value.
091         * @return the R value
092         */
093        public String getFactor() {
094            return get(R).asString();
095        }
096
097        /**
098         * Get the D value.
099         * @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}