|
17 | 17 |
|
18 | 18 | package org.apache.commons.codec.digest; |
19 | 19 |
|
| 20 | +import java.io.BufferedInputStream; |
| 21 | +import java.io.File; |
| 22 | +import java.io.FileInputStream; |
20 | 23 | import java.io.IOException; |
21 | 24 | import java.io.InputStream; |
| 25 | +import java.nio.ByteBuffer; |
22 | 26 | import java.security.InvalidKeyException; |
23 | 27 | import java.security.Key; |
24 | 28 | import java.security.NoSuchAlgorithmException; |
25 | 29 |
|
26 | 30 | import javax.crypto.Mac; |
27 | 31 | import javax.crypto.spec.SecretKeySpec; |
28 | 32 |
|
| 33 | +import org.apache.commons.codec.binary.Hex; |
29 | 34 | import org.apache.commons.codec.binary.StringUtils; |
30 | 35 |
|
31 | 36 | /** |
32 | 37 | * Simplifies common {@link javax.crypto.Mac} tasks. This class is immutable and thread-safe. |
33 | | - * |
| 38 | + * However the Mac may not be. |
34 | 39 | * <p> |
35 | | - * <strong>Note: Not all JCE implementations supports all algorithms. If not supported, an IllegalArgumentException is |
| 40 | + * <strong>Note: Not all JCE implementations support all algorithms. If not supported, an IllegalArgumentException is |
36 | 41 | * thrown.</strong> |
37 | | - * </p> |
38 | | - * |
| 42 | + * <p> |
| 43 | + * Sample usage: |
| 44 | + * <pre> |
| 45 | + * byte[] key = {1,2,3,4}; // don't use this! |
| 46 | + * String valueToDigest = "The quick brown fox jumps over the lazy dog"; |
| 47 | + * byte[] hmac = HmacUtils.use(HmacAlgorithms.HMAC_SHA_224).key(key).update(valueToDigest).doFinal(); |
| 48 | + * // Mac re-use |
| 49 | + * HmacUtils hm1 = HmacUtils.use(HmacAlgorithms.HMAC_SHA_1).key(key); |
| 50 | + * String hexPom = hm1.update(new File("pom.xml")).doFinalHex(); |
| 51 | + * String hexNot = hm1.update(new File("NOTICE.txt")).doFinalHex(); |
| 52 | + * // Mac key update |
| 53 | + * String algo = "HmacNew"; |
| 54 | + * HmacUtils hm2 = HmacUtils.use(algo).key(key); |
| 55 | + * byte[] key2 = {1,2,3,4,5}; // don't use this either! |
| 56 | + * String hexPom2 = hm2.update(new File("pom.xml")).doFinalHex(); |
| 57 | + * String hexNot2 = hm2.key(key2).update(new File("NOTICE.txt")).doFinalHex(); |
| 58 | + * </pre> |
39 | 59 | * @since 1.10 |
40 | 60 | * @version $Id$ |
41 | 61 | */ |
@@ -833,4 +853,198 @@ public static Mac updateHmac(final Mac mac, final String valueToDigest) { |
833 | 853 | mac.update(StringUtils.getBytesUtf8(valueToDigest)); |
834 | 854 | return mac; |
835 | 855 | } |
| 856 | + |
| 857 | + HmacUtils() { // TODO why does test code try to instantiate this? |
| 858 | + this(null); |
| 859 | + } |
| 860 | + |
| 861 | + // Fluent interface |
| 862 | + |
| 863 | + private final Mac mac; |
| 864 | + |
| 865 | + private HmacUtils(final Mac mac) { |
| 866 | + this.mac = mac; |
| 867 | + } |
| 868 | + |
| 869 | + |
| 870 | + /** |
| 871 | + * Creates an instance using the provided {@link Mac} |
| 872 | + * If necessary, the |
| 873 | + * key must be provided using the {@link #key(byte[])} method |
| 874 | + * before it can be used further. |
| 875 | + * |
| 876 | + * @param algorithm to be used. |
| 877 | + * @return the instance |
| 878 | + * @since 1.11 |
| 879 | + */ |
| 880 | + public static HmacUtils use(final Mac mac) { |
| 881 | + return new HmacUtils(mac); |
| 882 | + } |
| 883 | + |
| 884 | + /** |
| 885 | + * Creates an instance using the provided algorithm type. |
| 886 | + * The key must be provided using the {@link #key(byte[])} method |
| 887 | + * before it can be used further. |
| 888 | + * |
| 889 | + * @param algorithm to be used. |
| 890 | + * @return the instance |
| 891 | + * @since 1.11 |
| 892 | + */ |
| 893 | + public static HmacUtils use(final String algorithm) { |
| 894 | + try { |
| 895 | + return new HmacUtils(Mac.getInstance(algorithm)); |
| 896 | + } catch (NoSuchAlgorithmException e) { |
| 897 | + throw new IllegalArgumentException(e); |
| 898 | + } |
| 899 | + } |
| 900 | + |
| 901 | + /** |
| 902 | + * Creates an instance using the provided algorithm type. |
| 903 | + * The key must be provided using the {@link #key(byte[])} method |
| 904 | + * before it can be used further. |
| 905 | + * |
| 906 | + * @param algorithm to be used. |
| 907 | + * @return the instance |
| 908 | + * @since 1.11 |
| 909 | + */ |
| 910 | + public static HmacUtils use(final HmacAlgorithms algorithm) { |
| 911 | + return use(algorithm.getName()); |
| 912 | + } |
| 913 | + |
| 914 | + /** |
| 915 | + * Updates the stored {@link Mac} with the new key. |
| 916 | + * This resets the Mac ready for re-use. |
| 917 | + * |
| 918 | + * @param key the new key |
| 919 | + * @return this instance |
| 920 | + * @since 1.11 |
| 921 | + */ |
| 922 | + public HmacUtils key(byte[] key) { |
| 923 | + final SecretKeySpec keySpec = new SecretKeySpec(key, mac.getAlgorithm()); |
| 924 | + try { |
| 925 | + mac.init(keySpec); |
| 926 | + } catch (InvalidKeyException e) { |
| 927 | + throw new IllegalArgumentException(e); |
| 928 | + } |
| 929 | + return this; |
| 930 | + } |
| 931 | + |
| 932 | + /** |
| 933 | + * Updates the stored {@link Mac} with the value. |
| 934 | + * |
| 935 | + * @param valueToDigest |
| 936 | + * the value to update the {@link Mac} with (maybe null or empty) |
| 937 | + * @return the updated instance |
| 938 | + * @throws IllegalStateException |
| 939 | + * if the Mac was not initialized |
| 940 | + * @since 1.11 |
| 941 | + */ |
| 942 | + public HmacUtils update(final byte[] valueToDigest) { |
| 943 | + mac.update(valueToDigest); |
| 944 | + return this; |
| 945 | + } |
| 946 | + |
| 947 | + /** |
| 948 | + * Updates the stored {@link Mac} with the value. |
| 949 | + * |
| 950 | + * @param valueToDigest |
| 951 | + * the value to update the {@link Mac} with (maybe null or empty) |
| 952 | + * @return the updated instance |
| 953 | + * @throws IllegalStateException |
| 954 | + * if the Mac was not initialized |
| 955 | + * @since 1.11 |
| 956 | + */ |
| 957 | + public HmacUtils update(final ByteBuffer valueToDigest) { |
| 958 | + mac.update(valueToDigest); |
| 959 | + return this; |
| 960 | + } |
| 961 | + |
| 962 | + /** |
| 963 | + * Updates the stored {@link Mac} with the value. |
| 964 | + * String is converted to bytes using the UTF-8 charset. |
| 965 | + * @param valueToDigest |
| 966 | + * the value to update the {@link Mac} with. |
| 967 | + * @return the updated instance |
| 968 | + * @throws IllegalStateException |
| 969 | + * if the Mac was not initialized |
| 970 | + * @since 1.11 |
| 971 | + */ |
| 972 | + public HmacUtils update(final String valueToDigest) { |
| 973 | + mac.update(StringUtils.getBytesUtf8(valueToDigest)); |
| 974 | + return this; |
| 975 | + } |
| 976 | + |
| 977 | + /** |
| 978 | + * Updates the stored {@link Mac} with the value. |
| 979 | + * |
| 980 | + * @param valueToDigest |
| 981 | + * the value to update the {@link Mac} with |
| 982 | + * <p> |
| 983 | + * The InputStream must not be null and will not be closed |
| 984 | + * </p> |
| 985 | + * @return the updated instance |
| 986 | + * @throws IOException |
| 987 | + * If an I/O error occurs. |
| 988 | + * @throws IllegalStateException |
| 989 | + * If the Mac was not initialized |
| 990 | + * @since 1.11 |
| 991 | + */ |
| 992 | + public HmacUtils update(final InputStream valueToDigest) throws IOException { |
| 993 | + final byte[] buffer = new byte[STREAM_BUFFER_LENGTH]; |
| 994 | + int read; |
| 995 | + |
| 996 | + while ((read = valueToDigest.read(buffer, 0, STREAM_BUFFER_LENGTH) ) > -1) { |
| 997 | + mac.update(buffer, 0, read); |
| 998 | + } |
| 999 | + return this; |
| 1000 | + } |
| 1001 | + |
| 1002 | + /** |
| 1003 | + * Updates the stored {@link Mac} with the value. |
| 1004 | + * |
| 1005 | + * @param valueToDigest |
| 1006 | + * the value to update the {@link Mac} with |
| 1007 | + * <p> |
| 1008 | + * The InputStream must not be null and will not be closed |
| 1009 | + * </p> |
| 1010 | + * @return the updated instance |
| 1011 | + * @throws IOException |
| 1012 | + * If an I/O error occurs. |
| 1013 | + * @throws IllegalStateException |
| 1014 | + * If the Mac was not initialized |
| 1015 | + * @since 1.11 |
| 1016 | + */ |
| 1017 | + public HmacUtils update(final File valueToDigest) throws IOException { |
| 1018 | + final BufferedInputStream stream = new BufferedInputStream(new FileInputStream(valueToDigest)); |
| 1019 | + try { |
| 1020 | + return update(stream); |
| 1021 | + } finally { |
| 1022 | + stream.close(); |
| 1023 | + } |
| 1024 | + } |
| 1025 | + |
| 1026 | + /** |
| 1027 | + * Finishes the MAC operation and returns the result. |
| 1028 | + * The Mac can be re-used to produce further results from the same key. |
| 1029 | + * Or the key can be reset and the Mac reused. |
| 1030 | + * |
| 1031 | + * @return the result as a byte array |
| 1032 | + * @since 1.11 |
| 1033 | + */ |
| 1034 | + public byte[] doFinal() { |
| 1035 | + return mac.doFinal(); |
| 1036 | + } |
| 1037 | + |
| 1038 | + /** |
| 1039 | + * Finishes the MAC operation and returns the result. |
| 1040 | + * The Mac can be re-used to produce further results from the same key. |
| 1041 | + * Or the key can be reset and the Mac reused. |
| 1042 | + * |
| 1043 | + * @return the result as a Hex String |
| 1044 | + * @since 1.11 |
| 1045 | + */ |
| 1046 | + public String doFinalHex() { |
| 1047 | + return Hex.encodeHexString(mac.doFinal()); |
| 1048 | + } |
| 1049 | + |
836 | 1050 | } |
0 commit comments