001/**
002 * Java Web Archive Toolkit - Software to read and validate ARC, WARC
003 * and GZip files. (http://jwat.org/)
004 * Copyright 2011-2012 Netarkivet.dk (http://netarkivet.dk/)
005 *
006 * Licensed under the Apache License, Version 2.0 (the "License");
007 * you may not use this file except in compliance with the License.
008 * You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.jwat.common;
019
020import java.security.MessageDigest;
021import java.security.NoSuchAlgorithmException;
022import java.security.Provider;
023import java.util.Arrays;
024import java.util.Map;
025import java.util.TreeMap;
026import java.util.zip.CRC32;
027import java.util.zip.Checksum;
028
029/**
030 * This class represents a message digest including information about algorithm
031 * and encoding used.
032 *
033 * @author nicl
034 */
035public class Digest {
036
037    static {
038        Provider[] providers = SecurityProviderTools.getSecurityProviders();
039        if (!SecurityProviderTools.isProviderAvailable(providers, "BC")) {
040            SecurityProviderTools.loadBCProvider();
041        }
042    }
043
044    /** Digest algorithm used. */
045    public String algorithm;
046
047    /** Digest in bytes as returned by e.g. <code>MessageDigest</code>. */
048    public byte[] digestBytes;
049
050    /** Digest string, encoded. */
051    public String digestString;
052
053    /** Digest encoding used. (E.g. Base16, 32 or 64) */
054    public String encoding;
055
056    /** Cache length of algorithm digest output. */
057    protected static Map<String, Integer> digestAlgoLengthache = new TreeMap<String, Integer>();
058
059    /**
060     * Returns the length of an algorithms digest output or -1 if it is an
061     * invalid digest algorithm.
062     * @param digestAlgorithm digest algorithm
063     * @return digest algorithm output length or -1 if invalid digest algorithm
064     */
065    public static synchronized int digestAlgorithmLength(String digestAlgorithm) {
066        if (digestAlgorithm == null || digestAlgorithm.length() == 0) {
067            throw new IllegalArgumentException("'digestAlgorithm' is empty or null");
068        }
069        Integer cachedLen = digestAlgoLengthache.get(digestAlgorithm);
070        if (cachedLen == null) {
071            try {
072                MessageDigest md = MessageDigest.getInstance(digestAlgorithm);
073                byte[] digest = md.digest(new byte[16]);
074                cachedLen = digest.length;
075                md.reset();
076            } catch (NoSuchAlgorithmException e) {
077            }
078            if (cachedLen == null) {
079                cachedLen = -1;
080            }
081            digestAlgoLengthache.put(digestAlgorithm,  cachedLen);
082        }
083        return cachedLen;
084    }
085
086    @Override
087    public boolean equals(Object obj) {
088        if (obj == null || !(obj instanceof Digest)) {
089            return false;
090        }
091        Digest digestObj = (Digest)obj;
092        if (!Arrays.equals(digestBytes, digestObj.digestBytes)) {
093            return false;
094        }
095        if (algorithm != null) {
096            if (!algorithm.equals(digestObj.algorithm)) {
097                return false;
098            }
099        } else if (digestObj.algorithm != null) {
100            return false;
101        }
102        if (digestString != null) {
103            if (!digestString.equals(digestObj.digestString)) {
104                return false;
105            }
106        } else if (digestObj.digestString != null) {
107            return false;
108        }
109        if (encoding != null) {
110            if (!encoding.equals(digestObj.encoding)) {
111                return false;
112            }
113        } else if (digestObj.encoding != null) {
114            return false;
115        }
116        return true;
117    }
118
119    @Override
120    public int hashCode() {
121        int hashCode = 0;
122        if (digestBytes != null) {
123            Checksum checksum = new CRC32();
124            checksum.update(digestBytes, 0, digestBytes.length);
125            hashCode ^= checksum.getValue();
126        }
127        if (algorithm != null) {
128            hashCode ^= algorithm.hashCode();
129        }
130        if (digestString != null) {
131            hashCode ^= digestString.hashCode();
132        }
133        if (encoding != null) {
134            hashCode ^= encoding.hashCode();
135        }
136        return hashCode;
137    }
138
139}