Skip to content

Commit 4c84e20

Browse files
committed
CODEC-222 Fluent interface for HmacUtils
Initial implementation git-svn-id: https://svn.apache.org/repos/asf/commons/proper/codec/trunk@1744554 13f79535-47bb-0310-9956-ffa450edef68
1 parent a3816d2 commit 4c84e20

1 file changed

Lines changed: 218 additions & 4 deletions

File tree

src/main/java/org/apache/commons/codec/digest/HmacUtils.java

Lines changed: 218 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,25 +17,45 @@
1717

1818
package org.apache.commons.codec.digest;
1919

20+
import java.io.BufferedInputStream;
21+
import java.io.File;
22+
import java.io.FileInputStream;
2023
import java.io.IOException;
2124
import java.io.InputStream;
25+
import java.nio.ByteBuffer;
2226
import java.security.InvalidKeyException;
2327
import java.security.Key;
2428
import java.security.NoSuchAlgorithmException;
2529

2630
import javax.crypto.Mac;
2731
import javax.crypto.spec.SecretKeySpec;
2832

33+
import org.apache.commons.codec.binary.Hex;
2934
import org.apache.commons.codec.binary.StringUtils;
3035

3136
/**
3237
* Simplifies common {@link javax.crypto.Mac} tasks. This class is immutable and thread-safe.
33-
*
38+
* However the Mac may not be.
3439
* <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
3641
* 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>
3959
* @since 1.10
4060
* @version $Id$
4161
*/
@@ -833,4 +853,198 @@ public static Mac updateHmac(final Mac mac, final String valueToDigest) {
833853
mac.update(StringUtils.getBytesUtf8(valueToDigest));
834854
return mac;
835855
}
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+
8361050
}

0 commit comments

Comments
 (0)