package com.intel.mtwilson.model; import com.intel.dcsg.cpg.validation.ObjectModel; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Arrays; import org.apache.commons.codec.DecoderException; import org.apache.commons.codec.binary.Hex; import com.fasterxml.jackson.annotation.JsonValue; //import org.codehaus.jackson.annotate.JsonValue; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Base class for representations of MD5, SHA1 and other digests. * * @since 0.5.4 * @author jbuhacoff */ public abstract class AbstractMessageDigest extends ObjectModel { protected static Logger log = LoggerFactory.getLogger(AbstractMessageDigest.class); private final String algorithm; // parameter for MessageDigest.getInstance(String) private final int digestLength; // in bytes private byte[] value = null; private String hex = null; public AbstractMessageDigest(String algorithm, int digestLength) { this.algorithm = algorithm; this.digestLength = digestLength; } public String algorithm() { return algorithm; } /** * Makes a copy of the byte array. * @param value */ protected void setBytes(byte[] value) { if( value == null ) { throw new IllegalArgumentException(algorithm+": Null digest"); } if( value.length != digestLength ) { throw new IllegalArgumentException(algorithm+": Digest must be "+digestLength+" bytes long: "+Hex.encodeHexString(value)); } this.value = new byte[value.length]; System.arraycopy(value, 0, this.value, 0, value.length); this.hex = Hex.encodeHexString(value); } protected void setHex(String hex) { if( hex == null ) { throw new IllegalArgumentException(algorithm+": Null digest"); } if( hex.isEmpty() ) { throw new IllegalArgumentException(algorithm+": Empty digest"); } if( hex.length() != digestLength*2 ) { throw new IllegalArgumentException(algorithm+": Digest must be "+digestLength+" bytes ("+(digestLength*2)+" hex digits) long: "+hex); } try { this.value = Hex.decodeHex(hex.toCharArray()); } catch(DecoderException e) { throw new IllegalArgumentException(algorithm+": Invalid digest: "+value, e); } this.hex = hex; } /** * Returns the bytes comprising the Digest. * @return */ public byte[] toByteArray() { return value; } /** * Returns a string representing the Digest in hexadecimal form. * * @see java.lang.Object#toString() */ @JsonValue @Override public String toString() { return hex; } @Override public int hashCode() { return hex.hashCode(); } @Override public boolean equals(Object obj) { if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } final AbstractMessageDigest other = (AbstractMessageDigest) obj; if (!Arrays.equals(value, other.value)) { return false; } return true; } @Override protected void validate() { if( value == null ) { fault("%s value must not be null", algorithm); } else if( value.length != digestLength ) { fault("%s value must be %d bytes long", algorithm, digestLength); } } /** * Subclasses can implement a valueOf function like this: * * public Md5Digest valueOf(byte[] message) { * return valueOf(Md5Digest.class, message); * } * * @param <T> * @param clazz * @param message * @return * @throws NoSuchAlgorithmException * @throws InstantiationException * @throws IllegalAccessException */ protected static <T extends AbstractMessageDigest> T valueOf(Class<T> clazz, byte[] message) { try { T model = clazz.newInstance(); MessageDigest hash = MessageDigest.getInstance(model.algorithm()); byte[] digest = hash.digest(message); model.setBytes(digest); return model; } catch(InstantiationException e) { log.error("Cannot create instance of "+clazz.getName(), e); return null; } catch(IllegalAccessException e) { log.error("Cannot create instance of "+clazz.getName(), e); return null; } catch(NoSuchAlgorithmException e) { log.error("Cannot create instance of "+clazz.getName(), e); return null; } } }