Skip to content

Commit 8e21950

Browse files
balamuruganaharshavardhana
authored andcommitted
fix: calculate/add MD5 hash for multiple object delete. (#581)
Fixes #579
1 parent f76b496 commit 8e21950

File tree

2 files changed

+74
-80
lines changed

2 files changed

+74
-80
lines changed

api/src/main/java/io/minio/Digest.java

Lines changed: 54 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import com.google.common.io.BaseEncoding;
2727

2828
import io.minio.errors.InsufficientDataException;
29+
import io.minio.errors.InternalException;
2930

3031

3132
/**
@@ -42,84 +43,85 @@ private Digest() {}
4243
* Returns SHA-256 hash of given string.
4344
*/
4445
public static String sha256Hash(String string) throws NoSuchAlgorithmException {
45-
return sha256Hash(string.getBytes(StandardCharsets.UTF_8));
46-
}
47-
48-
49-
/**
50-
* Returns SHA-256 hash of given byte array.
51-
*/
52-
public static String sha256Hash(byte[] data) throws NoSuchAlgorithmException {
53-
return sha256Hash(data, data.length);
54-
}
55-
56-
57-
/**
58-
* Returns SHA-256 hash string of given byte array and it's length.
59-
*/
60-
public static String sha256Hash(byte[] data, int length) throws NoSuchAlgorithmException {
61-
MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
62-
63-
messageDigest.update(data, 0, length);
64-
65-
return BaseEncoding.base16().encode(messageDigest.digest()).toLowerCase();
46+
byte[] data = string.getBytes(StandardCharsets.UTF_8);
47+
MessageDigest sha256Digest = MessageDigest.getInstance("SHA-256");
48+
sha256Digest.update((byte[]) data, 0, data.length);
49+
return BaseEncoding.base16().encode(sha256Digest.digest()).toLowerCase();
6650
}
6751

6852

6953
/**
70-
* Returns SHA-256 of given input stream and it's length.
54+
* Returns SHA-256 hash of given data and it's length.
7155
*
72-
* @param inputStream Input stream whose type is either {@link RandomAccessFile} or {@link BufferedInputStream}.
73-
* @param len Length of Input stream.
56+
* @param data must be {@link RandomAccessFile}, {@link BufferedInputStream} or byte array.
57+
* @param len length of data to be read for hash calculation.
7458
*/
75-
public static String sha256Hash(Object inputStream, int len)
76-
throws NoSuchAlgorithmException, IOException, InsufficientDataException {
59+
public static String sha256Hash(Object data, int len)
60+
throws NoSuchAlgorithmException, IOException, InsufficientDataException, InternalException {
7761
MessageDigest sha256Digest = MessageDigest.getInstance("SHA-256");
78-
updateDigests(inputStream, len, sha256Digest, null);
79-
return BaseEncoding.base16().encode(sha256Digest.digest()).toLowerCase();
80-
}
81-
82-
/**
83-
* Returns MD5 hash of given string.
84-
*/
85-
public static String md5Hash(String string) throws NoSuchAlgorithmException {
86-
return md5Hash(string.getBytes(StandardCharsets.UTF_8));
87-
}
8862

63+
if (data instanceof BufferedInputStream || data instanceof RandomAccessFile) {
64+
updateDigests(data, len, sha256Digest, null);
65+
} else if (data instanceof byte[]) {
66+
sha256Digest.update((byte[]) data, 0, len);
67+
} else {
68+
throw new InternalException("Unknown data source to calculate sha256 hash. This should not happen, "
69+
+ "please report this issue at https://github.com/minio/minio-java/issues");
70+
}
8971

90-
/**
91-
* Returns MD5 hash of given byte array.
92-
*/
93-
public static String md5Hash(byte[] data) throws NoSuchAlgorithmException {
94-
return md5Hash(data, data.length);
72+
return BaseEncoding.base16().encode(sha256Digest.digest()).toLowerCase();
9573
}
9674

9775

9876
/**
99-
* Returns MD5 hash of given byte array and it's length.
77+
* Returns SHA-256 and MD5 hashes of given data and it's length.
78+
*
79+
* @param data must be {@link RandomAccessFile}, {@link BufferedInputStream} or byte array.
80+
* @param len length of data to be read for hash calculation.
10081
*/
101-
public static String md5Hash(byte[] data, int length) throws NoSuchAlgorithmException {
102-
MessageDigest messageDigest = MessageDigest.getInstance("MD5");
82+
public static String[] sha256Md5Hashes(Object data, int len)
83+
throws NoSuchAlgorithmException, IOException, InsufficientDataException, InternalException {
84+
MessageDigest sha256Digest = MessageDigest.getInstance("SHA-256");
85+
MessageDigest md5Digest = MessageDigest.getInstance("MD5");
10386

104-
messageDigest.update(data, 0, length);
87+
if (data instanceof BufferedInputStream || data instanceof RandomAccessFile) {
88+
updateDigests(data, len, sha256Digest, md5Digest);
89+
} else if (data instanceof byte[]) {
90+
sha256Digest.update((byte[]) data, 0, len);
91+
md5Digest.update((byte[]) data, 0, len);
92+
} else {
93+
throw new InternalException("Unknown data source to calculate sha256 hash. This should not happen, "
94+
+ "please report this issue at https://github.com/minio/minio-java/issues");
95+
}
10596

106-
return BaseEncoding.base64().encode(messageDigest.digest());
97+
return new String[]{BaseEncoding.base16().encode(sha256Digest.digest()).toLowerCase(),
98+
BaseEncoding.base64().encode(md5Digest.digest())};
10799
}
108100

109101

110102
/**
111-
* Returns MD5 hash of given input stream and it's length.
103+
* Returns MD5 hash of given data and it's length.
112104
*
113-
* @param inputStream Input stream whose type is either {@link RandomAccessFile} or {@link BufferedInputStream}.
114-
* @param len Length of Input stream.
105+
* @param data must be {@link RandomAccessFile}, {@link BufferedInputStream} or byte array.
106+
* @param len length of data to be read for hash calculation.
115107
*/
116-
public static String md5Hash(Object inputStream, int len)
117-
throws NoSuchAlgorithmException, IOException, InsufficientDataException {
108+
public static String md5Hash(Object data, int len)
109+
throws NoSuchAlgorithmException, IOException, InsufficientDataException, InternalException {
118110
MessageDigest md5Digest = MessageDigest.getInstance("MD5");
119-
updateDigests(inputStream, len, null, md5Digest);
111+
112+
if (data instanceof BufferedInputStream || data instanceof RandomAccessFile) {
113+
updateDigests(data, len, null, md5Digest);
114+
} else if (data instanceof byte[]) {
115+
md5Digest.update((byte[]) data, 0, len);
116+
} else {
117+
throw new InternalException("Unknown data source to calculate sha256 hash. This should not happen, "
118+
+ "please report this issue at https://github.com/minio/minio-java/issues");
119+
}
120+
120121
return BaseEncoding.base64().encode(md5Digest.digest());
121122
}
122123

124+
123125
/**
124126
* Updated MessageDigest with bytes read from file and stream.
125127
*/

api/src/main/java/io/minio/MinioClient.java

Lines changed: 20 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -778,7 +778,8 @@ private Request createRequest(Method method, String bucketName, String objectNam
778778
String region, Map<String,String> headerMap,
779779
Map<String,String> queryParamMap, final String contentType,
780780
final Object body, final int length)
781-
throws InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException, IOException {
781+
throws InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException, IOException,
782+
InternalException {
782783
if (bucketName == null && objectName != null) {
783784
throw new InvalidBucketNameException(NULL_STRING, "null bucket name for object '" + objectName + "'");
784785
}
@@ -896,40 +897,31 @@ public void writeTo(BufferedSink sink) throws IOException {
896897
// Fix issue #415: No need to compute sha256 if endpoint scheme is HTTPS.
897898
if (url.isHttps()) {
898899
sha256Hash = "UNSIGNED-PAYLOAD";
899-
if (body instanceof BufferedInputStream) {
900-
md5Hash = Digest.md5Hash((BufferedInputStream) body, length);
901-
} else if (body instanceof RandomAccessFile) {
902-
md5Hash = Digest.md5Hash((RandomAccessFile) body, length);
903-
} else if (body instanceof byte[]) {
904-
byte[] data = (byte[]) body;
905-
md5Hash = Digest.md5Hash(data, length);
900+
if (body != null) {
901+
md5Hash = Digest.md5Hash(body, length);
906902
}
907903
} else {
908-
// Fix issue #567: Compute SHA256 hash only.
909-
if (body == null) {
910-
sha256Hash = Digest.sha256Hash(new byte[0]);
911-
} else if (body instanceof BufferedInputStream) {
912-
sha256Hash = Digest.sha256Hash((BufferedInputStream) body, length);
913-
} else if (body instanceof RandomAccessFile) {
914-
sha256Hash = Digest.sha256Hash((RandomAccessFile) body, length);
915-
} else if (body instanceof byte[]) {
916-
sha256Hash = Digest.sha256Hash((byte[]) body, length);
904+
Object data = body;
905+
int len = length;
906+
if (data == null) {
907+
data = new byte[0];
908+
len = 0;
909+
}
910+
911+
if (method == Method.POST && queryParamMap != null && queryParamMap.containsKey("delete")) {
912+
// Fix issue #579: Treat 'Delete Multiple Objects' specially which requires MD5 hash.
913+
String[] hashes = Digest.sha256Md5Hashes(data, len);
914+
sha256Hash = hashes[0];
915+
md5Hash = hashes[1];
917916
} else {
918-
sha256Hash = Digest.sha256Hash(body.toString());
917+
// Fix issue #567: Compute SHA256 hash only.
918+
sha256Hash = Digest.sha256Hash(data, len);
919919
}
920920
}
921921
} else {
922922
// Fix issue #567: Compute MD5 hash only of anonymous access.
923923
if (body != null) {
924-
if (body instanceof BufferedInputStream) {
925-
md5Hash = Digest.md5Hash((BufferedInputStream) body, length);
926-
} else if (body instanceof RandomAccessFile) {
927-
md5Hash = Digest.md5Hash((RandomAccessFile) body, length);
928-
} else if (body instanceof byte[]) {
929-
md5Hash = Digest.md5Hash((byte[]) body, length);
930-
} else {
931-
md5Hash = Digest.md5Hash(body.toString());
932-
}
924+
md5Hash = Digest.md5Hash(body, length);
933925
}
934926
}
935927

@@ -2081,7 +2073,7 @@ public String presignedPutObject(String bucketName, String objectName, Integer e
20812073
}
20822074

20832075
String region = getRegion(bucketName);
2084-
Request request = createRequest(Method.PUT, bucketName, objectName, region, null, null, null, "", 0);
2076+
Request request = createRequest(Method.PUT, bucketName, objectName, region, null, null, null, new byte[0], 0);
20852077
HttpUrl url = Signer.presignV4(request, region, accessKey, secretKey, expires);
20862078
return url.toString();
20872079
}

0 commit comments

Comments
 (0)