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 * @throws NoSuchAlgorithmException if no {@code Provider} supports a 172 * {@code MacSpi} implementation for the specified algorithm 173 * 174 * @throws NullPointerException if {@code algorithm} is {@code null} 175 * 176 * @see java.security.Provider 177 */ 178 public static final Mac getInstance(String algorithm) 179 throws NoSuchAlgorithmException { 180 List<Service> services = GetInstance.getServices("Mac", algorithm); 181 // make sure there is at least one service from a signed provider 182 Iterator<Service> t = services.iterator(); 183 while (t.hasNext()) { 184 Service s = t.next(); 185 if (JceSecurity.canUseProvider(s.getProvider()) == false) { 186 continue; 187 } 188 return new Mac(s, t, algorithm); 189 } 190 throw new NoSuchAlgorithmException 191 ("Algorithm " + algorithm + " not available"); 192 } 193 194 /** 195 * Returns a {@code Mac} object that implements the 196 * specified MAC algorithm. 197 * 198 * <p> A new Mac object encapsulating the 199 * MacSpi implementation from the specified provider 200 * is returned. The specified provider must be registered 201 * in the security provider list. 202 * 203 * <p> Note that the list of registered providers may be retrieved via 204 * the {@link Security#getProviders() Security.getProviders()} method. 205 * 206 * @param algorithm the standard name of the requested MAC algorithm. 207 * See the Mac section in the <a href= 208 * "{@docRoot}/../technotes/guides/security/StandardNames.html#Mac"> 209 * Java Cryptography Architecture Standard Algorithm Name Documentation</a> 210 * for information about standard algorithm names. 211 * 212 * @param provider the name of the provider. 213 * 214 * @return the new {@code Mac} object 215 * 216 * @throws IllegalArgumentException if the {@code provider} 217 * is {@code null} or empty 218 * 219 * @throws NoSuchAlgorithmException if a {@code MacSpi} 220 * implementation for the specified algorithm is not 221 * available from the specified provider 222 * 223 * @throws NoSuchProviderException if the specified provider is not 224 * registered in the security provider list 225 * 226 * @throws NullPointerException if {@code algorithm} is {@code null} 227 * 228 * @see java.security.Provider 229 */ 230 public static final Mac getInstance(String algorithm, String provider) 231 throws NoSuchAlgorithmException, NoSuchProviderException { 232 Instance instance = JceSecurity.getInstance 233 ("Mac", MacSpi.class, algorithm, provider); 234 return new Mac((MacSpi)instance.impl, instance.provider, algorithm); 235 } 236 237 /** 238 * Returns a {@code Mac} object that implements the 239 * specified MAC algorithm. 240 * 241 * <p> A new Mac object encapsulating the 242 * MacSpi implementation from the specified Provider 243 * object is returned. Note that the specified Provider object 244 * does not have to be registered in the provider list. 245 * 246 * @param algorithm the standard name of the requested MAC algorithm. 247 * See the Mac section in the <a href= 248 * "{@docRoot}/../technotes/guides/security/StandardNames.html#Mac"> 249 * Java Cryptography Architecture Standard Algorithm Name Documentation</a> 250 * for information about standard algorithm names. 251 * 252 * @param provider the provider. 253 * 254 * @return the new {@code Mac} object 255 * 256 * @throws IllegalArgumentException if the {@code provider} is 257 * {@code null} 258 * 259 * @throws NoSuchAlgorithmException if a {@code MacSpi} 260 * implementation for the specified algorithm is not available 261 * from the specified {@code Provider} object 262 * 263 * @throws NullPointerException if {@code algorithm} is {@code null} 264 * 265 * @see java.security.Provider 266 */ 267 public static final Mac getInstance(String algorithm, Provider provider) 268 throws NoSuchAlgorithmException { 269 Instance instance = JceSecurity.getInstance 270 ("Mac", MacSpi.class, algorithm, provider); 271 return new Mac((MacSpi)instance.impl, instance.provider, algorithm); 272 } 273 274 // max number of debug warnings to print from chooseFirstProvider() 275 private static int warnCount = 10; 276 277 /** 278 * Choose the Spi from the first provider available. Used if 279 * delayed provider selection is not possible because init() 280 * is not the first method called. 281 */ 282 void chooseFirstProvider() { 283 if ((spi != null) || (serviceIterator == null)) { 284 return; 285 } 286 synchronized (lock) { 287 if (spi != null) { 288 return; 289 } 290 if (debug != null) { 291 int w = --warnCount; 292 if (w >= 0) { 293 debug.println("Mac.init() not first method " 294 + "called, disabling delayed provider selection"); 295 if (w == 0) { 296 debug.println("Further warnings of this type will " 297 + "be suppressed"); 298 } 299 new Exception("Call trace").printStackTrace(); 300 } 301 } 302 Exception lastException = null; 303 while ((firstService != null) || serviceIterator.hasNext()) { 304 Service s; 305 if (firstService != null) { 306 s = firstService; 307 firstService = null; 308 } else { 309 s = serviceIterator.next(); 310 } 311 if (JceSecurity.canUseProvider(s.getProvider()) == false) { 312 continue; 313 } 314 try { 315 Object obj = s.newInstance(null); 316 if (obj instanceof MacSpi == false) { 317 continue; 318 } 319 spi = (MacSpi)obj; 320 provider = s.getProvider(); 321 // not needed any more 322 firstService = null; 323 serviceIterator = null; 324 return; 325 } catch (NoSuchAlgorithmException e) { 326 lastException = e; 327 } 328 } 329 ProviderException e = new ProviderException 330 ("Could not construct MacSpi instance"); 331 if (lastException != null) { 332 e.initCause(lastException); 333 } 334 throw e; 335 } 336 } 337 338 private void chooseProvider(Key key, AlgorithmParameterSpec params) 339 throws InvalidKeyException, InvalidAlgorithmParameterException { 340 synchronized (lock) { 341 if (spi != null) { 342 spi.engineInit(key, params); 343 return; 344 } 345 Exception lastException = null; 346 while ((firstService != null) || serviceIterator.hasNext()) { 347 Service s; 348 if (firstService != null) { 349 s = firstService; 350 firstService = null; 351 } else { 352 s = serviceIterator.next(); 353 } 354 // if provider says it does not support this key, ignore it 355 if (s.supportsParameter(key) == false) { 356 continue; 357 } 358 if (JceSecurity.canUseProvider(s.getProvider()) == false) { 359 continue; 360 } 361 try { 362 MacSpi spi = (MacSpi)s.newInstance(null); 363 spi.engineInit(key, params); 364 provider = s.getProvider(); 365 this.spi = spi; 366 firstService = null; 367 serviceIterator = null; 368 return; 369 } catch (Exception e) { 370 // NoSuchAlgorithmException from newInstance() 371 // InvalidKeyException from init() 372 // RuntimeException (ProviderException) from init() 373 if (lastException == null) { 374 lastException = e; 375 } 376 } 377 } 378 // no working provider found, fail 379 if (lastException instanceof InvalidKeyException) { 380 throw (InvalidKeyException)lastException; 381 } 382 if (lastException instanceof InvalidAlgorithmParameterException) { 383 throw (InvalidAlgorithmParameterException)lastException; 384 } 385 if (lastException instanceof RuntimeException) { 386 throw (RuntimeException)lastException; 387 } 388 String kName = (key != null) ? key.getClass().getName() : "(null)"; 389 throw new InvalidKeyException 390 ("No installed provider supports this key: " 391 + kName, lastException); 392 } 393 } 394 395 /** 396 * Returns the provider of this {@code Mac} object. 397 * 398 * @return the provider of this {@code Mac} object. 399 */ 400 public final Provider getProvider() { 401 chooseFirstProvider(); 402 return this.provider; 403 } 404 405 /** 406 * Returns the length of the MAC in bytes. 407 * 408 * @return the MAC length in bytes. 409 */ 410 public final int getMacLength() { 411 chooseFirstProvider(); 412 return spi.engineGetMacLength(); 413 } 414 415 /** 416 * Initializes this {@code Mac} object with the given key. 417 * 418 * @param key the key. 419 * 420 * @exception InvalidKeyException if the given key is inappropriate for 421 * initializing this MAC. 422 */ 423 public final void init(Key key) throws InvalidKeyException { 424 try { 425 if (spi != null) { 426 spi.engineInit(key, null); 427 } else { 428 chooseProvider(key, null); 429 } 430 } catch (InvalidAlgorithmParameterException e) { 431 throw new InvalidKeyException("init() failed", e); 432 } 433 initialized = true; 434 435 if (!skipDebug && pdebug != null) { 436 pdebug.println("Mac." + algorithm + " algorithm from: " + 437 this.provider.getName()); 438 } 439 } 440 441 /** 442 * Initializes this {@code Mac} object with the given key and 443 * algorithm parameters. 444 * 445 * @param key the key. 446 * @param params the algorithm parameters. 447 * 448 * @exception InvalidKeyException if the given key is inappropriate for 449 * initializing this MAC. 450 * @exception InvalidAlgorithmParameterException if the given algorithm 451 * parameters are inappropriate for this MAC. 452 */ 453 public final void init(Key key, AlgorithmParameterSpec params) 454 throws InvalidKeyException, InvalidAlgorithmParameterException { 455 if (spi != null) { 456 spi.engineInit(key, params); 457 } else { 458 chooseProvider(key, params); 459 } 460 initialized = true; 461 462 if (!skipDebug && pdebug != null) { 463 pdebug.println("Mac." + algorithm + " algorithm from: " + 464 this.provider.getName()); 465 } 466 } 467 468 /** 469 * Processes the given byte. 470 * 471 * @param input the input byte to be processed. 472 * 473 * @exception IllegalStateException if this {@code Mac} has not been 474 * initialized. 475 */ 476 public final void update(byte input) throws IllegalStateException { 477 chooseFirstProvider(); 478 if (initialized == false) { 479 throw new IllegalStateException("MAC not initialized"); 480 } 481 spi.engineUpdate(input); 482 } 483 484 /** 485 * Processes the given array of bytes. 486 * 487 * @param input the array of bytes to be processed. 488 * 489 * @exception IllegalStateException if this {@code Mac} has not been 490 * initialized. 491 */ 492 public final void update(byte[] input) throws IllegalStateException { 493 chooseFirstProvider(); 494 if (initialized == false) { 495 throw new IllegalStateException("MAC not initialized"); 496 } 497 if (input != null) { 498 spi.engineUpdate(input, 0, input.length); 499 } 500 } 501 502 /** 503 * Processes the first {@code len} bytes in {@code input}, 504 * starting at {@code offset} inclusive. 505 * 506 * @param input the input buffer. 507 * @param offset the offset in {@code input} where the input starts. 508 * @param len the number of bytes to process. 509 * 510 * @exception IllegalStateException if this {@code Mac} has not been 511 * initialized. 512 */ 513 public final void update(byte[] input, int offset, int len) 514 throws IllegalStateException { 515 chooseFirstProvider(); 516 if (initialized == false) { 517 throw new IllegalStateException("MAC not initialized"); 518 } 519 520 if (input != null) { 521 if ((offset < 0) || (len > (input.length - offset)) || (len < 0)) 522 throw new IllegalArgumentException("Bad arguments"); 523 spi.engineUpdate(input, offset, len); 524 } 525 } 526 527 /** 528 * Processes {@code input.remaining()} bytes in the ByteBuffer 529 * {@code input}, starting at {@code input.position()}. 530 * Upon return, the buffer's position will be equal to its limit; 531 * its limit will not have changed. 532 * 533 * @param input the ByteBuffer 534 * 535 * @exception IllegalStateException if this {@code Mac} has not been 536 * initialized. 537 * @since 1.5 538 */ 539 public final void update(ByteBuffer input) { 540 chooseFirstProvider(); 541 if (initialized == false) { 542 throw new IllegalStateException("MAC not initialized"); 543 } 544 if (input == null) { 545 throw new IllegalArgumentException("Buffer must not be null"); 546 } 547 spi.engineUpdate(input); 548 } 549 550 /** 551 * Finishes the MAC operation. 552 * 553 * <p>A call to this method resets this {@code Mac} object to the 554 * state it was in when previously initialized via a call to 555 * {@code init(Key)} or 556 * {@code init(Key, AlgorithmParameterSpec)}. 557 * That is, the object is reset and available to generate another MAC from 558 * the same key, if desired, via new calls to {@code update} and 559 * {@code doFinal}. 560 * (In order to reuse this {@code Mac} object with a different key, 561 * it must be reinitialized via a call to {@code init(Key)} or 562 * {@code init(Key, AlgorithmParameterSpec)}. 563 * 564 * @return the MAC result. 565 * 566 * @exception IllegalStateException if this {@code Mac} has not been 567 * initialized. 568 */ 569 public final byte[] doFinal() throws IllegalStateException { 570 chooseFirstProvider(); 571 if (initialized == false) { 572 throw new IllegalStateException("MAC not initialized"); 573 } 574 byte[] mac = spi.engineDoFinal(); 575 spi.engineReset(); 576 return mac; 577 } 578 579 /** 580 * Finishes the MAC operation. 581 * 582 * <p>A call to this method resets this {@code Mac} object to the 583 * state it was in when previously initialized via a call to 584 * {@code init(Key)} or 585 * {@code init(Key, AlgorithmParameterSpec)}. 586 * That is, the object is reset and available to generate another MAC from 587 * the same key, if desired, via new calls to {@code update} and 588 * {@code doFinal}. 589 * (In order to reuse this {@code Mac} object with a different key, 590 * it must be reinitialized via a call to {@code init(Key)} or 591 * {@code init(Key, AlgorithmParameterSpec)}. 592 * 593 * <p>The MAC result is stored in {@code output}, starting at 594 * {@code outOffset} inclusive. 595 * 596 * @param output the buffer where the MAC result is stored 597 * @param outOffset the offset in {@code output} where the MAC is 598 * stored 599 * 600 * @exception ShortBufferException if the given output buffer is too small 601 * to hold the result 602 * @exception IllegalStateException if this {@code Mac} has not been 603 * initialized. 604 */ 605 public final void doFinal(byte[] output, int outOffset) 606 throws ShortBufferException, IllegalStateException 607 { 608 chooseFirstProvider(); 609 if (initialized == false) { 610 throw new IllegalStateException("MAC not initialized"); 611 } 612 int macLen = getMacLength(); 613 if (output == null || output.length-outOffset < macLen) { 614 throw new ShortBufferException 615 ("Cannot store MAC in output buffer"); 616 } 617 byte[] mac = doFinal(); 618 System.arraycopy(mac, 0, output, outOffset, macLen); 619 return; 620 } 621 622 /** 623 * Processes the given array of bytes and finishes the MAC operation. 624 * 625 * <p>A call to this method resets this {@code Mac} object to the 626 * state it was in when previously initialized via a call to 627 * {@code init(Key)} or 628 * {@code init(Key, AlgorithmParameterSpec)}. 629 * That is, the object is reset and available to generate another MAC from 630 * the same key, if desired, via new calls to {@code update} and 631 * {@code doFinal}. 632 * (In order to reuse this {@code Mac} object with a different key, 633 * it must be reinitialized via a call to {@code init(Key)} or 634 * {@code init(Key, AlgorithmParameterSpec)}. 635 * 636 * @param input data in bytes 637 * @return the MAC result. 638 * 639 * @exception IllegalStateException if this {@code Mac} has not been 640 * initialized. 641 */ 642 public final byte[] doFinal(byte[] input) throws IllegalStateException 643 { 644 chooseFirstProvider(); 645 if (initialized == false) { 646 throw new IllegalStateException("MAC not initialized"); 647 } 648 update(input); 649 return doFinal(); 650 } 651 652 /** 653 * Resets this {@code Mac} object. 654 * 655 * <p>A call to this method resets this {@code Mac} object to the 656 * state it was in when previously initialized via a call to 657 * {@code init(Key)} or 658 * {@code init(Key, AlgorithmParameterSpec)}. 659 * That is, the object is reset and available to generate another MAC from 660 * the same key, if desired, via new calls to {@code update} and 661 * {@code doFinal}. 662 * (In order to reuse this {@code Mac} object with a different key, 663 * it must be reinitialized via a call to {@code init(Key)} or 664 * {@code init(Key, AlgorithmParameterSpec)}. 665 */ 666 public final void reset() { 667 chooseFirstProvider(); 668 spi.engineReset(); 669 } 670 671 /** 672 * Returns a clone if the provider implementation is cloneable. 673 * 674 * @return a clone if the provider implementation is cloneable. 675 * 676 * @exception CloneNotSupportedException if this is called on a 677 * delegate that does not support {@code Cloneable}. 678 */ 679 public final Object clone() throws CloneNotSupportedException { 680 chooseFirstProvider(); 681 Mac that = (Mac)super.clone(); 682 that.spi = (MacSpi)this.spi.clone(); 683 return that; 684 } 685 }