Save This Page
Home » openjdk-7 » javax » crypto » [javadoc | source]
    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   }

Save This Page
Home » openjdk-7 » javax » crypto » [javadoc | source]