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}