I wanted to know how I can validate HTTP messages with JWS Detached. Currently, I am receiving x-sign-jws request in header which looks like below
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..UXwjHxU3tFlrzPMupG04zROiEcHFQpCg3l7J4Axr1fE
I need to verify this at my end whether the request is right or not using my secrete Key
Ex: 12345678
I am using firebase/jwt and tried below code
$hed = getallheaders();
$recievedJwt = $hed["X-Sign-Jws"];
$decoded = JWT::decode($recievedJwt, $secret_key, array('JWT','HS256'));```
but I am not getting any result.
I searched on net I found the article which mentioned below steps:
Validation HTTP message with JWS Detached:
a) Get the HTTP header "x-sign-jws",
b) Get BASE64URL HTTP body
c) Put generate string b) into the Payload section
d) Validate JWS
But I am confused with how to get Base64URL HTTP body
Any help would be greatly appreciated since there are only a few articles available on this topic.
JWS format is base64url(header).base64url(payload).base64url(signature), note the dot delimiter between 3 components.
Detached JWS still contains 3 components but the payload is removed and provided elsewhere, usually the payload is provided in the HTTP Body.
To verify detached JWS, you need to add base64url encoded payload to the detached JWS. The payload is available from your HTTP Body.
For example;
x-sign-jws = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..UXwjHxU3tFlrzPMupG04zROiEcHFQpCg3l7J4Axr1fE
//split x-sign-jws into array using delimiter .
x-sign-jws-attached = x-sign-jws-split[0] + '.' + base64Url(HTTPRequest.Body) + '.' + x-sign-jws-split[1]
Now you can verify x-sign-jws-attached as shown below;
$decoded = JWT::decode($x-sign-jws-attached, $secret_key, array('JWT','HS256'));```
Related
I am using Akka Http to handle the redirection of a signed url for a text resource stored in GCS (Google Cloud Storage). Basically I have some files in GCS that I want to deliver with my API when requested. Unfortunately, the path to this resource has a subdirectory with a = character in it, something like: https://storage.googleapis.com/path_start/more_path/format=avro/still_more_path/filename.avro.
Google storage's signUrl method percent encodes this = character as %3D in the signed url. I have verified that using this signed url allows access to the files in GCS (hitting it starts a download). The problem is when Akka Http redirects this signed url on request, it decodes it back to the equals sign. This results in a SignatureDoesNotMatch error from GCS:
<Code>SignatureDoesNotMatch</Code>
<Message>The request signature we calculated does not match the signature you provided. Check your Google secret key and signing method.</Message>
I attempt to generate the signed url and redirect it in the following code snippet:
val signedUrl = cloudStorageService.getSignedUrl(bucketId, filePath)
logger.info(s"signedUrl: $signedUrl")
redirect(signedUrl, TemporaryRedirect)
and my cloudStorageService.getSignedUrl method is defined separately here:
def getSignedUrl(bucket: String, path: String, expiration: Option[Int] = None): String = {
val maxLifetime = 60 * 60 * 24 * 7
val lifetime = expiration.getOrElse(maxLifetime)
val cappedExpiration = Math.min(lifetime, maxLifetime)
val blobInfo = BlobInfo.newBuilder(BlobId.of(bucket, path)).build
storage.signUrl(blobInfo, cappedExpiration, TimeUnit.SECONDS, Storage.SignUrlOption.withV4Signature).toString
}
The logging statement in the first snippet shows me that the signed URL is generated with the %3D, but the redirected URL has been percent decoded back to =, resulting in the SignatureDoesNotMatch error.
This behavior is standard for Akka Http. According to the documentation for Uri class, all members represent percent decoded elements. There is an option to construct the uri with a raw query string, but in my case the encoded character happens in the uri path itself, not the query.
So my question is, is there any way to get Akka Http to redirect this signed url without percent decoding the path? Or am I mistaken, and there's another way to approach this problem?
You might want to create the redirect response 'manually' instead of using the 'redirect' directive. The directive implementation is:
HttpResponse(
status = redirectionType,
headers = headers.Location(uri) :: Nil,
entity = redirectionType.htmlTemplate match {
case "" => HttpEntity.Empty
case template => HttpEntity(ContentTypes.`text/html(UTF-8)`, template format uri)
}
Instead of headers.Location(uri) you could introduce your own RawHeader to build up a Location header. You can probably leave the entity empty in your scenario.
(relevant documentation: https://doc.akka.io//docs/akka-http/current/routing-dsl/directives/route-directives/redirect.html)
Note - I am very much new to all this. Apologies if anything is unclear.
My overriding aim is to pull out MOT history data for a large batch of vehicles from the DVLA API. I understand that this can be done using Postman, which I am using (on a 64-bit Windows laptop if at all relevant).
The DVLA provide the following instructions
====================================================
Getting started
All API interfaces are implemented as restful APIs and accessed over https.
To access API you will need an API key that uniquely identifies the source of the request. DVSA will give you an API key if it approves your application.
You should keep your API key secure, as DVSA manages throttling and quotas at an API key level.
Each request must have the following mandatory fields in the header:
Accept: application/json+v6
x-api-key:
Content-type field confirms that the response type is in JSON format, and the x-api-key field serves your API key to identify the source of the request.
Technical resources
Access the API at https://beta.check-mot.service.gov.uk/
This root URL will change when the service moves from beta to live.
These 4 endpoints equate to the 4 methods of using the API:
/trade/vehicles/mot-tests?registration={registration}
‘Registration’ is the vehicle registration number.
===================================================
In order to test that this is possible, I am entering the following single request into the bar in Postman, selecting "POST" and hitting "SEND"
https://beta.check-mot.service.gov.uk/trade/vehicles/mot-tests?Content-type=application/json&x-api-key=ABCDEFGH®istration=MYREG
n.b. no inverted commas or other punctuation surrounds the actual values for ABCDEFH or MYREG
Expected result: Some sort of JSON with MOT history for this vehicle
Actual result: {"message": "Missing Authentication Token"}{"message": "Missing Authentication Token"}
I am unclear on:
- whether I should be using POST
what the +v6 after the application is necessary (other documentation leaves it out)
Why "Accept" and "Content-type" appear to be used interchangeably in the documentation
Whether the ordering of the parameters matters
Whether this can be equally tested by simply pasting the url into a browser
Thanks for any help
Reading through the Documentation found here:
https://dvsa.github.io/mot-history-api-documentation/
It mentions that those fields should be added as Headers:
Each request must have the following mandatory fields in
the header:
- Accept: application/json+v6
- x-api-key: <your api key>
There are example cURL requests on the site to help you with creating the request.
If you use Postman's Import feature within the app (found in the top right), you can add this cURL request in the Paste Raw Text tab.
curl -H "Accept: application/json+v6" -H "x-api-key: <your_api_key>" https://beta.check-mot.service.gov.uk/trade/vehicles/mot-tests\?registration=ZZ99ABC
This will give you an example request of what it should look like. From here, you will be able to add in your own API Token and send the request.
If you are using Postman, you can use the Authorization tab right under the request to give the required mandatory header fields. Select Header from Add to drop down. You can also add additional headers information using the next tab named Headers. (for example, the accept-headers).
Edit:
Authorization:
Headers Tab
Normally, you should be getting the authorization token when you register to the site in question(x-api-key here).You need to figure out the value of that token from the initial call's response headers. According to the document which Danny shared, you will be getting x-api-key from them once they approve your request.
Edit:
Alternatively, you can use import feature as Danny suggested in his answer. At the end of the day, you need to add the values as headers instead of query parameters.
For anyone using Python with the MOT history api and getting the same error message, try GET:
import requests
url = f'https://beta.check-mot.service.gov.uk/trade/vehicles/mot-tests?registration={plate_number}'
payload = {}
headers = {
'Accept': 'application/json+v6',
'x-api-key': 'your-api-key'}
response = requests.get(url, headers=headers, data=payload)
data = response.json()
model = data[0]['model'] # get the vehicle model for example
print(model)
Certain APIs in my project can only be accessed via OAuth 2.0 using an encoded format. I am able to manually submit a POST using POSTMAN as it has the x-www-form-urlencoded feature to send the request (image attached). As a workaround, I created a java function to convert my json body to an encoded URI and passed as a variable. However, I am getting a "http call failed for URL" when running it. I was wondering if Karate has its own library to convert json to Encoded URIs.
Scenario: Json to Encoded URI on POST body
* def JavaDemo = Java.type('com.ge.health.gam.service.servicerequest.testsuite.CustomJavaClass')
* def encodedURI = JavaDemo.jsonToURI()
* print encodedURI
Given url 'http://location.ver02.geicenter.com/uaa/oauth/token'
And request encodedURI
When method post
Then status 200
com.intuit.karate.exception.KarateException: http call failed after 263 milliseconds for URL: http://location.ver02.geicenter.com/uaa/oauth/token
As suggested by Peter Thomas, I found the below to be able to submit requests via Oath 2.0. In my case, I also had to install a required certificate in order to receive a valid response. you also have to take that into consideration.
Given path 'uaa', 'oauth', 'token'
And form field key = 'value'
And form field key = 'value'
And form field key = 'value'
And form field key = 'value'
When method post
Then status 200
Yes. Please refer to the documentation for form field. There is also an OAuth 2 example in the demos, look for it.
Also see: https://stackoverflow.com/a/58643689/143475
I am following this MSDN Reference (https://learn.microsoft.com/en-us/rest/api/storageservices/fileservices/put-block) to implement a rest call for Put Block.
I am coding in Java and I formed below Authorisation string and URL before signing.
PUT
364070
x-ms-blob-type:BlockBlob
x-ms-date:Fri, 20 Jan 2017 12:57:06 GMT
x-ms-version:2016-05-31
/xyz/mycontainer/imageBlock1
comp:block
sun.net.www.protocol.https.DelegateHttpsURLConnection:https://xyz.blob.core.windows.net/mycontainer/imageBlock1?comp=block&blockid=YmxvY2stMQ==
Error I am getting:
403
Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature.
I read gaurav mantras post http://gauravmantri.com/2013/02/16/uploading-large-files-in-windows-azure-blob-storage-using-shared-access-signature-html-and-javascript/. But, its not working for me.
Is there anything wrong with the string I am sending to sign or URL or
below httpConn Request Header.
The Http Header I am setting is:
httpConn.setRequestMethod("PUT");
httpConn.setRequestProperty("x-ms-blob-type", blobType);
httpConn.setRequestProperty("x-ms-date", date);
httpConn.setRequestProperty("x-ms-version", storageServiceVersion);
httpConn.setRequestProperty("Authorization", authorizationHeader);
httpConn.setRequestProperty("Content-Length",String.valueOf(blobLength) );
System.out.println(httpConn);
DataOutputStream wr = new DataOutputStream(httpConn.getOutputStream());
wr.write(bytes);
wr.flush();
wr.close();
int response = httpConn.getResponseCode();
As I known, Put Block is a operation against Block Blobs. So, we do not need to specify x-ms-blob-type header. If you specify this in your HTTP header, you need to follow the following tutorial about constructing the Canonicalized Headers String:
Retrieve all headers for the resource that begin with x-ms-, including the x-ms-date header.
Convert each HTTP header name to lowercase.
Sort the headers lexicographically by header name, in ascending order. Each header may appear only once in the string.
Finally, append a new-line character to each canonicalized header in the resulting list. Construct the CanonicalizedHeaders string by concatenating all headers in this list into a single string.
So, based on your code, your canonicalized headers string looks like:
x-ms-blob-type:BlockBlob\nx-ms-date:Fri, 20 Jan 2017 12:57:06 GMT\nx-ms-version:2016-05-31\n
Moreover, the CanonicalizedResource you built is incorrect. Based on your code, it should look as follows:
/{account-name}/{container-name}/{blob-name}\nblockid:{block-id}\ncomp:block
Note: For more details about constructing the Canonicalized Resource String, you could refer to this official document.
The StringToSign would look like this:
And the traffic captured by Fiddler as follows:
I'm computing the HMAC of the HTTP body request in this way:
payload = {"name":"myvm","os":"gentoo","resources":{"vCPU":"4","RAM":"512","Disk":"1000"},"actions":["start"]}
key = "supersecretkey"
secret = bytes(key, encoding='utf-8')
msg = json.dumps(payload, sort_keys=True)
message = bytes(msg, encoding='utf-8')
print(hmac.new(secret, message, sha1).hexdigest())
After that I encode with b64 the hexdigest and send it with curl like the docs says Authorization: paolo:$hmac_base64_encoded
The problem is that I always get a 500 error. What am I doing wrong?
Since you are using Python 3x make sure that in your custom HMACAuth you are converting to bytes (the code snippet from the official documentation is for Python 2x).