Axios Encrypted POST parameters - rest

I want to use the API from bitgrail (docs: https://bitgrail.com/api-documentation). And request the amount of balances. To do that you have to set a SIGNATURE which includes the encrypted post parameters using HMAC-SHA512 with ur API-Secret.
So you have to sent this data:
Header:
KEY - Public API key
SIGNATURE - encrypted POST parameters with HMAC-SHA512 alghoritm using your secret API key
Data:
nonce - Integer number, always greater then nonce of previous call.
But everytime I try to send the request I get an 'Authentication failed'-Error from Bitgrail.
The params are set like so:
params = {}
params.nonce = n();
and then encrypted like this:
let hmac = crypto.createHmac('sha512', 'MYSECRET');
let digest = hmac.update(params.toString()).digest('hex');
let signature = new Buffer(digest).toString('base64');
Maybe the 'params.toString()' is not working. Do I have to set the params variable as an array?

I figured it our by myself by using const { URLSearchParams } = require('url');
and deleting this line: let signature = new Buffer(digest).toString('base64'); and just using the digest as signature.

Related

Is there a native method in nestjs to decode JWT?

In nestjs I create JWT (tokens) by creating a payload object and signing it. Something like this:
const jwtPayload: JwtPayload =
{
iss: issuer,
sub: info,
aud: audience,
// exp: - populated by fn: this.jwtService.sign(payload),
// iat: - populated by fn: this.jwtService.sign(payload),
jti: 'XXXX1234'
}
const signedJwtAccessToken: string = this.jwtService.sign(jwtPayload);
Nest encodes the jwtPayload into a string.
For cleanup work I would like to know when exactly the JWT expires. This is automatically encoded into the 'signedJwtAccessToken' - property .exp - by the .sign() function.
To access it right after the signing, it needs to be decoded.
What would be the simplest way to decode the signedJwtAccessToken in the same method right after it has been signed ???
Note:
When the JWT comes back from client, nestjs decodes it when accessing the fn: validate(), but I want to decode right after signing it - before sending the response to client, something like:
// signing - encoding
const signedJwtAccessToken: string = this.jwtService.sign(jwtPayload);
// decoding
const decodedJwtAccessToken: string = decodeJwt(signedJwtAccessToken);
// parsing back to an object
const updatedJwtPayload: JwtPayload = JSON.parse(decodedJwtAccessToken);
// reading property of .exp
const expires = updatedJwtPayload.exp;
Just decode it as a normal jwt token. If you use nestjs-jwt package, just call decode function:
const decodedJwtAccessToken: JwtPayload = this.jwtService.decode(signedJwtAccessToken);
const expires = decodedJwtAccessToken.exp;
Or just decode the token as a base64 string
const base64Payload = signedJwtAccessToken.split('.')[1];
const payloadBuffer = Buffer.from(base64Payload, 'base64');
const updatedJwtPayload: JwtPayload = JSON.parse(payloadBuffer.toString()) as JwtPayload;
const expires = updatedJwtPayload.exp;

Decrypt JWT Token in C#

Hello I'm trying to see if they're any JWT token library similar to JOSE-JWT that also works on a Linux machine during runtime.
This is what I currently have in code that decrypts a token and grabs what I need.
private IamTokenDecrypted DecryptToken(string idToken)
{
byte[] key = WebEncoders.Base64UrlDecode(_ssoEncryptionKeyInfo.JsonWebKey.K);
string json = Jose.JWT.Decode(idToken, key, JweAlgorithm.DIR, JweEncryption.A256GCM);
var result = Jose.JWT.Payload(json);
var iamTokenDecrypted = JsonConvert.DeserializeObject<IamTokenDecrypted>(result);
return iamTokenDecrypted;
}
I have a security key and and as you see it has some encryption in it

Invalid nonce and timestamp Garmin access token API response using oauthSwift

I am trying to connect Garmin with my app. I have authToken and verifier but when I call https://connectapi.garmin.com/oauth-service/oauth/access_token API I get "invalid nonce and timestamp response.
This is code below to make my signature header
var parameter = OAuthSwift.Parameters()
parameter = ["oauth_verifier": "HGJNBBHJ", "oauth_token": "d9209df0-044e-4872-817e-51caa0b38edb"]
let oauthswift = OAuth1Swift(
consumerKey: "********",
consumerSecret: "********"
)
this is the code to make header
let head = oauthswift.client.credential.makeHeaders(URL(string: "https://connectapi.garmin.com/oauth-service/oauth/access_token")!, method: .POST, parameters: parameter)
this is the header I got.
["Authorization": "OAuth oauth_consumer_key=\"********\", oauth_nonce=\"01C7E0DF\", oauth_signature=\"uAZhDpkU3REnm%2Fs%2BEbEK9KPT3wM%3D\", oauth_signature_method=\"HMAC-SHA1\", oauth_timestamp=\"1585117527\", oauth_token=\"d9209df0-044e-4872-817e-51caa0b38edb\", oauth_verifier=\"NvyJlPDlzY\", oauth_version=\"1.0\""]
but when I call https://connectapi.garmin.com/oauth-service/oauth/access_token I get invalid nonce and timestamp error in response.
anyone who can help me out?
#Awais Shahid you need to update oauth_nonce & oauth_timestamp for each api call.
So if it is used once and when you try to use same oauth_nonce & oauth_timestamp second time even for same api it will throw this error.

jsonwebtoken package fails to verify user Office.context.mailbox.getUserIdentityToken result

I'm developing an Outlook web add-in.
I'm trying to verify a token that I'm passing to server side, through a node.js library, but it's failing and I can't understand why.
This is what I'm doing to retrieve the user identity token.
Office.context.mailbox.getUserIdentityTokenAsync(function(result) {
result.value // contains the token.
// send this value to server side, which I can see that it's working.
})
On the server side, I retrieve the token and do below:
token; // contains the token passed from the web-app.
const jwt = require('jsonwebtoken');
const request = require('request');
let decoded = jwt.decode(token, {complete: true});
// Get the key, we'll need this later since we'll have to
// find the matching key from the config file.
let key = decoded.header.x5t;
let amurl = JSON.parse(decoded.payload.appctx).amurl;
// Make a request to the url to get the configuration json file.
request(amurl, {}, (err, response, body) => {
let keys = JSON.parse(body).keys;
// Filter the keys so we get the one which we can verify.
let s = keys.filter(t => t.keyinfo.x5t === key);
let cert = s[0].keyvalue.value;
// Fails the verification.
console.log(jwt.verify(token, cert));
});
Just to clarify, I'm retrieving the token correctly and this npm package seems to be functioning fine for other jwt tokens. (So it's not really a configuration issue)
I have now found the answer to this question.
Just to re-iterate the problem was:
Office.context.mailbox.getUserIdentityToken method returns a jwt token.
When decoded this token contains an amurl field which points to the public certificate as a text.
When jsonwebtoken.verify(token, certText) is called, it's failing with the message invalid algorithm (even if you specify the algorithm from the token's header)
The problem was the formatting of the certificate text. jsonwebtoken package was looking for a particular formatting (splitted with 64 characters across each line along with the certificate begin and certificate end lines, so when formatted with method below - it started working properly.
The original code is taken from here: https://github.com/auth0/node-jsonwebtoken/issues/68 and formatted slightly to fit the needs.
/**
* #param {string} key - The certificate value retrieved from amurl property.
*/
formatKey: function(key) {
const beginKey = "-----BEGIN CERTIFICATE-----";
const endKey = "-----END CERTIFICATE-----";
const sanitizedKey = key.replace(beginKey, '').replace(endKey, '').replace('\n', '')
const keyArray = sanitizedKey.split('').map((l, i) => {
const position = i + 1
const isLastCharacter = sanitizedKey.length === position
if(position % 64 === 0 || isLastCharacter) {
return l + '\n'
}
return l
})
return `${beginKey}\n${keyArray.join('')}${endKey}\n`
}

Google Cloud Services put Fails when Using Signed URL

I am not able to PUT a file to google cloud services via a signed URL. When I try to do a PUT from a JS Client, I get:
"SignatureDoesNotMatch...The request signature we calculated does not match the signature you provided. Check your Google secret key and signing method."
When I try to post the file using CURL, I get the same error.
The curl command I use is:
#!/bin/bash
URL="https://storage.googleapis.com/..."
echo $URL
curl $URL -H "Content-Type: image/jpg" --upload-file b.jpg
I have configured the bucket I intend to post data to based on the documentation, I have generated a service account with the key, and this key is used to generate the signed url.
The request I sign is of the form:
PUT
image/jpg
1234567890
my-bucket/b.jpg
where the expiration and bucket names are set and computed.
I have the following Groovy code to generate signed urls:
public String sign(PrivateKey key, String toSign) {
Signature signer = Signature.getInstance("SHA256withRSA");
signer.initSign(key);
signer.update(toSign.getBytes("UTF-8"));
byte[] rawSignature = signer.sign();
String s = new String(Base64.encodeBase64(rawSignature), "UTF-8");
return s;
}
public String signUrl(PrivateKey key, String clientId, String method, String md5, String contentType,
long expiration, String gcsPath) {
String toSign = "${method}\n${md5}\n${contentType}\n${expiration}\n${gcsPath}";
String signature = sign(key, toSign);
String url = java.net.URLEncoder.encode(signature);
return url;
}
public String generateSignedUrl(PrivateKey key, String clientId, String method, String md5, String contentType,
long expiration, String gcsPath) {
String canonicalizedResource = "/${gcsPath}";
String signature = signUrl(key, clientId, method, md5, contentType, expiration, canonicalizedResource);
String finalUrl = "https://storage.googleapis.com/${gcsPath}?GoogleAccessId=${clientId}&Expires=${expiration}&Signature=${signature}"
finalUrl
}
This code is accompanied with the following passing unit test lifted straight out of the gsutils github project (https://github.com/GoogleCloudPlatform/gsutil/blob/master/gslib/tests/test_signurl.py):
#Test
void thatWeCanSignAPutUrlCorrectly() {
String expected = """https://storage.googleapis.com/test/test.txt?GoogleAccessId=test#developer.gserviceaccount.com&Expires=1391816302&Signature=A6QbgTA8cXZCtjy2xCr401bdi0e7zChTBQ6BX61L7AfytTGEQDMD%2BbvOQKjX7%2FsEh77cmzcSxOEKqTLUDbbkPgPqW3j8sGPSRX9VM58bgj1vt9yU8cRKoegFHXAqsATx2G5rc%2FvEliFp9UWMfVj5TaukqlBAVuzZWlyx0aQa9tCKXRtC9YcxORxG41RfiowA2kd8XBTQt4M9XTzpVyr5rVMzfr2LvtGf9UAJvlt8p6T6nThl2vy9%2FwBoPcMFaOWQcGTagwjyKWDcI1vQPIFQLGftAcv3QnGZxZTtg8pZW%2FIxRJrBhfFfcAc62hDKyaU2YssSMy%2FjUJynWx3TIiJjhg%3D%3D""";
long expiration = 1391816302;
String signedUrl = gsUtils.generateSignedUrl(privateKey, "test#developer.gserviceaccount.com","PUT", "", "", expiration, "test/test.txt")
assertEquals(expected, signedUrl);
}
Thank you for whatever insights you may be able to provide, I have been at this problem for a while.
Debugging signed URL logic is difficult. There is a useful trick that helps, though. An error response like the one you describe will look like this:
<?xml version='1.0' encoding='UTF-8'?><Error><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>
<StringToSign>PUT
text/jpeg
1472720161
/myBucket/test.txt</StringToSign></Error>
That last bit, <StringToSign>, is critical. The string in there is exactly the string that GCS will sign, and it's the string that you should also be signing. Compare you string against this one; it will probably be different in some way.
Also, because implementing this signing logic is tricky, the gcloud-java library has a signUrl() method that I recommend you use instead of implementing the logic yourself.
One reason that might cause this error (happened to me before) is when you generate a base64 encoded signature with your signed string, the encoded signature may contain illegal url characters + and /. Make sure you replace them in the string with %2B and %2F, respectively.