1 /* 2 * Copyright (c) 1998, 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package javax.crypto; 27 28 import java.util.*; 29 30 import java.security.*; 31 import java.security.Provider.Service; 32 import java.security.spec.AlgorithmParameterSpec; 33 34 import java.nio.ByteBuffer; 35 36 import sun.security.util.Debug; 37 import sun.security.jca.*; 38 import sun.security.jca.GetInstance.Instance; 39 40 /** 41 * This class provides the functionality of a "Message Authentication Code" 42 * (MAC) algorithm. 43 * 44 * <p> A MAC provides a way to check 45 * the integrity of information transmitted over or stored in an unreliable 46 * medium, based on a secret key. Typically, message 47 * authentication codes are used between two parties that share a secret 48 * key in order to validate information transmitted between these 49 * parties. 50 * 51 * <p> A MAC mechanism that is based on cryptographic hash functions is 52 * referred to as HMAC. HMAC can be used with any cryptographic hash function, 53 * e.g., MD5 or SHA-1, in combination with a secret shared key. HMAC is 54 * specified in RFC 2104. 55 * 56 * <p> Every implementation of the Java platform is required to support 57 * the following standard {@code Mac} algorithms: 58 * <ul> 59 * <li>{@code HmacMD5}</li> 60 * <li>{@code HmacSHA1}</li> 61 * <li>{@code HmacSHA256}</li> 62 * </ul> 63 * These algorithms are described in the 64 * <a href="{@docRoot}/../technotes/guides/security/StandardNames.html#Mac"> 65 * Mac section</a> of the 66 * Java Cryptography Architecture Standard Algorithm Name Documentation. 67 * Consult the release documentation for your implementation to see if any 68 * other algorithms are supported. 69 * 70 * @author Jan Luehe 71 * 72 * @since 1.4 73 */ 74 75 public class Mac implements Cloneable { 76 77 private static final Debug debug = 78 Debug.getInstance("jca", "Mac"); 79 80 private static final Debug pdebug = 81 Debug.getInstance("provider", "Provider"); 82 private static final boolean skipDebug = 83 Debug.isOn("engine=") && !Debug.isOn("mac"); 84 85 // The provider 86 private Provider provider; 87 88 // The provider implementation (delegate) 89 private MacSpi spi; 90 91 // The name of the MAC algorithm. 92 private final String algorithm; 93 94 // Has this object been initialized? 95 private boolean initialized = false; 96 97 // next service to try in provider selection 98 // null once provider is selected 99 private Service firstService; 100 101 // remaining services to try in provider selection 102 // null once provider is selected 103 private Iterator<Service> serviceIterator; 104 105 private final Object lock; 106 107 /** 108 * Creates a MAC object. 109 * 110 * @param macSpi the delegate 111 * @param provider the provider 112 * @param algorithm the algorithm 113 */ 114 protected Mac(MacSpi macSpi, Provider provider, String algorithm) { 115 this.spi = macSpi; 116 this.provider = provider; 117 this.algorithm = algorithm; 118 serviceIterator = null; 119 lock = null; 120 } 121 122 private Mac(Service s, Iterator<Service> t, String algorithm) { 123 firstService = s; 124 serviceIterator = t; 125 this.algorithm = algorithm; 126 lock = new Object(); 127 } 128 129 /** 130 * Returns the algorithm name of this {@code Mac} object. 131 * 132 * <p>This is the same name that was specified in one of the 133 * {@code getInstance} calls that created this 134 * {@code Mac} object. 135 * 136 * @return the algorithm name of this {@code Mac} object. 137 */ 138 public final String getAlgorithm() { 139 return this.algorithm; 140 } 141 142 /** 143 * Returns a {@code Mac} object that implements the 144 * specified MAC algorithm. 145 * 146 * <p> This method traverses the list of registered security Providers, 147 * starting with the most preferred Provider. 148 * A new Mac object encapsulating the 149 * MacSpi implementation from the first 150 * Provider that supports the specified algorithm is returned. 151 * 152 * <p> Note that the list of registered providers may be retrieved via 153 * the {@link Security#getProviders() Security.getProviders()} method. 154 * 155 * @implNote 156 * The JDK Reference Implementation additionally uses the 157 * {@code jdk.security.provider.preferred} 158 * {@link Security#getProperty(String) Security} property to determine 159 * the preferred provider order for the specified algorithm. This 160 * may be different than the order of providers returned by 161 * {@link Security#getProviders() Security.getProviders()}. 162 * 163 * @param algorithm the standard name of the requested MAC algorithm. 164 * See the Mac section in the <a href= 165 * "{@docRoot}/../technotes/guides/security/StandardNames.html#Mac"> 166 * Java Cryptography Architecture Standard Algorithm Name Documentation</a> 167 * for information about standard algorithm names. 168 * 169 * @return the new {@code Mac} object. 170 * 171 * @exception NoSuchAlgorithmException if no Provider supports a 172 * MacSpi implementation for the 173 * specified algorithm. 174 * 175 * @see java.security.Provider 176 */ 177 public static final Mac getInstance(String algorithm) 178 throws NoSuchAlgorithmException { 179 List<Service> services = GetInstance.getServices("Mac", algorithm); 180 // make sure there is at least one service from a signed provider 181 Iterator<Service> t = services.iterator(); 182 while (t.hasNext()) { 183 Service s = t.next(); 184 if (JceSecurity.canUseProvider(s.getProvider()) == false) { 185 continue; 186 } 187 return new Mac(s, t, algorithm); 188 } 189 throw new NoSuchAlgorithmException 190 ("Algorithm " + algorithm + " not available"); 191 } 192 193 /** 194 * Returns a {@code Mac} object that implements the 195 * specified MAC algorithm. 196 * 197 * <p> A new Mac object encapsulating the 198 * MacSpi implementation from the specified provider 199 * is returned. The specified provider must be registered 200 * in the security provider list. 201 * 202 * <p> Note that the list of registered providers may be retrieved via 203 * the {@link Security#getProviders() Security.getProviders()} method. 204 * 205 * @param algorithm the standard name of the requested MAC algorithm. 206 * See the Mac section in the <a href= 207 * "{@docRoot}/../technotes/guides/security/StandardNames.html#Mac"> 208 * Java Cryptography Architecture Standard Algorithm Name Documentation</a> 209 * for information about standard algorithm names. 210 * 211 * @param provider the name of the provider. 212 * 213 * @return the new {@code Mac} object. 214 * 215 * @exception NoSuchAlgorithmException if a MacSpi 216 * implementation for the specified algorithm is not 217 * available from the specified provider. 218 * 219 * @exception NoSuchProviderException if the specified provider is not 220 * registered in the security provider list. 221 * 222 * @exception IllegalArgumentException if the {@code provider} 223 * is null or empty. 224 * 225 * @see java.security.Provider 226 */ 227 public static final Mac getInstance(String algorithm, String provider) 228 throws NoSuchAlgorithmException, NoSuchProviderException { 229 Instance instance = JceSecurity.getInstance 230 ("Mac", MacSpi.class, algorithm, provider); 231 return new Mac((MacSpi)instance.impl, instance.provider, algorithm); 232 } 233 234 /** 235 * Returns a {@code Mac} object that implements the 236 * specified MAC algorithm. 237 * 238 * <p> A new Mac object encapsulating the 239 * MacSpi implementation from the specified Provider 240 * object is returned. Note that the specified Provider object 241 * does not have to be registered in the provider list. 242 * 243 * @param algorithm the standard name of the requested MAC algorithm. 244 * See the Mac section in the <a href= 245 * "{@docRoot}/../technotes/guides/security/StandardNames.html#Mac"> 246 * Java Cryptography Architecture Standard Algorithm Name Documentation</a> 247 * for information about standard algorithm names. 248 * 249 * @param provider the provider. 250 * 251 * @return the new {@code Mac} object. 252 * 253 * @exception NoSuchAlgorithmException if a MacSpi 254 * implementation for the specified algorithm is not available 255 * from the specified Provider object. 256 * 257 * @exception IllegalArgumentException if the {@code provider} 258 * is null. 259 * 260 * @see java.security.Provider 261 */ 262 public static final Mac getInstance(String algorithm, Provider provider) 263 throws NoSuchAlgorithmException { 264 Instance instance = JceSecurity.getInstance 265 ("Mac", MacSpi.class, algorithm, provider); 266 return new Mac((MacSpi)instance.impl, instance.provider, algorithm); 267 } 268 269 // max number of debug warnings to print from chooseFirstProvider() 270 private static int warnCount = 10; 271 272 /** 273 * Choose the Spi from the first provider available. Used if 274 * delayed provider selection is not possible because init() 275 * is not the first method called. 276 */ 277 void chooseFirstProvider() { 278 if ((spi != null) || (serviceIterator == null)) { 279 return; 280 } 281 synchronized (lock) { 282 if (spi != null) { 283 return; 284 } 285 if (debug != null) { 286 int w = --warnCount; 287 if (w >= 0) { 288 debug.println("Mac.init() not first method " 289 + "called, disabling delayed provider selection"); 290 if (w == 0) { 291 debug.println("Further warnings of this type will " 292 + "be suppressed"); 293 } 294 new Exception("Call trace").printStackTrace(); 295 } 296 } 297 Exception lastException = null; 298 while ((firstService != null) || serviceIterator.hasNext()) { 299 Service s; 300 if (firstService != null) { 301 s = firstService; 302 firstService = null; 303 } else { 304 s = serviceIterator.next(); 305 } 306 if (JceSecurity.canUseProvider(s.getProvider()) == false) { 307 continue; 308 } 309 try { 310 Object obj = s.newInstance(null); 311 if (obj instanceof MacSpi == false) { 312 continue; 313 } 314 spi = (MacSpi)obj; 315 provider = s.getProvider(); 316 // not needed any more 317 firstService = null; 318 serviceIterator = null; 319 return; 320 } catch (NoSuchAlgorithmException e) { 321 lastException = e; 322 } 323 } 324 ProviderException e = new ProviderException 325 ("Could not construct MacSpi instance"); 326 if (lastException != null) { 327 e.initCause(lastException); 328 } 329 throw e; 330 } 331 } 332 333 private void chooseProvider(Key key, AlgorithmParameterSpec params) 334 throws InvalidKeyException, InvalidAlgorithmParameterException { 335 synchronized (lock) { 336 if (spi != null) { 337 spi.engineInit(key, params); 338 return; 339 } 340 Exception lastException = null; 341 while ((firstService != null) || serviceIterator.hasNext()) { 342 Service s; 343 if (firstService != null) { 344 s = firstService; 345 firstService = null; 346 } else { 347 s = serviceIterator.next(); 348 } 349 // if provider says it does not support this key, ignore it 350 if (s.supportsParameter(key) == false) { 351 continue; 352 } 353 if (JceSecurity.canUseProvider(s.getProvider()) == false) { 354 continue; 355 } 356 try { 357 MacSpi spi = (MacSpi)s.newInstance(null); 358 spi.engineInit(key, params); 359 provider = s.getProvider(); 360 this.spi = spi; 361 firstService = null; 362 serviceIterator = null; 363 return; 364 } catch (Exception e) { 365 // NoSuchAlgorithmException from newInstance() 366 // InvalidKeyException from init() 367 // RuntimeException (ProviderException) from init() 368 if (lastException == null) { 369 lastException = e; 370 } 371 } 372 } 373 // no working provider found, fail 374 if (lastException instanceof InvalidKeyException) { 375 throw (InvalidKeyException)lastException; 376 } 377 if (lastException instanceof InvalidAlgorithmParameterException) { 378 throw (InvalidAlgorithmParameterException)lastException; 379 } 380 if (lastException instanceof RuntimeException) { 381 throw (RuntimeException)lastException; 382 } 383 String kName = (key != null) ? key.getClass().getName() : "(null)"; 384 throw new InvalidKeyException 385 ("No installed provider supports this key: " 386 + kName, lastException); 387 } 388 } 389 390 /** 391 * Returns the provider of this {@code Mac} object. 392 * 393 * @return the provider of this {@code Mac} object. 394 */ 395 public final Provider getProvider() { 396 chooseFirstProvider(); 397 return this.provider; 398 } 399 400 /** 401 * Returns the length of the MAC in bytes. 402 * 403 * @return the MAC length in bytes. 404 */ 405 public final int getMacLength() { 406 chooseFirstProvider(); 407 return spi.engineGetMacLength(); 408 } 409 410 /** 411 * Initializes this {@code Mac} object with the given key. 412 * 413 * @param key the key. 414 * 415 * @exception InvalidKeyException if the given key is inappropriate for 416 * initializing this MAC. 417 */ 418 public final void init(Key key) throws InvalidKeyException { 419 try { 420 if (spi != null) { 421 spi.engineInit(key, null); 422 } else { 423 chooseProvider(key, null); 424 } 425 } catch (InvalidAlgorithmParameterException e) { 426 throw new InvalidKeyException("init() failed", e); 427 } 428 initialized = true; 429 430 if (!skipDebug && pdebug != null) { 431 pdebug.println("Mac." + algorithm + " algorithm from: " + 432 this.provider.getName()); 433 } 434 } 435 436 /** 437 * Initializes this {@code Mac} object with the given key and 438 * algorithm parameters. 439 * 440 * @param key the key. 441 * @param params the algorithm parameters. 442 * 443 * @exception InvalidKeyException if the given key is inappropriate for 444 * initializing this MAC. 445 * @exception InvalidAlgorithmParameterException if the given algorithm 446 * parameters are inappropriate for this MAC. 447 */ 448 public final void init(Key key, AlgorithmParameterSpec params) 449 throws InvalidKeyException, InvalidAlgorithmParameterException { 450 if (spi != null) { 451 spi.engineInit(key, params); 452 } else { 453 chooseProvider(key, params); 454 } 455 initialized = true; 456 457 if (!skipDebug && pdebug != null) { 458 pdebug.println("Mac." + algorithm + " algorithm from: " + 459 this.provider.getName()); 460 } 461 } 462 463 /** 464 * Processes the given byte. 465 * 466 * @param input the input byte to be processed. 467 * 468 * @exception IllegalStateException if this {@code Mac} has not been 469 * initialized. 470 */ 471 public final void update(byte input) throws IllegalStateException { 472 chooseFirstProvider(); 473 if (initialized == false) { 474 throw new IllegalStateException("MAC not initialized"); 475 } 476 spi.engineUpdate(input); 477 } 478 479 /** 480 * Processes the given array of bytes. 481 * 482 * @param input the array of bytes to be processed. 483 * 484 * @exception IllegalStateException if this {@code Mac} has not been 485 * initialized. 486 */ 487 public final void update(byte[] input) throws IllegalStateException { 488 chooseFirstProvider(); 489 if (initialized == false) { 490 throw new IllegalStateException("MAC not initialized"); 491 } 492 if (input != null) { 493 spi.engineUpdate(input, 0, input.length); 494 } 495 } 496 497 /** 498 * Processes the first {@code len} bytes in {@code input}, 499 * starting at {@code offset} inclusive. 500 * 501 * @param input the input buffer. 502 * @param offset the offset in {@code input} where the input starts. 503 * @param len the number of bytes to process. 504 * 505 * @exception IllegalStateException if this {@code Mac} has not been 506 * initialized. 507 */ 508 public final void update(byte[] input, int offset, int len) 509 throws IllegalStateException { 510 chooseFirstProvider(); 511 if (initialized == false) { 512 throw new IllegalStateException("MAC not initialized"); 513 } 514 515 if (input != null) { 516 if ((offset < 0) || (len > (input.length - offset)) || (len < 0)) 517 throw new IllegalArgumentException("Bad arguments"); 518 spi.engineUpdate(input, offset, len); 519 } 520 } 521 522 /** 523 * Processes {@code input.remaining()} bytes in the ByteBuffer 524 * {@code input}, starting at {@code input.position()}. 525 * Upon return, the buffer's position will be equal to its limit; 526 * its limit will not have changed. 527 * 528 * @param input the ByteBuffer 529 * 530 * @exception IllegalStateException if this {@code Mac} has not been 531 * initialized. 532 * @since 1.5 533 */ 534 public final void update(ByteBuffer input) { 535 chooseFirstProvider(); 536 if (initialized == false) { 537 throw new IllegalStateException("MAC not initialized"); 538 } 539 if (input == null) { 540 throw new IllegalArgumentException("Buffer must not be null"); 541 } 542 spi.engineUpdate(input); 543 } 544 545 /** 546 * Finishes the MAC operation. 547 * 548 * <p>A call to this method resets this {@code Mac} object to the 549 * state it was in when previously initialized via a call to 550 * {@code init(Key)} or 551 * {@code init(Key, AlgorithmParameterSpec)}. 552 * That is, the object is reset and available to generate another MAC from 553 * the same key, if desired, via new calls to {@code update} and 554 * {@code doFinal}. 555 * (In order to reuse this {@code Mac} object with a different key, 556 * it must be reinitialized via a call to {@code init(Key)} or 557 * {@code init(Key, AlgorithmParameterSpec)}. 558 * 559 * @return the MAC result. 560 * 561 * @exception IllegalStateException if this {@code Mac} has not been 562 * initialized. 563 */ 564 public final byte[] doFinal() throws IllegalStateException { 565 chooseFirstProvider(); 566 if (initialized == false) { 567 throw new IllegalStateException("MAC not initialized"); 568 } 569 byte[] mac = spi.engineDoFinal(); 570 spi.engineReset(); 571 return mac; 572 } 573 574 /** 575 * Finishes the MAC operation. 576 * 577 * <p>A call to this method resets this {@code Mac} object to the 578 * state it was in when previously initialized via a call to 579 * {@code init(Key)} or 580 * {@code init(Key, AlgorithmParameterSpec)}. 581 * That is, the object is reset and available to generate another MAC from 582 * the same key, if desired, via new calls to {@code update} and 583 * {@code doFinal}. 584 * (In order to reuse this {@code Mac} object with a different key, 585 * it must be reinitialized via a call to {@code init(Key)} or 586 * {@code init(Key, AlgorithmParameterSpec)}. 587 * 588 * <p>The MAC result is stored in {@code output}, starting at 589 * {@code outOffset} inclusive. 590 * 591 * @param output the buffer where the MAC result is stored 592 * @param outOffset the offset in {@code output} where the MAC is 593 * stored 594 * 595 * @exception ShortBufferException if the given output buffer is too small 596 * to hold the result 597 * @exception IllegalStateException if this {@code Mac} has not been 598 * initialized. 599 */ 600 public final void doFinal(byte[] output, int outOffset) 601 throws ShortBufferException, IllegalStateException 602 { 603 chooseFirstProvider(); 604 if (initialized == false) { 605 throw new IllegalStateException("MAC not initialized"); 606 } 607 int macLen = getMacLength(); 608 if (output == null || output.length-outOffset < macLen) { 609 throw new ShortBufferException 610 ("Cannot store MAC in output buffer"); 611 } 612 byte[] mac = doFinal(); 613 System.arraycopy(mac, 0, output, outOffset, macLen); 614 return; 615 } 616 617 /** 618 * Processes the given array of bytes and finishes the MAC operation. 619 * 620 * <p>A call to this method resets this {@code Mac} object to the 621 * state it was in when previously initialized via a call to 622 * {@code init(Key)} or 623 * {@code init(Key, AlgorithmParameterSpec)}. 624 * That is, the object is reset and available to generate another MAC from 625 * the same key, if desired, via new calls to {@code update} and 626 * {@code doFinal}. 627 * (In order to reuse this {@code Mac} object with a different key, 628 * it must be reinitialized via a call to {@code init(Key)} or 629 * {@code init(Key, AlgorithmParameterSpec)}. 630 * 631 * @param input data in bytes 632 * @return the MAC result. 633 * 634 * @exception IllegalStateException if this {@code Mac} has not been 635 * initialized. 636 */ 637 public final byte[] doFinal(byte[] input) throws IllegalStateException 638 { 639 chooseFirstProvider(); 640 if (initialized == false) { 641 throw new IllegalStateException("MAC not initialized"); 642 } 643 update(input); 644 return doFinal(); 645 } 646 647 /** 648 * Resets this {@code Mac} object. 649 * 650 * <p>A call to this method resets this {@code Mac} object to the 651 * state it was in when previously initialized via a call to 652 * {@code init(Key)} or 653 * {@code init(Key, AlgorithmParameterSpec)}. 654 * That is, the object is reset and available to generate another MAC from 655 * the same key, if desired, via new calls to {@code update} and 656 * {@code doFinal}. 657 * (In order to reuse this {@code Mac} object with a different key, 658 * it must be reinitialized via a call to {@code init(Key)} or 659 * {@code init(Key, AlgorithmParameterSpec)}. 660 */ 661 public final void reset() { 662 chooseFirstProvider(); 663 spi.engineReset(); 664 } 665 666 /** 667 * Returns a clone if the provider implementation is cloneable. 668 * 669 * @return a clone if the provider implementation is cloneable. 670 * 671 * @exception CloneNotSupportedException if this is called on a 672 * delegate that does not support {@code Cloneable}. 673 */ 674 public final Object clone() throws CloneNotSupportedException { 675 chooseFirstProvider(); 676 Mac that = (Mac)super.clone(); 677 that.spi = (MacSpi)this.spi.clone(); 678 return that; 679 } 680 }