-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
(storage) Add V4 signing support #4692
Merged
Merged
Changes from 10 commits
Commits
Show all changes
42 commits
Select commit
Hold shift + click to select a range
d3b121c
Add support for V4 signing
JesseLovelace 71846ac
Merge branch 'master' of github.com:googleapis/google-cloud-java into…
JesseLovelace ab931d8
Merge branch 'master' of github.com:googleapis/google-cloud-java into…
JesseLovelace d4fb299
(storage) WIP: Add V4 signing support
JesseLovelace 4e30cdc
(storage) Add V4 signing support
JesseLovelace ddc786c
Merge branch 'master' of github.com:googleapis/google-cloud-java into…
JesseLovelace 0d7f114
Merge branch 'v4support' of github.com:googleapis/google-cloud-java i…
JesseLovelace 8e8ca08
Merge branch 'master' of github.com:googleapis/google-cloud-java into…
JesseLovelace d48e8c4
Merge branch 'master' of github.com:googleapis/google-cloud-java into…
JesseLovelace 7d23539
Merge branch 'v4support' of github.com:googleapis/google-cloud-java i…
JesseLovelace d3acaf1
Merge branch 'master' of github.com:googleapis/google-cloud-java into…
JesseLovelace 85a0dc2
Merge branch 'master' of github.com:googleapis/google-cloud-java into…
JesseLovelace c36cbc3
Merge branch 'v4support' of github.com:googleapis/google-cloud-java i…
JesseLovelace 15737c5
Merge branch 'v4support' of github.com:googleapis/google-cloud-java i…
JesseLovelace 69d4a17
Merge branch 'v4support' of github.com:googleapis/google-cloud-java i…
JesseLovelace 964af50
Merge branch 'v4support' of github.com:googleapis/google-cloud-java i…
JesseLovelace 9986c97
Merge branch 'master' of github.com:googleapis/google-cloud-java into…
JesseLovelace 24bf576
Merge branch 'v4support' of github.com:googleapis/google-cloud-java i…
JesseLovelace 7d18fde
Merge branch 'v4support' of github.com:googleapis/google-cloud-java i…
JesseLovelace c5fd070
Merge branch 'v4support' of github.com:googleapis/google-cloud-java i…
JesseLovelace 98c31b9
Merge branch 'v4support' of github.com:googleapis/google-cloud-java i…
JesseLovelace e0d0f32
Merge branch 'master' of github.com:googleapis/google-cloud-java into…
JesseLovelace 9709087
Merge branch 'v4support' of github.com:googleapis/google-cloud-java i…
JesseLovelace ed25ac4
Add V4 samples (#4753)
frankyn 63c42ad
Merge branch 'v4support' of github.com:googleapis/google-cloud-java i…
JesseLovelace ed7e8c4
Merge branch 'master' of github.com:googleapis/google-cloud-java into…
JesseLovelace 836d7d5
Merge branch 'v4support' of github.com:googleapis/google-cloud-java i…
JesseLovelace 022d5ba
Merge branch 'v4support' of github.com:googleapis/google-cloud-java i…
JesseLovelace 5534b2f
Merge branch 'v4support' of github.com:googleapis/google-cloud-java i…
JesseLovelace c104811
storage: fix v4 samples (#4754)
frankyn 9fcb46f
Merge branch 'master' of github.com:googleapis/google-cloud-java into…
JesseLovelace 6d35976
Merge branch 'v4support' of github.com:googleapis/google-cloud-java i…
JesseLovelace 54e722c
Merge branch 'v4support' of github.com:googleapis/google-cloud-java i…
JesseLovelace b0a992e
Merge branch 'master' of github.com:googleapis/google-cloud-java into…
JesseLovelace 7a836e4
Merge branch 'v4support' of github.com:googleapis/google-cloud-java i…
JesseLovelace 0e152ec
Merge branch 'v4support' of github.com:googleapis/google-cloud-java i…
JesseLovelace 6785005
Merge branch 'v4support' of github.com:googleapis/google-cloud-java i…
JesseLovelace 8958a7d
Merge branch 'v4support' of github.com:googleapis/google-cloud-java i…
JesseLovelace b0d677d
Merge branch 'master' of github.com:googleapis/google-cloud-java into…
JesseLovelace d94d805
Merge branch 'v4support' of github.com:googleapis/google-cloud-java i…
JesseLovelace b249848
Merge branch 'v4support' of github.com:googleapis/google-cloud-java i…
JesseLovelace aa837fd
Merge branch 'v4support' of github.com:googleapis/google-cloud-java i…
JesseLovelace File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,7 +18,13 @@ | |
|
||
import static com.google.common.base.Preconditions.checkArgument; | ||
|
||
import com.google.common.collect.ImmutableMap; | ||
import com.google.common.hash.Hashing; | ||
import com.google.common.net.UrlEscapers; | ||
import java.net.URI; | ||
import java.nio.charset.StandardCharsets; | ||
import java.text.SimpleDateFormat; | ||
import java.util.Date; | ||
import java.util.Map; | ||
|
||
/** | ||
|
@@ -31,30 +37,64 @@ | |
public class SignatureInfo { | ||
|
||
public static final char COMPONENT_SEPARATOR = '\n'; | ||
public static final String GOOG4_RSA_SHA256 = "GOOG4-RSA-SHA256"; | ||
public static final String SCOPE = "/auto/storage/goog4_request"; | ||
|
||
private final HttpMethod httpVerb; | ||
private final String contentMd5; | ||
private final String contentType; | ||
private final long expiration; | ||
private final Map<String, String> canonicalizedExtensionHeaders; | ||
private final URI canonicalizedResource; | ||
private final Storage.SignUrlOption.SignatureVersion signatureVersion; | ||
private final String accountEmail; | ||
private final long timestamp; | ||
|
||
private final String yearMonthDay; | ||
private final String exactDate; | ||
|
||
private SignatureInfo(Builder builder) { | ||
this.httpVerb = builder.httpVerb; | ||
this.contentMd5 = builder.contentMd5; | ||
this.contentType = builder.contentType; | ||
this.expiration = builder.expiration; | ||
this.canonicalizedExtensionHeaders = builder.canonicalizedExtensionHeaders; | ||
this.canonicalizedResource = builder.canonicalizedResource; | ||
this.signatureVersion = builder.signatureVersion; | ||
this.accountEmail = builder.accountEmail; | ||
this.timestamp = builder.timestamp; | ||
|
||
if (Storage.SignUrlOption.SignatureVersion.V4.equals(signatureVersion) | ||
&& !builder.canonicalizedExtensionHeaders.containsKey("host")) { | ||
canonicalizedExtensionHeaders = | ||
new ImmutableMap.Builder<String, String>() | ||
.putAll(builder.canonicalizedExtensionHeaders) | ||
.put("host", "storage.googleapis.com") | ||
.build(); | ||
} else { | ||
canonicalizedExtensionHeaders = builder.canonicalizedExtensionHeaders; | ||
} | ||
|
||
Date date = new Date(timestamp); | ||
|
||
yearMonthDay = new SimpleDateFormat("yyyyMMdd").format(date); | ||
exactDate = new SimpleDateFormat("yyyyMMdd'T'hhmmss'Z'").format(date); | ||
} | ||
|
||
/** | ||
* Constructs payload to be signed. | ||
* | ||
* @return paylod to sign | ||
* @return payload to sign | ||
* @see <a href="https://cloud.google.com/storage/docs/access-control#Signed-URLs">Signed URLs</a> | ||
*/ | ||
public String constructUnsignedPayload() { | ||
// TODO reverse order when V4 becomes default | ||
if (Storage.SignUrlOption.SignatureVersion.V4.equals(signatureVersion)) { | ||
return constructV4UnsignedPayload(); | ||
} | ||
return constructV2UnsignedPayload(); | ||
} | ||
|
||
private String constructV2UnsignedPayload() { | ||
StringBuilder payload = new StringBuilder(); | ||
|
||
payload.append(httpVerb.name()).append(COMPONENT_SEPARATOR); | ||
|
@@ -80,6 +120,66 @@ public String constructUnsignedPayload() { | |
return payload.toString(); | ||
} | ||
|
||
private String constructV4UnsignedPayload() { | ||
StringBuilder payload = new StringBuilder(); | ||
|
||
payload.append(GOOG4_RSA_SHA256).append(COMPONENT_SEPARATOR); | ||
|
||
payload.append(exactDate).append(COMPONENT_SEPARATOR); | ||
|
||
payload.append(yearMonthDay).append(SCOPE).append(COMPONENT_SEPARATOR); | ||
|
||
payload.append(constructV4CanonicalRequestHash()); | ||
|
||
return payload.toString(); | ||
} | ||
|
||
private String constructV4CanonicalRequestHash() { | ||
StringBuilder canonicalRequest = new StringBuilder(); | ||
|
||
CanonicalExtensionHeadersSerializer serializer = new CanonicalExtensionHeadersSerializer(); | ||
|
||
canonicalRequest.append(httpVerb.name()).append(COMPONENT_SEPARATOR); | ||
|
||
canonicalRequest.append(canonicalizedResource).append(COMPONENT_SEPARATOR); | ||
|
||
canonicalRequest.append(constructV4QueryString()).append(COMPONENT_SEPARATOR); | ||
|
||
canonicalRequest | ||
.append(serializer.serialize(canonicalizedExtensionHeaders)) | ||
.append(COMPONENT_SEPARATOR); | ||
|
||
canonicalRequest | ||
.append(serializer.serializeHeaderNames(canonicalizedExtensionHeaders)) | ||
.append(COMPONENT_SEPARATOR); | ||
|
||
canonicalRequest.append("UNSIGNED-PAYLOAD"); | ||
|
||
return Hashing.sha256() | ||
.hashString(canonicalRequest.toString(), StandardCharsets.UTF_8) | ||
.toString(); | ||
} | ||
|
||
public String constructV4QueryString() { | ||
StringBuilder signedHeaders = | ||
new CanonicalExtensionHeadersSerializer() | ||
.serializeHeaderNames(canonicalizedExtensionHeaders); | ||
|
||
StringBuilder queryString = new StringBuilder(); | ||
queryString.append("X-Goog-Algorithm=").append(GOOG4_RSA_SHA256).append("&"); | ||
queryString.append( | ||
"X-Goog-Credential=" | ||
+ UrlEscapers.urlFormParameterEscaper() | ||
.escape(accountEmail + "/" + yearMonthDay + SCOPE) | ||
+ "&"); | ||
queryString.append("X-Goog-Date=" + exactDate + "&"); | ||
queryString.append("X-Goog-Expires=" + expiration + "&"); | ||
queryString.append( | ||
"X-Goog-SignedHeaders=" | ||
+ UrlEscapers.urlFormParameterEscaper().escape(signedHeaders.toString())); | ||
return queryString.toString(); | ||
} | ||
|
||
public HttpMethod getHttpVerb() { | ||
return httpVerb; | ||
} | ||
|
@@ -104,6 +204,18 @@ public URI getCanonicalizedResource() { | |
return canonicalizedResource; | ||
} | ||
|
||
public Storage.SignUrlOption.SignatureVersion getSignatureVersion() { | ||
return signatureVersion; | ||
} | ||
|
||
public long getTimestamp() { | ||
return timestamp; | ||
} | ||
|
||
public String getAccountEmail() { | ||
return accountEmail; | ||
} | ||
|
||
public static final class Builder { | ||
|
||
private final HttpMethod httpVerb; | ||
|
@@ -112,6 +224,9 @@ public static final class Builder { | |
private final long expiration; | ||
private Map<String, String> canonicalizedExtensionHeaders; | ||
private final URI canonicalizedResource; | ||
private Storage.SignUrlOption.SignatureVersion signatureVersion; | ||
private String accountEmail; | ||
private long timestamp; | ||
|
||
/** | ||
* Constructs builder. | ||
|
@@ -134,6 +249,9 @@ public Builder(SignatureInfo signatureInfo) { | |
this.expiration = signatureInfo.expiration; | ||
this.canonicalizedExtensionHeaders = signatureInfo.canonicalizedExtensionHeaders; | ||
this.canonicalizedResource = signatureInfo.canonicalizedResource; | ||
this.signatureVersion = signatureInfo.signatureVersion; | ||
this.accountEmail = signatureInfo.accountEmail; | ||
this.timestamp = signatureInfo.timestamp; | ||
} | ||
|
||
public Builder setContentMd5(String contentMd5) { | ||
|
@@ -155,12 +273,37 @@ public Builder setCanonicalizedExtensionHeaders( | |
return this; | ||
} | ||
|
||
public Builder setSignatureVersion(Storage.SignUrlOption.SignatureVersion signatureVersion) { | ||
this.signatureVersion = signatureVersion; | ||
|
||
return this; | ||
} | ||
|
||
public Builder setAccountEmail(String accountEmail) { | ||
this.accountEmail = accountEmail; | ||
|
||
return this; | ||
} | ||
|
||
public Builder setTimestamp(long timestamp) { | ||
this.timestamp = timestamp; | ||
|
||
return this; | ||
} | ||
|
||
/** Creates an {@code SignatureInfo} object from this builder. */ | ||
public SignatureInfo build() { | ||
checkArgument(httpVerb != null, "Required HTTP method"); | ||
checkArgument(canonicalizedResource != null, "Required canonicalized resource"); | ||
checkArgument(expiration >= 0, "Expiration must be greater than or equal to zero"); | ||
|
||
if (Storage.SignUrlOption.SignatureVersion.V4.equals(signatureVersion)) { | ||
checkArgument(accountEmail != null, "Account email required to use V4 signing"); | ||
checkArgument(timestamp > 0, "Timestamp required to use V4 signing"); | ||
checkArgument( | ||
expiration <= 604800000, "Expiration can't be longer than 7 days to use V4 signing"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 7 days is inclusive of maximum (604800). This is longer. |
||
} | ||
|
||
return new SignatureInfo(this); | ||
} | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please remove the new lines between each append.