Digital signatures provide means of authentication, integrity and non-repudiation. OAuth 1.0a had digital signatures in it which are used during the “OAuth Dance” (an unofficial term used by Google developers to describe the set of steps performed in order to do the full OAuth authentication and authorization process to receive and access token) and as well as when going to access the protected resources with an access token. In OAuth 2.0 digital signatures were removed from the “OAuth Dance” citing the difficulty for the clients to do signing and primarily relied on transport layer security such as SSL over HTTP. OAuth 2.0 supports an extensible list of token profiles. The widely used profile is the bearer token profile. This profile does not involve any signatures with it. Another popular profile is the MAC token profile. There may be certain situations where users might prefer to have some kind of signatures and verification of them to have non-repudiation during the access token validation process. The MAC token profile seems the ideal candidate for this. The difference between OAuth 1.0a and OAuth 2.0 with MAC access tokens is that the MAC access token profile does not have to have signatures during the “OAuth Dance”.
The WSO2 API Manager currently only supports OAuth 2.0 bearer token profile out of the box. It still does not support MAC token profile. But in case if you would want to have non-repudiation during the access of protected resources we have a work around that we can do. This work around is possible by adding a new ‘API Handler’ before the default ‘Authentication Hander’. It mimics the MAC token profile but is not an exact implementation of the profile. As of now this could be useful because we need not change any WSO2 API Manager code and you get signatures and signature verification during resource access.
This new handler is designed to work together with the default APIAuthenticationHandler which expects a bearer token by default. How it works is by sitting in front of the default handler, verifying the signatures in the request and converting the Authorization header to what the default handler expects and hands over the request to the default handler. This signature is calculated by signing the normalized request string by the consumer secret (which is where this implementation defers from the MAC token profile because we are not signing with a MAC key that was received in the token response step). This ensures that even if the bearer token is compromised, an illegitimate user is denied access to APIs, because he/she is unable to calculate the correct signature without knowing the shared consumer secret.
Signature verification can be implemented as an API handler similar to the ‘APIAuthenticationHandler’ or ‘APIUsageHandler’. After the introduction of this feature, the access token that was provided earlier can now function as the Mac Identifier. The consumer secret is used as the Mac key, which is a shared secret between the consumer and the provider used to sign the normalized request string. Timestamps and nonce are added to prevent replay attacks.
Engaging the Handler to an API
Follow the steps below to engage the handler to an API.
For demonstration purposes, we use selected WSO2 API Manager sample and .jar files in the steps below. Similar steps apply to other user-specific samples as well.
1. A compiled binary of a sample signature verification handler can be found here. The source code for this project can be found here.
Open the .jar file and search for the ‘verifier.properties’ file. It contains 4 properties as follows:
allowed.time.delay – The allowed time difference between the timestamp sent and the current time.
timediff.map.max.size – Maximum size of the map that should be maintained to keep timestamps, in order to prevent replay attacks.
nonce.map.max.size – Maximum size of the map that should be maintained to keep nonce values, in order to prevent replay attacks.
hash.algorithm – Hashing algorithm supported by SunJCE. For example, “HMacSHA1”, “HMacSHA256”, etc.
2. Copy the provided jar file to <AM_HOME>/repository/components/dropins folder where <AM_HOME> is the root of the WSO2 API Manager distribution. This is the location where any custom libraries are added.
3. Start WSO2 API Manager and log in to its Management Console.
4. Build the API Manager ‘YouTube’ sample using the instructions given up to section ‘Invoking the API’. Instructions can be found here
5. You can engage the developed handler to the API through the Management Console. Log in to the console and select ‘Main’ > ‘Service Bus’ > ‘Source View’.
6. In the ESB configuration that opens, as the first handler in the YouTube API, add the following line above ‘APIAuthenticationHandler’.
The class ‘org.wso2.carbon.apimgt.gateway.verifier.SignatureVerificationHandler’ is the handler that we have implemented by extending the ‘org.apache.synapse.rest.AbstractHandler’ class and packed into the .jar file.
Invoking the API
Now that you have engaged the developed handler to the API, let’s see how to invoke this API using a REST client such as cURL. Note that none of the steps until invoking the API in the ‘YouTube’ sample have changed due to engaging the handler. The change only occurs in the way the API is invoked. Note the following differences in the new REST calls.
Previous cURL request (As seen in API Manager 1.3.0 documentation):
curl -H "Authorization :Bearer 8f74ac7a87caee6967b75dcda51b8edc" http://localhost:8280/youtube/1.0.0/most_viewed
Previous authorization header:
Authorization :Bearer 8f74ac7a87caee6967b75dcda51b8edc
New cURL request:
curl -H "Authorization :MAC id=\"8f74ac7a87caee6967b75dcda51b8edc\",ts=\"1347023000\",nonce=\"a1b2c3d4e5\",mac=\"5X/zg3RnRSMP1JkaMJCaqWOk/srpw4ybGwIbPRVNUYA=\""http://localhost:8280/youtube6/6.0.0/most_viewed
New authorization header:
Authorization :MAC id=\"8f74ac7a87caee6967b75dcda51b8edc\",ts=\"1347023000\",nonce=\"a1b2c3d4e5\",mac=\"5X/zg3RnRSMP1JkaMJCaqWOk/srpw4ybGwIbPRVNUYA=\"
The difference of the REST calls lies only in the Authorization header as follows:
The access token in the previous request has now become the ‘id’. ‘ts’ is the timestamp. It can be any positive integer value the client sends (ideally the number of seconds elapsed from 1/1/1970 0:0:0). However, a timestamp verification is done from the second request onwards from a particular ‘id’. The ‘nonce’ also is a string that is chosen by the client. It needs to be unique to the combination of timestamp and Mac identifier.
Considerations When Executing the new cURL Request
When invoking a cURL request with the new authentication header, every request from the same Mac identifier (which is the access token in the earlier method) should have a different timestamp value whose difference in value from the previous request is greater than the actual time elapsed between the previous request and this one. The Mac value is taken by creating the ‘Normalized Request’ string as shown in the ‘HMacGenerator’ console output, and hashing it using the ‘SunJCE’ library with the algorithm specified in ‘verifier.properties’ file. Then, we use the ‘ConsumerSecret’ as key and transcode it using Base64. Request String is equal to the full URL that comes in, minus the transport protocol (http,https) and the hostname:port pair. In other words, it is the string from the context onwards. URL encoding is done only for the query parameter values using the “UTF-8” encoding scheme. A Java client to generate HMac signature can be found in here. All required parameters including the algorithm to be used can be given as console inputs. Any algorithm that is supported by the SunJCE cryptographic provider such as “HMacSHA1”, “HMacSHA256”, etc. can be given. Before you run the command, ensure that the jar file in the ‘lib’ folder of the ‘HMacGenerator.jar’ is added to the Java classpath.