blob: 315402856b89a171f39dfe6fb88efcb3e58077fa [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package java.security;
import java.nio.ByteBuffer;
import org.apache.harmony.security.fortress.Engine;
/**
* Uses a one-way hash function to turn an arbitrary number of bytes into a
* fixed-length byte sequence. The original arbitrary-length sequence is the
* <i>message</i>, and the fixed-length byte sequence is the <i>digest</i> or
* <i>message digest</i>.
*
* <h4>Sample Code</h4>
* <p>The basic pattern to digest an {@link java.io.InputStream} looks like this:
* <pre>
* MessageDigest digester = MessageDigest.getInstance("MD5");
* byte[] bytes = new byte[8192];
* int byteCount;
* while ((byteCount = in.read(bytes)) > 0) {
* digester.update(bytes, 0, byteCount);
* }
* byte[] digest = digester.digest();
* </pre>
*
* <p>That is, after creating or resetting a {@code MessageDigest} you should
* call {@link #update(byte[],int,int)} for each block of input data, and then call {@link #digest}
* to get the final digest. Note that calling {@code digest} resets the {@code MessageDigest}.
* Advanced users who want partial digests should clone their {@code MessageDigest} before
* calling {@code digest}.
*
* <p>This class is not thread-safe.
*
* @see MessageDigestSpi
*/
public abstract class MessageDigest extends MessageDigestSpi {
// Used to access common engine functionality
private static final Engine ENGINE = new Engine("MessageDigest");
// The provider
private Provider provider;
// The algorithm.
private String algorithm;
/**
* Constructs a new instance of {@code MessageDigest} with the name of
* the algorithm to use.
*
* @param algorithm
* the name of algorithm to use
*/
protected MessageDigest(String algorithm) {
this.algorithm = algorithm;
}
/**
* Returns a new instance of {@code MessageDigest} that utilizes the
* specified algorithm.
*
* @param algorithm
* the name of the algorithm to use
* @return a new instance of {@code MessageDigest} that utilizes the
* specified algorithm
* @throws NoSuchAlgorithmException
* if the specified algorithm is not available
* @throws NullPointerException
* if {@code algorithm} is {@code null}
*/
public static MessageDigest getInstance(String algorithm)
throws NoSuchAlgorithmException {
if (algorithm == null) {
throw new NullPointerException();
}
Engine.SpiAndProvider sap = ENGINE.getInstance(algorithm, null);
Object spi = sap.spi;
Provider provider = sap.provider;
if (spi instanceof MessageDigest) {
MessageDigest result = (MessageDigest) spi;
result.algorithm = algorithm;
result.provider = provider;
return result;
}
return new MessageDigestImpl((MessageDigestSpi) sap.spi, sap.provider, algorithm);
}
/**
* Returns a new instance of {@code MessageDigest} that utilizes the
* specified algorithm from the specified provider.
*
* @param algorithm
* the name of the algorithm to use
* @param provider
* the name of the provider
* @return a new instance of {@code MessageDigest} that utilizes the
* specified algorithm from the specified provider
* @throws NoSuchAlgorithmException
* if the specified algorithm is not available
* @throws NoSuchProviderException
* if the specified provider is not available
* @throws NullPointerException
* if {@code algorithm} is {@code null}
* @throws IllegalArgumentException if {@code provider == null || provider.isEmpty()}
*/
public static MessageDigest getInstance(String algorithm, String provider)
throws NoSuchAlgorithmException, NoSuchProviderException {
if (provider == null || provider.isEmpty()) {
throw new IllegalArgumentException();
}
Provider p = Security.getProvider(provider);
if (p == null) {
throw new NoSuchProviderException(provider);
}
return getInstance(algorithm, p);
}
/**
* Returns a new instance of {@code MessageDigest} that utilizes the
* specified algorithm from the specified provider.
*
* @param algorithm
* the name of the algorithm to use
* @param provider
* the provider
* @return a new instance of {@code MessageDigest} that utilizes the
* specified algorithm from the specified provider
* @throws NoSuchAlgorithmException
* if the specified algorithm is not available
* @throws NullPointerException
* if {@code algorithm} is {@code null}
* @throws IllegalArgumentException if {@code provider == null}
*/
public static MessageDigest getInstance(String algorithm, Provider provider)
throws NoSuchAlgorithmException {
if (provider == null) {
throw new IllegalArgumentException();
}
if (algorithm == null) {
throw new NullPointerException();
}
Object spi = ENGINE.getInstance(algorithm, provider, null);
if (spi instanceof MessageDigest) {
MessageDigest result = (MessageDigest) spi;
result.algorithm = algorithm;
result.provider = provider;
return result;
}
return new MessageDigestImpl((MessageDigestSpi) spi, provider, algorithm);
}
/**
* Puts this {@code MessageDigest} back in an initial state, such that it is
* ready to compute a one way hash value.
*/
public void reset() {
engineReset();
}
/**
* Updates this {@code MessageDigest} using the given {@code byte}.
*
* @param arg0
* the {@code byte} to update this {@code MessageDigest} with
* @see #reset()
*/
public void update(byte arg0) {
engineUpdate(arg0);
}
/**
* Updates this {@code MessageDigest} using the given {@code byte[]}.
*
* @param input
* the {@code byte} array
* @param offset
* the index of the first byte in {@code input} to update from
* @param len
* the number of bytes in {@code input} to update from
* @throws IllegalArgumentException
* if {@code offset} or {@code len} are not valid in respect to
* {@code input}
*/
public void update(byte[] input, int offset, int len) {
if (input == null ||
// offset < 0 || len < 0 ||
// checks for negative values are commented out intentionally
// see HARMONY-1120 for details
(long) offset + (long) len > input.length) {
throw new IllegalArgumentException();
}
engineUpdate(input, offset, len);
}
/**
* Updates this {@code MessageDigest} using the given {@code byte[]}.
*
* @param input
* the {@code byte} array
* @throws NullPointerException
* if {@code input} is {@code null}
*/
public void update(byte[] input) {
if (input == null) {
throw new NullPointerException();
}
engineUpdate(input, 0, input.length);
}
/**
* Computes and returns the final hash value for this {@link MessageDigest}.
* After the digest is computed the receiver is reset.
*
* @return the computed one way hash value
* @see #reset
*/
public byte[] digest() {
return engineDigest();
}
/**
* Computes and stores the final hash value for this {@link MessageDigest}.
* After the digest is computed the receiver is reset.
*
* @param buf
* the buffer to store the result
* @param offset
* the index of the first byte in {@code buf} to store
* @param len
* the number of bytes allocated for the digest
* @return the number of bytes written to {@code buf}
* @throws DigestException
* if an error occurs
* @throws IllegalArgumentException
* if {@code offset} or {@code len} are not valid in respect to
* {@code buf}
* @see #reset()
*/
public int digest(byte[] buf, int offset, int len) throws DigestException {
if (buf == null ||
// offset < 0 || len < 0 ||
// checks for negative values are commented out intentionally
// see HARMONY-1148 for details
(long) offset + (long) len > buf.length) {
throw new IllegalArgumentException();
}
return engineDigest(buf, offset, len);
}
/**
* Performs the final update and then computes and returns the final hash
* value for this {@link MessageDigest}. After the digest is computed the
* receiver is reset.
*
* @param input
* the {@code byte} array
* @return the computed one way hash value
* @see #reset()
*/
public byte[] digest(byte[] input) {
update(input);
return digest();
}
/**
* Returns a string containing a concise, human-readable description of this
* {@code MessageDigest} including the name of its algorithm.
*
* @return a printable representation for this {@code MessageDigest}
*/
@Override
public String toString() {
return "MESSAGE DIGEST " + algorithm;
}
/**
* Indicates whether to digest are equal by performing a simply
* byte-per-byte compare of the two digests.
*
* @param digesta
* the first digest to be compared
* @param digestb
* the second digest to be compared
* @return {@code true} if the two hashes are equal, {@code false} otherwise
*/
public static boolean isEqual(byte[] digesta, byte[] digestb) {
if (digesta.length != digestb.length) {
return false;
}
for (int i = 0; i < digesta.length; i++) {
if (digesta[i] != digestb[i]) {
return false;
}
}
return true;
}
/**
* Returns the name of the algorithm of this {@code MessageDigest}.
*
* @return the name of the algorithm of this {@code MessageDigest}
*/
public final String getAlgorithm() {
return algorithm;
}
/**
* Returns the provider associated with this {@code MessageDigest}.
*
* @return the provider associated with this {@code MessageDigest}
*/
public final Provider getProvider() {
return provider;
}
/**
* Returns the engine digest length in bytes. If the implementation does not
* implement this function or is not an instance of {@code Cloneable},
* {@code 0} is returned.
*
* @return the digest length in bytes, or {@code 0}
*/
public final int getDigestLength() {
int l = engineGetDigestLength();
if (l != 0) {
return l;
}
if (!(this instanceof Cloneable)) {
return 0;
}
try {
MessageDigest md = (MessageDigest) clone();
return md.digest().length;
} catch (CloneNotSupportedException e) {
return 0;
}
}
@Override
public Object clone() throws CloneNotSupportedException {
if (this instanceof Cloneable) {
return super.clone();
}
throw new CloneNotSupportedException();
}
/**
* Updates this {@code MessageDigest} using the given {@code input}.
*
* @param input
* the {@code ByteBuffer}
*/
public final void update(ByteBuffer input) {
engineUpdate(input);
}
/**
*
* The internal MessageDigest implementation
*
*/
private static class MessageDigestImpl extends MessageDigest {
// MessageDigestSpi implementation
private MessageDigestSpi spiImpl;
// MessageDigestImpl ctor
private MessageDigestImpl(MessageDigestSpi messageDigestSpi,
Provider provider, String algorithm) {
super(algorithm);
super.provider = provider;
spiImpl = messageDigestSpi;
}
// engineReset() implementation
@Override
protected void engineReset() {
spiImpl.engineReset();
}
// engineDigest() implementation
@Override
protected byte[] engineDigest() {
return spiImpl.engineDigest();
}
// engineGetDigestLength() implementation
@Override
protected int engineGetDigestLength() {
return spiImpl.engineGetDigestLength();
}
// engineUpdate() implementation
@Override
protected void engineUpdate(byte arg0) {
spiImpl.engineUpdate(arg0);
}
// engineUpdate() implementation
@Override
protected void engineUpdate(byte[] arg0, int arg1, int arg2) {
spiImpl.engineUpdate(arg0, arg1, arg2);
}
// Returns a clone if the spiImpl is cloneable
@Override
public Object clone() throws CloneNotSupportedException {
if (spiImpl instanceof Cloneable) {
MessageDigestSpi spi = (MessageDigestSpi) spiImpl.clone();
return new MessageDigestImpl(spi, getProvider(), getAlgorithm());
}
throw new CloneNotSupportedException();
}
}
}