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