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

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