/*
* Part of the CCNx Java Library.
*
* Copyright (C) 2008-2010 Palo Alto Research Center, Inc.
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License version 2.1
* as published by the Free Software Foundation.
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details. You should have received
* a copy of the GNU Lesser General Public License along with this library;
* if not, write to the Free Software Foundation, Inc., 51 Franklin Street,
* Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.ccnx.ccn.impl.security.crypto.util;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateEncodingException;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.DERObject;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x509.DigestInfo;
import org.ccnx.ccn.impl.support.Log;
/**
* Helper class for digest algorithms.
* Includes static methods to compute the digest of an array of bytes with
* DEFAULT_DIGEST_ALGORITHM ("SHA-1" by default).
* Includes methods for computing Merkle hash trees (hash computation of the
* concatenation of two or more arrays of bytes.)
*/
public class DigestHelper {
public static String DEFAULT_DIGEST_ALGORITHM = "SHA-1";
// public static String DEFAULT_DIGEST_ALGORITHM = "SHA-256"; // change length to 32
public static int DEFAULT_DIGEST_LENGTH = 20;
protected MessageDigest _md;
/**
* Instantiates a MessageDigest of type DEFAULT_DIGEST_ALGORITHM.
*/
public DigestHelper() {
try {
_md = MessageDigest.getInstance(getDefaultDigest());
} catch (java.security.NoSuchAlgorithmException ex) {
// possible configuration problem
Log.warning("Fatal Error: cannot find default algorithm " + getDefaultDigest());
throw new RuntimeException("Error: can't find default algorithm " + getDefaultDigest() + "! " + ex.toString());
}
}
/**
* Instantiates a MessageDigest of type digestAlgorithm.
* @param digestAlgorithm the digest algorithm selected.
* @throws NoSuchAlgorithmException
*/
public DigestHelper(String digestAlgorithm) throws NoSuchAlgorithmException {
_md = MessageDigest.getInstance((null == digestAlgorithm) ? getDefaultDigest() : digestAlgorithm);
}
/**
* This method is non-static so subclasses can override it.
* @return the default digest algorithm.
*/
public String getDefaultDigest() { return DEFAULT_DIGEST_ALGORITHM; }
/**
* Updates the digest using the specified array of bytes, starting at the specified offset.
* @param content the array of bytes.
* @param offset the offset.
* @param len the number of bytes to use, starting at offset.
*/
public void update(byte [] content, int offset, int len) {
_md.update(content, offset, len);
}
public void update(byte [] content) {
_md.update(content);
}
/**
* Completes the hash computation by performing final operations such as padding.
* The digest is reset after this call is made.
* @return the array of bytes for the resulting hash value.
*/
public byte [] digest() {
return _md.digest();
}
/**
* Static method to hash an array of bytes with DEFAULT_DIGEST_ALGORITHM.
* @param content the array of bytes.
* @return the array of bytes for the resulting hash value.
*/
public static byte[] digest(byte [] content) {
if (null == content) {
throw new IllegalArgumentException("Content cannot be null!");
}
return digest(content, 0, content.length);
}
/**
* Static method to hash an array of bytes with a specified digest algorithm.
* @param digestAlgorithm the digest algorithm.
* @param content the array of bytes.
* @return the array of bytes for the resulting hash value.
* @throws NoSuchAlgorithmException
*/
public static byte [] digest(String digestAlgorithm, byte [] content) throws NoSuchAlgorithmException {
if (null == content) {
throw new IllegalArgumentException("Content cannot be null!");
}
return digest(digestAlgorithm, content, 0, content.length);
}
/**
* Static method to hash an array of bytes with DEFAULT_DIGEST_ALGORITHM,
* starting at the specified offset.
* @param content the array of bytes.
* @param offset the offset.
* @param length the number of bytes to use, starting at offset.
* @return the array of bytes for the resulting hash value.
*/
public static byte [] digest(byte [] content, int offset, int length) {
DigestHelper dh = new DigestHelper();
dh.update(content, offset, length);
return dh.digest();
}
/**
* Static method to hash an array of bytes with a specified digest algorithm,
* starting at the specified offset.
* @param digestAlgorithm the digest algorithm.
* @param content the array of bytes.
* @param offset the offset.
* @param length the number of bytes to user, starting at offset.
* @return the array of bytes for the resulting hash value.
* @throws NoSuchAlgorithmException
*/
public static byte [] digest(String digestAlgorithm, byte [] content, int offset, int length) throws NoSuchAlgorithmException {
DigestHelper dh = new DigestHelper(digestAlgorithm);
dh.update(content, offset, length);
return dh.digest();
}
/**
* Helper function for building Merkle hash trees. Returns digest of
* two concatenated byte arrays. If either is null, simply includes
* the non-null array. The digest is computed with DEFAULT_DIGEST_ALGORITHM.
* @param content1 first array of bytes.
* @param content2 second array of bytes.
* @return the array of bytes for the resulting hash value.
*/
public static byte[] digest(byte [] content1, byte [] content2) {
return digest(new byte [][]{content1, content2});
}
/**
* Helper function for building Merkle hash trees. Returns digest of
* two concatenated byte arrays. If either is null, simply includes
* the non-null array. The digest is computed with the specified digest algorithm.
* @param digestAlgorithm the digest algorithm.
* @param content1 first array of bytes.
* @param content2 second array of bytes.
* @return the array of bytes for the resulting hash value.
* @throws NoSuchAlgorithmException
*/
public static byte [] digest(String digestAlgorithm, byte [] content1, byte [] content2) throws NoSuchAlgorithmException {
return digest(digestAlgorithm, new byte [][]{content1, content2});
}
/**
* Helper function for building Merkle hash trees.
* Returns the digest of an array of byte arrays.
* The digest is computed with DEFAULT_DIGEST_ALGORITHM.
* @param contents the array of byte arrays.
* @return the array of bytes for the resulting hash value.
*/
public static byte [] digest(byte[][] contents) {
DigestHelper dh = new DigestHelper();
for (int i=0; i < contents.length; ++i) {
dh.update(contents[i], 0, contents[i].length);
}
return dh.digest();
}
/**
* Helper function for building Merkle hash trees.
* Returns the digest of an array of byte arrays.
* The digest is computed with the specified digest algorithm.
* @param digestAlgorithm the digest algorithm.
* @param contents the array of byte arrays.
* @return the array of bytes of the resulting hash value.
* @throws NoSuchAlgorithmException
*/
public static byte [] digest(String digestAlgorithm, byte[][] contents) throws NoSuchAlgorithmException {
DigestHelper dh = new DigestHelper(digestAlgorithm);
for (int i=0; i < contents.length; ++i) {
dh.update(contents[i], 0, contents[i].length);
}
return dh.digest();
}
/**
* Digests some array of bytes with the specified digest algorithm and wraps it in a DigestInfo.
* @param digestAlgorithm the digest algorithm.
* @param content the array of bytes.
* @return the array of bytes of the resulting DigestInfo.
* @throws CertificateEncodingException
* @throws NoSuchAlgorithmException
*/
public static byte [] encodedDigest(String digestAlgorithm, byte [] content) throws CertificateEncodingException, NoSuchAlgorithmException {
byte [] digest = digest(digestAlgorithm, content);
return digestEncoder(digestAlgorithm, digest);
}
/**
* Digests some array of bytes with DEFAULT_DIGEST_ALGORITHM and wraps it in a DigestInfo.
* @param content the array of bytes.
* @return the array of bytes of the resulting DigestInfo.
* @throws CertificateEncodingException
* @throws NoSuchAlgorithmException
*/
public static byte [] encodedDigest(byte [] content) throws CertificateEncodingException {
byte [] digest = digest(content);
return digestEncoder(DEFAULT_DIGEST_ALGORITHM, digest);
}
/**
* Takes a specified digest and wraps it in a DigestInfo for the specified digest algorithm.
* @param digestAlgorithm the digest algorithm.
* @param theDigest the digest.
* @return the array of bytes of the resulting DigestInfo.
*/
public static byte [] digestEncoder(String digestAlgorithm, byte [] theDigest) {
AlgorithmIdentifier digestAlg =
new AlgorithmIdentifier(OIDLookup.getDigestOID(digestAlgorithm));
DigestInfo info = new DigestInfo(digestAlg, theDigest);
try {
return CryptoUtil.encode(info);
} catch (CertificateEncodingException e) {
Log.warning("Exception encoding digest as digest info using standard algorithms: " + e.getMessage());
Log.warningStackTrace(e);
// DKS TODO what to actually throw
return new byte[0];
}
}
/**
* Returns the DigestInfo corresponding to a specified array of bytes.
* @param encodedDigest the array of bytes.
* @return the corresponding DigestInfo.
* @throws CertificateEncodingException
*/
public static DigestInfo digestDecoder(byte [] encodedDigest) throws CertificateEncodingException {
DERObject di = CryptoUtil.decode(encodedDigest);
DigestInfo info = new DigestInfo((ASN1Sequence)di);
return info;
}
/**
* Returns an array of bytes as a String.
* @param binaryObject the array of bytes.
* @param radix the radix.
* @return the corresponding String.
*/
public static String printBytes(byte [] binaryObject, int radix) {
BigInteger bi = new BigInteger(binaryObject);
return bi.toString(radix);
}
/**
* Returns a String as an array of bytes.
* @param encodedString the String.
* @param radix the radix.
* @return the corresponding array of bytes.
*/
public static byte [] scanBytes(String encodedString, int radix) {
BigInteger bi = new BigInteger(encodedString, radix);
return bi.toByteArray();
}
}