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