Skip to content

Commit

Permalink
fix: calculate/add MD5 hash for multiple object delete.
Browse files Browse the repository at this point in the history
Fixes minio#579
  • Loading branch information
balamurugana committed May 22, 2017
1 parent 73ca5e5 commit 8812c1c
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 81 deletions.
106 changes: 54 additions & 52 deletions api/src/main/java/io/minio/Digest.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import com.google.common.io.BaseEncoding;

import io.minio.errors.InsufficientDataException;
import io.minio.errors.InternalException;


/**
Expand All @@ -42,84 +43,85 @@ private Digest() {}
* Returns SHA-256 hash of given string.
*/
public static String sha256Hash(String string) throws NoSuchAlgorithmException {
return sha256Hash(string.getBytes(StandardCharsets.UTF_8));
}


/**
* Returns SHA-256 hash of given byte array.
*/
public static String sha256Hash(byte[] data) throws NoSuchAlgorithmException {
return sha256Hash(data, data.length);
}


/**
* Returns SHA-256 hash string of given byte array and it's length.
*/
public static String sha256Hash(byte[] data, int length) throws NoSuchAlgorithmException {
MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");

messageDigest.update(data, 0, length);

return BaseEncoding.base16().encode(messageDigest.digest()).toLowerCase();
byte[] data = string.getBytes(StandardCharsets.UTF_8);
MessageDigest sha256Digest = MessageDigest.getInstance("SHA-256");
sha256Digest.update((byte[]) data, 0, data.length);
return BaseEncoding.base16().encode(sha256Digest.digest()).toLowerCase();
}


/**
* Returns SHA-256 of given input stream and it's length.
* Returns SHA-256 hash of given data and it's length.
*
* @param inputStream Input stream whose type is either {@link RandomAccessFile} or {@link BufferedInputStream}.
* @param len Length of Input stream.
* @param data must be {@link RandomAccessFile}, {@link BufferedInputStream} or byte array.
* @param len length of data to be read for hash calculation.
*/
public static String sha256Hash(Object inputStream, int len)
throws NoSuchAlgorithmException, IOException, InsufficientDataException {
public static String sha256Hash(Object data, int len)
throws NoSuchAlgorithmException, IOException, InsufficientDataException, InternalException {
MessageDigest sha256Digest = MessageDigest.getInstance("SHA-256");
updateDigests(inputStream, len, sha256Digest, null);
return BaseEncoding.base16().encode(sha256Digest.digest()).toLowerCase();
}

/**
* Returns MD5 hash of given string.
*/
public static String md5Hash(String string) throws NoSuchAlgorithmException {
return md5Hash(string.getBytes(StandardCharsets.UTF_8));
}

if (data instanceof BufferedInputStream || data instanceof RandomAccessFile) {
updateDigests(data, len, sha256Digest, null);
} else if (data instanceof byte[]) {
sha256Digest.update((byte[]) data, 0, len);
} else {
throw new InternalException("unknown data to calculate SHA256 hash. This should not happend. "
+ "Please report this issue at https://github.com/minio/minio-java/issues");
}

/**
* Returns MD5 hash of given byte array.
*/
public static String md5Hash(byte[] data) throws NoSuchAlgorithmException {
return md5Hash(data, data.length);
return BaseEncoding.base16().encode(sha256Digest.digest()).toLowerCase();
}


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

messageDigest.update(data, 0, length);
if (data instanceof BufferedInputStream || data instanceof RandomAccessFile) {
updateDigests(data, len, sha256Digest, md5Digest);
} else if (data instanceof byte[]) {
sha256Digest.update((byte[]) data, 0, len);
md5Digest.update((byte[]) data, 0, len);
} else {
throw new InternalException("unknown data to calculate SHA256 hash. This should not happend. "
+ "Please report this issue at https://github.com/minio/minio-java/issues");
}

return BaseEncoding.base64().encode(messageDigest.digest());
return new String[]{BaseEncoding.base16().encode(sha256Digest.digest()).toLowerCase(),
BaseEncoding.base64().encode(md5Digest.digest())};
}


/**
* Returns MD5 hash of given input stream and it's length.
* Returns MD5 hash of given data and it's length.
*
* @param inputStream Input stream whose type is either {@link RandomAccessFile} or {@link BufferedInputStream}.
* @param len Length of Input stream.
* @param data must be {@link RandomAccessFile}, {@link BufferedInputStream} or byte array.
* @param len length of data to be read for hash calculation.
*/
public static String md5Hash(Object inputStream, int len)
throws NoSuchAlgorithmException, IOException, InsufficientDataException {
public static String md5Hash(Object data, int len)
throws NoSuchAlgorithmException, IOException, InsufficientDataException, InternalException {
MessageDigest md5Digest = MessageDigest.getInstance("MD5");
updateDigests(inputStream, len, null, md5Digest);

if (data instanceof BufferedInputStream || data instanceof RandomAccessFile) {
updateDigests(data, len, null, md5Digest);
} else if (data instanceof byte[]) {
md5Digest.update((byte[]) data, 0, len);
} else {
throw new InternalException("unknown data to calculate SHA256 hash. This should not happend. "
+ "Please report this issue at https://github.com/minio/minio-java/issues");
}

return BaseEncoding.base64().encode(md5Digest.digest());
}


/**
* Updated MessageDigest with bytes read from file and stream.
*/
Expand Down
48 changes: 19 additions & 29 deletions api/src/main/java/io/minio/MinioClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -778,7 +778,8 @@ private Request createRequest(Method method, String bucketName, String objectNam
String region, Map<String,String> headerMap,
Map<String,String> queryParamMap, final String contentType,
final Object body, final int length)
throws InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException, IOException {
throws InvalidBucketNameException, NoSuchAlgorithmException, InsufficientDataException, IOException,
InternalException {
if (bucketName == null && objectName != null) {
throw new InvalidBucketNameException(NULL_STRING, "null bucket name for object '" + objectName + "'");
}
Expand Down Expand Up @@ -896,40 +897,29 @@ public void writeTo(BufferedSink sink) throws IOException {
// Fix issue #415: No need to compute sha256 if endpoint scheme is HTTPS.
if (url.isHttps()) {
sha256Hash = "UNSIGNED-PAYLOAD";
if (body instanceof BufferedInputStream) {
md5Hash = Digest.md5Hash((BufferedInputStream) body, length);
} else if (body instanceof RandomAccessFile) {
md5Hash = Digest.md5Hash((RandomAccessFile) body, length);
} else if (body instanceof byte[]) {
byte[] data = (byte[]) body;
md5Hash = Digest.md5Hash(data, length);
}
md5Hash = Digest.md5Hash(body, length);
} else {
// Fix issue #567: Compute SHA256 hash only.
if (body == null) {
sha256Hash = Digest.sha256Hash(new byte[0]);
} else if (body instanceof BufferedInputStream) {
sha256Hash = Digest.sha256Hash((BufferedInputStream) body, length);
} else if (body instanceof RandomAccessFile) {
sha256Hash = Digest.sha256Hash((RandomAccessFile) body, length);
} else if (body instanceof byte[]) {
sha256Hash = Digest.sha256Hash((byte[]) body, length);
Object data = body;
int len = length;
if (data == null) {
data = new byte[0];
len = 0;
}

if (method == Method.POST && queryParamMap != null && queryParamMap.containsKey("delete")) {
// Fix issue #579: Treat 'Delete Multiple Objects' specially which requires MD5 hash.
String[] hashes = Digest.sha256Md5Hashes(data, len);
sha256Hash = hashes[0];
md5Hash = hashes[1];
} else {
sha256Hash = Digest.sha256Hash(body.toString());
// Fix issue #567: Compute SHA256 hash only.
sha256Hash = Digest.sha256Hash(data, len);
}
}
} else {
// Fix issue #567: Compute MD5 hash only of anonymous access.
if (body != null) {
if (body instanceof BufferedInputStream) {
md5Hash = Digest.md5Hash((BufferedInputStream) body, length);
} else if (body instanceof RandomAccessFile) {
md5Hash = Digest.md5Hash((RandomAccessFile) body, length);
} else if (body instanceof byte[]) {
md5Hash = Digest.md5Hash((byte[]) body, length);
} else {
md5Hash = Digest.md5Hash(body.toString());
}
md5Hash = Digest.md5Hash(body, length);
}
}

Expand Down Expand Up @@ -2081,7 +2071,7 @@ public String presignedPutObject(String bucketName, String objectName, Integer e
}

String region = getRegion(bucketName);
Request request = createRequest(Method.PUT, bucketName, objectName, region, null, null, null, "", 0);
Request request = createRequest(Method.PUT, bucketName, objectName, region, null, null, null, new byte[0], 0);
HttpUrl url = Signer.presignV4(request, region, accessKey, secretKey, expires);
return url.toString();
}
Expand Down

0 comments on commit 8812c1c

Please sign in to comment.