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