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