001/* 002 * nimbus-jose-jwt 003 * 004 * Copyright 2012-2016, Connect2id Ltd. 005 * 006 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use 007 * this file except in compliance with the License. You may obtain a copy of the 008 * License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software distributed 013 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 014 * CONDITIONS OF ANY KIND, either express or implied. See the License for the 015 * specific language governing permissions and limitations under the License. 016 */ 017 018package com.nimbusds.jose.jwk; 019 020 021import java.net.URI; 022import java.security.*; 023import java.text.ParseException; 024import java.util.LinkedHashMap; 025import java.util.List; 026import java.util.Set; 027import javax.crypto.SecretKey; 028import javax.crypto.spec.SecretKeySpec; 029 030import com.nimbusds.jose.Algorithm; 031import com.nimbusds.jose.JOSEException; 032import com.nimbusds.jose.util.Base64; 033import com.nimbusds.jose.util.Base64URL; 034import com.nimbusds.jose.util.ByteUtils; 035import com.nimbusds.jose.util.JSONObjectUtils; 036import net.jcip.annotations.Immutable; 037import net.minidev.json.JSONObject; 038 039 040/** 041 * {@link KeyType#OCT Octet sequence} JSON Web Key (JWK), used to represent 042 * symmetric keys. This class is immutable. 043 * 044 * <p>Octet sequence JWKs should specify the algorithm intended to be used with 045 * the key, unless the application uses other means or convention to determine 046 * the algorithm used. 047 * 048 * <p>Example JSON object representation of an octet sequence JWK: 049 * 050 * <pre> 051 * { 052 * "kty" : "oct", 053 * "alg" : "A128KW", 054 * "k" : "GawgguFyGrWKav7AX4VKUg" 055 * } 056 * </pre> 057 * 058 * @author Justin Richer 059 * @author Vladimir Dzhuvinov 060 * @version 2016-12-06 061 */ 062@Immutable 063public final class OctetSequenceKey extends JWK implements SecretJWK { 064 065 066 private static final long serialVersionUID = 1L; 067 068 069 /** 070 * The key value. 071 */ 072 private final Base64URL k; 073 074 075 /** 076 * Builder for constructing octet sequence JWKs. 077 * 078 * <p>Example usage: 079 * 080 * <pre> 081 * OctetSequenceKey key = new OctetSequenceKey.Builder(k). 082 * algorithm(JWSAlgorithm.HS512). 083 * keyID("123"). 084 * build(); 085 * </pre> 086 */ 087 public static class Builder { 088 089 090 /** 091 * The key value. 092 */ 093 private final Base64URL k; 094 095 096 /** 097 * The public key use, optional. 098 */ 099 private KeyUse use; 100 101 102 /** 103 * The key operations, optional. 104 */ 105 private Set<KeyOperation> ops; 106 107 108 /** 109 * The intended JOSE algorithm for the key, optional. 110 */ 111 private Algorithm alg; 112 113 114 /** 115 * The key ID, optional. 116 */ 117 private String kid; 118 119 120 /** 121 * X.509 certificate URL, optional. 122 */ 123 private URI x5u; 124 125 126 /** 127 * X.509 certificate thumbprint, optional. 128 */ 129 private Base64URL x5t; 130 131 132 /** 133 * The X.509 certificate chain, optional. 134 */ 135 private List<Base64> x5c; 136 137 138 /** 139 * Creates a new octet sequence JWK builder. 140 * 141 * @param k The key value. It is represented as the Base64URL 142 * encoding of value's big endian representation. Must 143 * not be {@code null}. 144 */ 145 public Builder(final Base64URL k) { 146 147 if (k == null) { 148 throw new IllegalArgumentException("The key value must not be null"); 149 } 150 151 this.k = k; 152 } 153 154 155 /** 156 * Creates a new octet sequence JWK builder. 157 * 158 * @param key The key value. Must not be empty byte array or 159 * {@code null}. 160 */ 161 public Builder(final byte[] key) { 162 163 this(Base64URL.encode(key)); 164 165 if (key.length == 0) { 166 throw new IllegalArgumentException("The key must have a positive length"); 167 } 168 } 169 170 171 /** 172 * Creates a new octet sequence JWK builder. 173 * 174 * @param secretKey The secret key to represent. Must not be 175 * {@code null}. 176 */ 177 public Builder(final SecretKey secretKey) { 178 179 this(secretKey.getEncoded()); 180 } 181 182 183 /** 184 * Sets the use ({@code use}) of the JWK. 185 * 186 * @param use The key use, {@code null} if not specified or if 187 * the key is intended for signing as well as 188 * encryption. 189 * 190 * @return This builder. 191 */ 192 public Builder keyUse(final KeyUse use) { 193 194 this.use = use; 195 return this; 196 } 197 198 199 /** 200 * Sets the operations ({@code key_ops}) of the JWK (for a 201 * non-public key). 202 * 203 * @param ops The key operations, {@code null} if not 204 * specified. 205 * 206 * @return This builder. 207 */ 208 public Builder keyOperations(final Set<KeyOperation> ops) { 209 210 this.ops = ops; 211 return this; 212 } 213 214 215 /** 216 * Sets the intended JOSE algorithm ({@code alg}) for the JWK. 217 * 218 * @param alg The intended JOSE algorithm, {@code null} if not 219 * specified. 220 * 221 * @return This builder. 222 */ 223 public Builder algorithm(final Algorithm alg) { 224 225 this.alg = alg; 226 return this; 227 } 228 229 /** 230 * Sets the ID ({@code kid}) of the JWK. The key ID can be used 231 * to match a specific key. This can be used, for instance, to 232 * choose a key within a {@link JWKSet} during key rollover. 233 * The key ID may also correspond to a JWS/JWE {@code kid} 234 * header parameter value. 235 * 236 * @param kid The key ID, {@code null} if not specified. 237 * 238 * @return This builder. 239 */ 240 public Builder keyID(final String kid) { 241 242 this.kid = kid; 243 return this; 244 } 245 246 247 /** 248 * Sets the ID ({@code kid}) of the JWK to its SHA-256 JWK 249 * thumbprint (RFC 7638). The key ID can be used to match a 250 * specific key. This can be used, for instance, to choose a 251 * key within a {@link JWKSet} during key rollover. The key ID 252 * may also correspond to a JWS/JWE {@code kid} header 253 * parameter value. 254 * 255 * @return This builder. 256 * 257 * @throws JOSEException If the SHA-256 hash algorithm is not 258 * supported. 259 */ 260 public Builder keyIDFromThumbprint() 261 throws JOSEException { 262 263 return keyIDFromThumbprint("SHA-256"); 264 } 265 266 267 /** 268 * Sets the ID ({@code kid}) of the JWK to its JWK thumbprint 269 * (RFC 7638). The key ID can be used to match a specific key. 270 * This can be used, for instance, to choose a key within a 271 * {@link JWKSet} during key rollover. The key ID may also 272 * correspond to a JWS/JWE {@code kid} header parameter value. 273 * 274 * @param hashAlg The hash algorithm for the JWK thumbprint 275 * computation. Must not be {@code null}. 276 * 277 * @return This builder. 278 * 279 * @throws JOSEException If the hash algorithm is not 280 * supported. 281 */ 282 public Builder keyIDFromThumbprint(final String hashAlg) 283 throws JOSEException { 284 285 // Put mandatory params in sorted order 286 LinkedHashMap<String,String> requiredParams = new LinkedHashMap<>(); 287 requiredParams.put("k", k.toString()); 288 requiredParams.put("kty", KeyType.OCT.getValue()); 289 this.kid = ThumbprintUtils.compute(hashAlg, requiredParams).toString(); 290 return this; 291 } 292 293 294 /** 295 * Sets the X.509 certificate URL ({@code x5u}) of the JWK. 296 * 297 * @param x5u The X.509 certificate URL, {@code null} if not 298 * specified. 299 * 300 * @return This builder. 301 */ 302 public Builder x509CertURL(final URI x5u) { 303 304 this.x5u = x5u; 305 return this; 306 } 307 308 309 /** 310 * Sets the X.509 certificate thumbprint ({@code x5t}) of the 311 * JWK. 312 * 313 * @param x5t The X.509 certificate thumbprint, {@code null} if 314 * not specified. 315 * 316 * @return This builder. 317 */ 318 public Builder x509CertThumbprint(final Base64URL x5t) { 319 320 this.x5t = x5t; 321 return this; 322 } 323 324 /** 325 * Sets the X.509 certificate chain ({@code x5c}) of the JWK. 326 * 327 * @param x5c The X.509 certificate chain as a unmodifiable 328 * list, {@code null} if not specified. 329 * 330 * @return This builder. 331 */ 332 public Builder x509CertChain(final List<Base64> x5c) { 333 334 this.x5c = x5c; 335 return this; 336 } 337 338 /** 339 * Builds a new octet sequence JWK. 340 * 341 * @return The octet sequence JWK. 342 * 343 * @throws IllegalStateException If the JWK parameters were 344 * inconsistently specified. 345 */ 346 public OctetSequenceKey build() { 347 348 try { 349 return new OctetSequenceKey(k, use, ops, alg, kid, x5u, x5t, x5c); 350 351 } catch (IllegalArgumentException e) { 352 353 throw new IllegalStateException(e.getMessage(), e); 354 } 355 } 356 } 357 358 359 /** 360 * Creates a new octet sequence JSON Web Key (JWK) with the specified 361 * parameters. 362 * 363 * @param k The key value. It is represented as the Base64URL 364 * encoding of the value's big endian representation. Must 365 * not be {@code null}. 366 * @param use The key use, {@code null} if not specified or if the key 367 * is intended for signing as well as encryption. 368 * @param ops The key operations, {@code null} if not specified. 369 * @param alg The intended JOSE algorithm for the key, {@code null} if 370 * not specified. 371 * @param kid The key ID. {@code null} if not specified. 372 * @param x5u The X.509 certificate URL, {@code null} if not specified. 373 * @param x5t The X.509 certificate thumbprint, {@code null} if not 374 * specified. 375 * @param x5c The X.509 certificate chain, {@code null} if not 376 * specified. 377 */ 378 public OctetSequenceKey(final Base64URL k, 379 final KeyUse use, final Set<KeyOperation> ops, final Algorithm alg, final String kid, 380 final URI x5u, final Base64URL x5t, final List<Base64> x5c) { 381 382 super(KeyType.OCT, use, ops, alg, kid, x5u, x5t, x5c); 383 384 if (k == null) { 385 throw new IllegalArgumentException("The key value must not be null"); 386 } 387 388 this.k = k; 389 } 390 391 392 /** 393 * Returns the value of this octet sequence key. 394 * 395 * @return The key value. It is represented as the Base64URL encoding 396 * of the value's big endian representation. 397 */ 398 public Base64URL getKeyValue() { 399 400 return k; 401 } 402 403 404 /** 405 * Returns a copy of this octet sequence key value as a byte array. 406 * 407 * @return The key value as a byte array. 408 */ 409 public byte[] toByteArray() { 410 411 return getKeyValue().decode(); 412 } 413 414 415 /** 416 * Returns a secret key representation of this octet sequence key. 417 * 418 * @return The secret key representation, with an algorithm set to 419 * {@code NONE}. 420 */ 421 @Override 422 public SecretKey toSecretKey() { 423 424 return toSecretKey("NONE"); 425 } 426 427 428 /** 429 * Returns a secret key representation of this octet sequence key with 430 * the specified Java Cryptography Architecture (JCA) algorithm. 431 * 432 * @param jcaAlg The JCA algorithm. Must not be {@code null}. 433 * 434 * @return The secret key representation. 435 */ 436 public SecretKey toSecretKey(final String jcaAlg) { 437 438 return new SecretKeySpec(toByteArray(), jcaAlg); 439 } 440 441 442 @Override 443 public LinkedHashMap<String,?> getRequiredParams() { 444 445 // Put mandatory params in sorted order 446 LinkedHashMap<String,String> requiredParams = new LinkedHashMap<>(); 447 requiredParams.put("k", k.toString()); 448 requiredParams.put("kty", getKeyType().toString()); 449 return requiredParams; 450 } 451 452 453 /** 454 * Octet sequence (symmetric) keys are never considered public, this 455 * method always returns {@code true}. 456 * 457 * @return {@code true} 458 */ 459 @Override 460 public boolean isPrivate() { 461 462 return true; 463 } 464 465 466 /** 467 * Octet sequence (symmetric) keys are never considered public, this 468 * method always returns {@code null}. 469 * 470 * @return {@code null} 471 */ 472 @Override 473 public OctetSequenceKey toPublicJWK() { 474 475 return null; 476 } 477 478 479 @Override 480 public int size() { 481 482 return ByteUtils.bitLength(k.decode()); 483 } 484 485 486 @Override 487 public JSONObject toJSONObject() { 488 489 JSONObject o = super.toJSONObject(); 490 491 // Append key value 492 o.put("k", k.toString()); 493 494 return o; 495 } 496 497 498 /** 499 * Parses an octet sequence JWK from the specified JSON object string 500 * representation. 501 * 502 * @param s The JSON object string to parse. Must not be {@code null}. 503 * 504 * @return The octet sequence JWK. 505 * 506 * @throws ParseException If the string couldn't be parsed to an octet 507 * sequence JWK. 508 */ 509 public static OctetSequenceKey parse(final String s) 510 throws ParseException { 511 512 return parse(JSONObjectUtils.parse(s)); 513 } 514 515 516 /** 517 * Parses an octet sequence JWK from the specified JSON object 518 * representation. 519 * 520 * @param jsonObject The JSON object to parse. Must not be 521 * {@code null}. 522 * 523 * @return The octet sequence JWK. 524 * 525 * @throws ParseException If the JSON object couldn't be parsed to an 526 * octet sequence JWK. 527 */ 528 public static OctetSequenceKey parse(final JSONObject jsonObject) 529 throws ParseException { 530 531 // Parse the mandatory parameters first 532 Base64URL k = new Base64URL(JSONObjectUtils.getString(jsonObject, "k")); 533 534 // Check key type 535 KeyType kty = JWKMetadata.parseKeyType(jsonObject); 536 537 if (kty != KeyType.OCT) { 538 539 throw new ParseException("The key type \"kty\" must be oct", 0); 540 } 541 542 return new OctetSequenceKey(k, 543 JWKMetadata.parseKeyUse(jsonObject), 544 JWKMetadata.parseKeyOperations(jsonObject), 545 JWKMetadata.parseAlgorithm(jsonObject), 546 JWKMetadata.parseKeyID(jsonObject), 547 JWKMetadata.parseX509CertURL(jsonObject), 548 JWKMetadata.parseX509CertThumbprint(jsonObject), 549 JWKMetadata.parseX509CertChain(jsonObject)); 550 } 551 552 553 /** 554 * Loads an octet sequence JWK from the specified JCA key store. 555 * 556 * @param keyStore The key store. Must not be {@code null}. 557 * @param alias The alias. Must not be {@code null}. 558 * @param pin The pin to unlock the private key if any, empty or 559 * {@code null} if not required. 560 * 561 * @return The octet sequence JWK, {@code null} if no key with the 562 * specified alias was found. 563 * 564 * @throws KeyStoreException On a key store exception. 565 * @throws JOSEException If octet sequence key loading failed. 566 */ 567 public static OctetSequenceKey load(final KeyStore keyStore, final String alias, final char[] pin) 568 throws KeyStoreException, JOSEException { 569 570 Key key; 571 try { 572 key = keyStore.getKey(alias, pin); 573 } catch (UnrecoverableKeyException | NoSuchAlgorithmException e) { 574 throw new JOSEException("Couldn't retrieve secret key (bad pin?): " + e.getMessage(), e); 575 } 576 577 if (! (key instanceof SecretKey)) { 578 return null; 579 } 580 581 return new OctetSequenceKey.Builder((SecretKey)key) 582 .keyID(alias) 583 .build(); 584 } 585}