| // Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) |
| |
| package org.xbill.DNS.utils; |
| |
| import java.util.Arrays; |
| import java.security.*; |
| |
| /** |
| * An implementation of the HMAC message authentication code. |
| * |
| * @author Brian Wellington |
| */ |
| |
| public class HMAC { |
| |
| private MessageDigest digest; |
| private int blockLength; |
| |
| private byte [] ipad, opad; |
| |
| private static final byte IPAD = 0x36; |
| private static final byte OPAD = 0x5c; |
| |
| private void |
| init(byte [] key) { |
| int i; |
| |
| if (key.length > blockLength) { |
| key = digest.digest(key); |
| digest.reset(); |
| } |
| ipad = new byte[blockLength]; |
| opad = new byte[blockLength]; |
| for (i = 0; i < key.length; i++) { |
| ipad[i] = (byte) (key[i] ^ IPAD); |
| opad[i] = (byte) (key[i] ^ OPAD); |
| } |
| for (; i < blockLength; i++) { |
| ipad[i] = IPAD; |
| opad[i] = OPAD; |
| } |
| digest.update(ipad); |
| } |
| |
| /** |
| * Creates a new HMAC instance |
| * @param digest The message digest object. |
| * @param blockLength The block length of the message digest. |
| * @param key The secret key |
| */ |
| public |
| HMAC(MessageDigest digest, int blockLength, byte [] key) { |
| digest.reset(); |
| this.digest = digest; |
| this.blockLength = blockLength; |
| init(key); |
| } |
| |
| /** |
| * Creates a new HMAC instance |
| * @param digestName The name of the message digest function. |
| * @param blockLength The block length of the message digest. |
| * @param key The secret key. |
| */ |
| public |
| HMAC(String digestName, int blockLength, byte [] key) { |
| try { |
| digest = MessageDigest.getInstance(digestName); |
| } catch (NoSuchAlgorithmException e) { |
| throw new IllegalArgumentException("unknown digest algorithm " |
| + digestName); |
| } |
| this.blockLength = blockLength; |
| init(key); |
| } |
| |
| /** |
| * Creates a new HMAC instance |
| * @param digest The message digest object. |
| * @param key The secret key |
| * @deprecated won't work with digests using a padding length other than 64; |
| * use {@code HMAC(MessageDigest digest, int blockLength, |
| * byte [] key)} instead. |
| * @see HMAC#HMAC(MessageDigest digest, int blockLength, byte [] key) |
| */ |
| public |
| HMAC(MessageDigest digest, byte [] key) { |
| this(digest, 64, key); |
| } |
| |
| /** |
| * Creates a new HMAC instance |
| * @param digestName The name of the message digest function. |
| * @param key The secret key. |
| * @deprecated won't work with digests using a padding length other than 64; |
| * use {@code HMAC(String digestName, int blockLength, byte [] key)} |
| * instead |
| * @see HMAC#HMAC(String digestName, int blockLength, byte [] key) |
| */ |
| public |
| HMAC(String digestName, byte [] key) { |
| this(digestName, 64, key); |
| } |
| |
| /** |
| * Adds data to the current hash |
| * @param b The data |
| * @param offset The index at which to start adding to the hash |
| * @param length The number of bytes to hash |
| */ |
| public void |
| update(byte [] b, int offset, int length) { |
| digest.update(b, offset, length); |
| } |
| |
| /** |
| * Adds data to the current hash |
| * @param b The data |
| */ |
| public void |
| update(byte [] b) { |
| digest.update(b); |
| } |
| |
| /** |
| * Signs the data (computes the secure hash) |
| * @return An array with the signature |
| */ |
| public byte [] |
| sign() { |
| byte [] output = digest.digest(); |
| digest.reset(); |
| digest.update(opad); |
| return digest.digest(output); |
| } |
| |
| /** |
| * Verifies the data (computes the secure hash and compares it to the input) |
| * @param signature The signature to compare against |
| * @return true if the signature matches, false otherwise |
| */ |
| public boolean |
| verify(byte [] signature) { |
| return verify(signature, false); |
| } |
| |
| /** |
| * Verifies the data (computes the secure hash and compares it to the input) |
| * @param signature The signature to compare against |
| * @param truncation_ok If true, the signature may be truncated; only the |
| * number of bytes in the provided signature are compared. |
| * @return true if the signature matches, false otherwise |
| */ |
| public boolean |
| verify(byte [] signature, boolean truncation_ok) { |
| byte [] expected = sign(); |
| if (truncation_ok && signature.length < expected.length) { |
| byte [] truncated = new byte[signature.length]; |
| System.arraycopy(expected, 0, truncated, 0, truncated.length); |
| expected = truncated; |
| } |
| return Arrays.equals(signature, expected); |
| } |
| |
| /** |
| * Resets the HMAC object for further use |
| */ |
| public void |
| clear() { |
| digest.reset(); |
| digest.update(ipad); |
| } |
| |
| /** |
| * Returns the length of the digest. |
| */ |
| public int |
| digestLength() { |
| return digest.getDigestLength(); |
| } |
| |
| } |