Skip to content
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

webservice: new "urlsign" parameter #637

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -269,14 +269,10 @@ public static String url(String endpoint, String path, String username, String s
}
url = url.substring(0, url.length() - 1);

// add parameter "sign"
// add parameter "urlsign"
try {
String hash = calculateRFC2104HMAC(url, secret);
try {
url += "&sign"
+ "=" + URLEncoder.encode(hash, "UTF-8"); }
catch (UnsupportedEncodingException e) {
throw new Pipeline2Exception("Unsupported encoding: UTF-8", e); }
url += "&urlsign=" + hash;
} catch (SignatureException e) {
throw new Pipeline2Exception("Could not sign request.");
}
Expand Down Expand Up @@ -313,7 +309,7 @@ private static String calculateRFC2104HMAC(String data, String secret) throws ja
byte[] rawHmac = mac.doFinal(data.getBytes());

// base64-encode the hmac
result = Base64.getEncoder().encode(rawHmac);
result = Base64.getUrlEncoder().withoutPadding().encode(rawHmac);

} catch (Exception e) {
throw new SignatureException("Failed to generate HMAC : " + e.getMessage());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,19 +31,19 @@ public Authenticator(RequestLog requestLog) {
this.requestLog = requestLog;
}

public boolean authenticate(Client client, String hash, String timestamp, String nonce, String URI, long maxRequestTime) {
public boolean authenticate(Client client, String hash, String urlhash, String timestamp, String nonce, String URI, long maxRequestTime) {
// rules for hashing: use the whole URL string, minus the hash part (&sign=<some value>)
// important! put the sign param last so we can easily strip it out

int idx = URI.indexOf("&sign=", 0);

if(urlhash != null) {
idx = URI.indexOf("&urlsign=", 0);
}
if (idx > 1) {
String hashuri = URI.substring(0, idx);
String clientSecret = client.getSecret();
String serverHash = "";
try {
serverHash = calculateRFC2104HMAC(hashuri, clientSecret);

SimpleDateFormat UTC_FORMATTER = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
UTC_FORMATTER.setTimeZone(TimeZone.getTimeZone("UTC"));

Expand All @@ -56,6 +56,12 @@ public boolean authenticate(Client client, String hash, String timestamp, String
e.printStackTrace();
return false;
}

serverHash = calculateRFC2104HMAC(hashuri, clientSecret, false);
if(urlhash != null) {
serverHash = calculateRFC2104HMAC(hashuri, clientSecret, true);
hash = urlhash;
}
if(!hash.equals(serverHash)) {
logger.error("Hash values do not match");
return false;
Expand Down Expand Up @@ -97,7 +103,7 @@ public static URI createUriWithCredentials(String uri, Client client) {
String hash;
URI newUri = null;
try {
hash = calculateRFC2104HMAC(uristring, client.getSecret());
hash = calculateRFC2104HMAC(uristring, client.getSecret(), false);
String authUri = uristring + "&sign=" + hash;
newUri = new URI(authUri);
} catch (SignatureException e) {
Expand Down Expand Up @@ -142,7 +148,7 @@ private boolean checkValidNonce(Client client, String nonce, String timestamp) {
* @throws
* java.security.SignatureException when signature generation fails
*/
private static String calculateRFC2104HMAC(String data, String secret) throws java.security.SignatureException {
private static String calculateRFC2104HMAC(String data, String secret, boolean urlEncoded) throws java.security.SignatureException {
String result;
try {
// get an hmac_sha1 key from the raw key bytes
Expand All @@ -157,6 +163,9 @@ private static String calculateRFC2104HMAC(String data, String secret) throws ja

// base64-encode the hmac
result = Base64.getEncoder().encodeToString(rawHmac);
if (urlEncoded) {
result = Base64.getUrlEncoder().withoutPadding().encodeToString(rawHmac);
}
} catch (Exception e) {
throw new SignatureException("Failed to generate HMAC : " + e.getMessage());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,15 @@ private boolean authenticate() {
}
this.client=optionalClient.get();
RequestLog requestLog = webservice().getStorage().getRequestLog();
return new Authenticator(requestLog).authenticate(this.client, getQuery().getFirstValue("sign"),
getQuery().getFirstValue("time"), getQuery().getFirstValue("nonce"), getReference().toString(),
maxRequestTime);
return new Authenticator(requestLog).authenticate(
this.client,
getQuery().getFirstValue("sign"),
getQuery().getFirstValue("urlsign"),
getQuery().getFirstValue("time"),
getQuery().getFirstValue("nonce"),
getReference().toString(),
maxRequestTime
);
}

public boolean isAuthenticated() {
Expand Down
22 changes: 22 additions & 0 deletions website/src/_wiki/WebServiceAuthentication.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,25 @@ This is the URI to submit to the Web Service. If `myclient` has a
permission level to do what they want, then their request will be
accepted. Currently, only admin requests require a client to have a
special permission level (`ADMIN` instead of the default `CLIENTAPP`).

### Signing using the `urlsign` parameter instead

Another approach that is similar but might be simpler to handle is using the `urlsign` parameter.
To do this, you follow the approach above but generate a Base64Url encoded string instead and add that at the end of the URL.

For example

`http://example.org/ws/scripts?authid=myclient&time=2012-02-09T02:23:40Z&nonce=533473712461604713238933268313&urlsign=gq_lpIuWqEDjhWviAjyccNTzdZk`

This format is supported by libraries or built-in for most languages. To describe it briefly, you need to generate a standard Base64, replace some characters and remove the padding.

Let's take the example of `gq/lpIuWqEDjhWviAjyccNT+zdZk==`

The replacements we need to do is
```
replace + with -
replace / with _
remove padding of =
```

The result we get then is `gq_lpIuWqEDjhWviAjyccNT-zdZk`