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}