Path: blob/master/src/java.base/share/classes/javax/crypto/Mac.java
41152 views
/*1* Copyright (c) 1998, 2019, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package javax.crypto;2627import java.util.*;2829import java.security.*;30import java.security.Provider.Service;31import java.security.spec.AlgorithmParameterSpec;3233import java.nio.ByteBuffer;3435import sun.security.util.Debug;36import sun.security.jca.*;37import sun.security.jca.GetInstance.Instance;3839/**40* This class provides the functionality of a "Message Authentication Code"41* (MAC) algorithm.42*43* <p> A MAC provides a way to check44* the integrity of information transmitted over or stored in an unreliable45* medium, based on a secret key. Typically, message46* authentication codes are used between two parties that share a secret47* key in order to validate information transmitted between these48* parties.49*50* <p> A MAC mechanism that is based on cryptographic hash functions is51* referred to as HMAC. HMAC can be used with any cryptographic hash function,52* e.g., SHA256 or SHA384, in combination with a secret shared key. HMAC is53* specified in RFC 2104.54*55* <p> Every implementation of the Java platform is required to support56* the following standard {@code Mac} algorithms:57* <ul>58* <li>{@code HmacSHA1}</li>59* <li>{@code HmacSHA256}</li>60* </ul>61* These algorithms are described in the62* <a href="{@docRoot}/../specs/security/standard-names.html#mac-algorithms">63* Mac section</a> of the64* Java Security Standard Algorithm Names Specification.65* Consult the release documentation for your implementation to see if any66* other algorithms are supported.67*68* @author Jan Luehe69*70* @since 1.471*/7273public class Mac implements Cloneable {7475private static final Debug debug =76Debug.getInstance("jca", "Mac");7778private static final Debug pdebug =79Debug.getInstance("provider", "Provider");80private static final boolean skipDebug =81Debug.isOn("engine=") && !Debug.isOn("mac");8283// The provider84private Provider provider;8586// The provider implementation (delegate)87private MacSpi spi;8889// The name of the MAC algorithm.90private final String algorithm;9192// Has this object been initialized?93private boolean initialized = false;9495// next service to try in provider selection96// null once provider is selected97private Service firstService;9899// remaining services to try in provider selection100// null once provider is selected101private Iterator<Service> serviceIterator;102103private final Object lock;104105/**106* Creates a MAC object.107*108* @param macSpi the delegate109* @param provider the provider110* @param algorithm the algorithm111*/112protected Mac(MacSpi macSpi, Provider provider, String algorithm) {113this.spi = macSpi;114this.provider = provider;115this.algorithm = algorithm;116serviceIterator = null;117lock = null;118}119120private Mac(Service s, Iterator<Service> t, String algorithm) {121firstService = s;122serviceIterator = t;123this.algorithm = algorithm;124lock = new Object();125}126127/**128* Returns the algorithm name of this {@code Mac} object.129*130* <p>This is the same name that was specified in one of the131* {@code getInstance} calls that created this132* {@code Mac} object.133*134* @return the algorithm name of this {@code Mac} object.135*/136public final String getAlgorithm() {137return this.algorithm;138}139140/**141* Returns a {@code Mac} object that implements the142* specified MAC algorithm.143*144* <p> This method traverses the list of registered security Providers,145* starting with the most preferred Provider.146* A new Mac object encapsulating the147* MacSpi implementation from the first148* Provider that supports the specified algorithm is returned.149*150* <p> Note that the list of registered providers may be retrieved via151* the {@link Security#getProviders() Security.getProviders()} method.152*153* @implNote154* The JDK Reference Implementation additionally uses the155* {@code jdk.security.provider.preferred}156* {@link Security#getProperty(String) Security} property to determine157* the preferred provider order for the specified algorithm. This158* may be different than the order of providers returned by159* {@link Security#getProviders() Security.getProviders()}.160*161* @param algorithm the standard name of the requested MAC algorithm.162* See the Mac section in the <a href=163* "{@docRoot}/../specs/security/standard-names.html#mac-algorithms">164* Java Security Standard Algorithm Names Specification</a>165* for information about standard algorithm names.166*167* @return the new {@code Mac} object168*169* @throws NoSuchAlgorithmException if no {@code Provider} supports a170* {@code MacSpi} implementation for the specified algorithm171*172* @throws NullPointerException if {@code algorithm} is {@code null}173*174* @see java.security.Provider175*/176public static final Mac getInstance(String algorithm)177throws NoSuchAlgorithmException {178Objects.requireNonNull(algorithm, "null algorithm name");179List<Service> services = GetInstance.getServices("Mac", algorithm);180// make sure there is at least one service from a signed provider181Iterator<Service> t = services.iterator();182while (t.hasNext()) {183Service s = t.next();184if (JceSecurity.canUseProvider(s.getProvider()) == false) {185continue;186}187return new Mac(s, t, algorithm);188}189throw new NoSuchAlgorithmException190("Algorithm " + algorithm + " not available");191}192193/**194* Returns a {@code Mac} object that implements the195* specified MAC algorithm.196*197* <p> A new Mac object encapsulating the198* MacSpi implementation from the specified provider199* is returned. The specified provider must be registered200* in the security provider list.201*202* <p> Note that the list of registered providers may be retrieved via203* the {@link Security#getProviders() Security.getProviders()} method.204*205* @param algorithm the standard name of the requested MAC algorithm.206* See the Mac section in the <a href=207* "{@docRoot}/../specs/security/standard-names.html#mac-algorithms">208* Java Security Standard Algorithm Names Specification</a>209* for information about standard algorithm names.210*211* @param provider the name of the provider.212*213* @return the new {@code Mac} object214*215* @throws IllegalArgumentException if the {@code provider}216* is {@code null} or empty217*218* @throws NoSuchAlgorithmException if a {@code MacSpi}219* implementation for the specified algorithm is not220* available from the specified provider221*222* @throws NoSuchProviderException if the specified provider is not223* registered in the security provider list224*225* @throws NullPointerException if {@code algorithm} is {@code null}226*227* @see java.security.Provider228*/229public static final Mac getInstance(String algorithm, String provider)230throws NoSuchAlgorithmException, NoSuchProviderException {231Objects.requireNonNull(algorithm, "null algorithm name");232Instance instance = JceSecurity.getInstance233("Mac", MacSpi.class, algorithm, provider);234return new Mac((MacSpi)instance.impl, instance.provider, algorithm);235}236237/**238* Returns a {@code Mac} object that implements the239* specified MAC algorithm.240*241* <p> A new Mac object encapsulating the242* MacSpi implementation from the specified Provider243* object is returned. Note that the specified Provider object244* does not have to be registered in the provider list.245*246* @param algorithm the standard name of the requested MAC algorithm.247* See the Mac section in the <a href=248* "{@docRoot}/../specs/security/standard-names.html#mac-algorithms">249* Java Security Standard Algorithm Names Specification</a>250* for information about standard algorithm names.251*252* @param provider the provider.253*254* @return the new {@code Mac} object255*256* @throws IllegalArgumentException if the {@code provider} is257* {@code null}258*259* @throws NoSuchAlgorithmException if a {@code MacSpi}260* implementation for the specified algorithm is not available261* from the specified {@code Provider} object262*263* @throws NullPointerException if {@code algorithm} is {@code null}264*265* @see java.security.Provider266*/267public static final Mac getInstance(String algorithm, Provider provider)268throws NoSuchAlgorithmException {269Objects.requireNonNull(algorithm, "null algorithm name");270Instance instance = JceSecurity.getInstance271("Mac", MacSpi.class, algorithm, provider);272return new Mac((MacSpi)instance.impl, instance.provider, algorithm);273}274275// max number of debug warnings to print from chooseFirstProvider()276private static int warnCount = 10;277278/**279* Choose the Spi from the first provider available. Used if280* delayed provider selection is not possible because init()281* is not the first method called.282*/283void chooseFirstProvider() {284if ((spi != null) || (serviceIterator == null)) {285return;286}287synchronized (lock) {288if (spi != null) {289return;290}291if (debug != null) {292int w = --warnCount;293if (w >= 0) {294debug.println("Mac.init() not first method "295+ "called, disabling delayed provider selection");296if (w == 0) {297debug.println("Further warnings of this type will "298+ "be suppressed");299}300new Exception("Call trace").printStackTrace();301}302}303Exception lastException = null;304while ((firstService != null) || serviceIterator.hasNext()) {305Service s;306if (firstService != null) {307s = firstService;308firstService = null;309} else {310s = serviceIterator.next();311}312if (JceSecurity.canUseProvider(s.getProvider()) == false) {313continue;314}315try {316Object obj = s.newInstance(null);317if (obj instanceof MacSpi == false) {318continue;319}320spi = (MacSpi)obj;321provider = s.getProvider();322// not needed any more323firstService = null;324serviceIterator = null;325return;326} catch (NoSuchAlgorithmException e) {327lastException = e;328}329}330ProviderException e = new ProviderException331("Could not construct MacSpi instance");332if (lastException != null) {333e.initCause(lastException);334}335throw e;336}337}338339private void chooseProvider(Key key, AlgorithmParameterSpec params)340throws InvalidKeyException, InvalidAlgorithmParameterException {341synchronized (lock) {342if (spi != null) {343spi.engineInit(key, params);344return;345}346Exception lastException = null;347while ((firstService != null) || serviceIterator.hasNext()) {348Service s;349if (firstService != null) {350s = firstService;351firstService = null;352} else {353s = serviceIterator.next();354}355// if provider says it does not support this key, ignore it356if (s.supportsParameter(key) == false) {357continue;358}359if (JceSecurity.canUseProvider(s.getProvider()) == false) {360continue;361}362try {363MacSpi spi = (MacSpi)s.newInstance(null);364spi.engineInit(key, params);365provider = s.getProvider();366this.spi = spi;367firstService = null;368serviceIterator = null;369return;370} catch (Exception e) {371// NoSuchAlgorithmException from newInstance()372// InvalidKeyException from init()373// RuntimeException (ProviderException) from init()374if (lastException == null) {375lastException = e;376}377}378}379// no working provider found, fail380if (lastException instanceof InvalidKeyException) {381throw (InvalidKeyException)lastException;382}383if (lastException instanceof InvalidAlgorithmParameterException) {384throw (InvalidAlgorithmParameterException)lastException;385}386if (lastException instanceof RuntimeException) {387throw (RuntimeException)lastException;388}389String kName = (key != null) ? key.getClass().getName() : "(null)";390throw new InvalidKeyException391("No installed provider supports this key: "392+ kName, lastException);393}394}395396/**397* Returns the provider of this {@code Mac} object.398*399* @return the provider of this {@code Mac} object.400*/401public final Provider getProvider() {402chooseFirstProvider();403return this.provider;404}405406/**407* Returns the length of the MAC in bytes.408*409* @return the MAC length in bytes.410*/411public final int getMacLength() {412chooseFirstProvider();413return spi.engineGetMacLength();414}415416private String getProviderName() {417return (provider == null) ? "(no provider)" : provider.getName();418}419420/**421* Initializes this {@code Mac} object with the given key.422*423* @param key the key.424*425* @exception InvalidKeyException if the given key is inappropriate for426* initializing this MAC.427*/428public final void init(Key key) throws InvalidKeyException {429try {430if (spi != null) {431spi.engineInit(key, null);432} else {433chooseProvider(key, null);434}435} catch (InvalidAlgorithmParameterException e) {436throw new InvalidKeyException("init() failed", e);437}438initialized = true;439440if (!skipDebug && pdebug != null) {441pdebug.println("Mac." + algorithm + " algorithm from: " +442getProviderName());443}444}445446/**447* Initializes this {@code Mac} object with the given key and448* algorithm parameters.449*450* @param key the key.451* @param params the algorithm parameters.452*453* @exception InvalidKeyException if the given key is inappropriate for454* initializing this MAC.455* @exception InvalidAlgorithmParameterException if the given algorithm456* parameters are inappropriate for this MAC.457*/458public final void init(Key key, AlgorithmParameterSpec params)459throws InvalidKeyException, InvalidAlgorithmParameterException {460if (spi != null) {461spi.engineInit(key, params);462} else {463chooseProvider(key, params);464}465initialized = true;466467if (!skipDebug && pdebug != null) {468pdebug.println("Mac." + algorithm + " algorithm from: " +469getProviderName());470}471}472473/**474* Processes the given byte.475*476* @param input the input byte to be processed.477*478* @exception IllegalStateException if this {@code Mac} has not been479* initialized.480*/481public final void update(byte input) throws IllegalStateException {482chooseFirstProvider();483if (initialized == false) {484throw new IllegalStateException("MAC not initialized");485}486spi.engineUpdate(input);487}488489/**490* Processes the given array of bytes.491*492* @param input the array of bytes to be processed.493*494* @exception IllegalStateException if this {@code Mac} has not been495* initialized.496*/497public final void update(byte[] input) throws IllegalStateException {498chooseFirstProvider();499if (initialized == false) {500throw new IllegalStateException("MAC not initialized");501}502if (input != null) {503spi.engineUpdate(input, 0, input.length);504}505}506507/**508* Processes the first {@code len} bytes in {@code input},509* starting at {@code offset} inclusive.510*511* @param input the input buffer.512* @param offset the offset in {@code input} where the input starts.513* @param len the number of bytes to process.514*515* @exception IllegalStateException if this {@code Mac} has not been516* initialized.517*/518public final void update(byte[] input, int offset, int len)519throws IllegalStateException {520chooseFirstProvider();521if (initialized == false) {522throw new IllegalStateException("MAC not initialized");523}524525if (input != null) {526if ((offset < 0) || (len > (input.length - offset)) || (len < 0))527throw new IllegalArgumentException("Bad arguments");528spi.engineUpdate(input, offset, len);529}530}531532/**533* Processes {@code input.remaining()} bytes in the ByteBuffer534* {@code input}, starting at {@code input.position()}.535* Upon return, the buffer's position will be equal to its limit;536* its limit will not have changed.537*538* @param input the ByteBuffer539*540* @exception IllegalStateException if this {@code Mac} has not been541* initialized.542* @since 1.5543*/544public final void update(ByteBuffer input) {545chooseFirstProvider();546if (initialized == false) {547throw new IllegalStateException("MAC not initialized");548}549if (input == null) {550throw new IllegalArgumentException("Buffer must not be null");551}552spi.engineUpdate(input);553}554555/**556* Finishes the MAC operation.557*558* <p>A call to this method resets this {@code Mac} object to the559* state it was in when previously initialized via a call to560* {@code init(Key)} or561* {@code init(Key, AlgorithmParameterSpec)}.562* That is, the object is reset and available to generate another MAC from563* the same key, if desired, via new calls to {@code update} and564* {@code doFinal}.565* (In order to reuse this {@code Mac} object with a different key,566* it must be reinitialized via a call to {@code init(Key)} or567* {@code init(Key, AlgorithmParameterSpec)}.568*569* @return the MAC result.570*571* @exception IllegalStateException if this {@code Mac} has not been572* initialized.573*/574public final byte[] doFinal() throws IllegalStateException {575chooseFirstProvider();576if (initialized == false) {577throw new IllegalStateException("MAC not initialized");578}579byte[] mac = spi.engineDoFinal();580spi.engineReset();581return mac;582}583584/**585* Finishes the MAC operation.586*587* <p>A call to this method resets this {@code Mac} object to the588* state it was in when previously initialized via a call to589* {@code init(Key)} or590* {@code init(Key, AlgorithmParameterSpec)}.591* That is, the object is reset and available to generate another MAC from592* the same key, if desired, via new calls to {@code update} and593* {@code doFinal}.594* (In order to reuse this {@code Mac} object with a different key,595* it must be reinitialized via a call to {@code init(Key)} or596* {@code init(Key, AlgorithmParameterSpec)}.597*598* <p>The MAC result is stored in {@code output}, starting at599* {@code outOffset} inclusive.600*601* @param output the buffer where the MAC result is stored602* @param outOffset the offset in {@code output} where the MAC is603* stored604*605* @exception ShortBufferException if the given output buffer is too small606* to hold the result607* @exception IllegalStateException if this {@code Mac} has not been608* initialized.609*/610public final void doFinal(byte[] output, int outOffset)611throws ShortBufferException, IllegalStateException612{613chooseFirstProvider();614if (initialized == false) {615throw new IllegalStateException("MAC not initialized");616}617int macLen = getMacLength();618if (output == null || output.length-outOffset < macLen) {619throw new ShortBufferException620("Cannot store MAC in output buffer");621}622byte[] mac = doFinal();623System.arraycopy(mac, 0, output, outOffset, macLen);624return;625}626627/**628* Processes the given array of bytes and finishes the MAC operation.629*630* <p>A call to this method resets this {@code Mac} object to the631* state it was in when previously initialized via a call to632* {@code init(Key)} or633* {@code init(Key, AlgorithmParameterSpec)}.634* That is, the object is reset and available to generate another MAC from635* the same key, if desired, via new calls to {@code update} and636* {@code doFinal}.637* (In order to reuse this {@code Mac} object with a different key,638* it must be reinitialized via a call to {@code init(Key)} or639* {@code init(Key, AlgorithmParameterSpec)}.640*641* @param input data in bytes642* @return the MAC result.643*644* @exception IllegalStateException if this {@code Mac} has not been645* initialized.646*/647public final byte[] doFinal(byte[] input) throws IllegalStateException648{649chooseFirstProvider();650if (initialized == false) {651throw new IllegalStateException("MAC not initialized");652}653update(input);654return doFinal();655}656657/**658* Resets this {@code Mac} object.659*660* <p>A call to this method resets this {@code Mac} object to the661* state it was in when previously initialized via a call to662* {@code init(Key)} or663* {@code init(Key, AlgorithmParameterSpec)}.664* That is, the object is reset and available to generate another MAC from665* the same key, if desired, via new calls to {@code update} and666* {@code doFinal}.667* (In order to reuse this {@code Mac} object with a different key,668* it must be reinitialized via a call to {@code init(Key)} or669* {@code init(Key, AlgorithmParameterSpec)}.670*/671public final void reset() {672chooseFirstProvider();673spi.engineReset();674}675676/**677* Returns a clone if the provider implementation is cloneable.678*679* @return a clone if the provider implementation is cloneable.680*681* @exception CloneNotSupportedException if this is called on a682* delegate that does not support {@code Cloneable}.683*/684public final Object clone() throws CloneNotSupportedException {685chooseFirstProvider();686Mac that = (Mac)super.clone();687that.spi = (MacSpi)this.spi.clone();688return that;689}690}691692693