Create RSA Token in nodejs - node.js

I'm trying to authenticate to a REST API using encryption.
First I need to call the API to get an encryptionKey and a timestamp.
The encryptionKey is in Base 64 format.
Then I need to create a RSAToken using the key and finaly encrypt the password using password + "|" + timestamp.
This is some sample code using python to authenticate to the API
key, timestamp = get_encryption_key()
decoded_key = key.decode('base64')
rsa_key = RSA.importKey(decoded_key)
encrypted = rsa_key.encrypt(password + '|' + str(timestamp), 'x')
encrypted_password = encrypted[0]
and
import base64
from Crypto.PublicKey import RSA
r = requests.get(my_url, headers=headers)
myData = r.json()
decoded = base64.b64decode(myData['encryptionKey'])
key = RSA.importKey(decoded)
enc = key.encrypt(password + '|' + str(myData['timeStamp']), 'x')
encryptedPassword = enc[0]
session = "/session"
my_url = url + session
payload = {"identifier": identifier,
"password": encryptedPassword,
"encryptedPassword": "True"
}
Any hints to achieve this under Node?

You can use crypto.publicEncrypt to encrypt your password. Notice the padding, you might want to use the right padding that is being used in your Python script.
const crypto = require('crypto');
const constants = require('constants');
const decodedKey = Buffer(encriptionKey, 'base64').toString();
const encryptedPassword = crypto.publicEncrypt({
key: decodedKey,
padding : constants.RSA_PKCS1_OAEP_PADDING
} , Buffer(`${password}|${timestamp}`));
Check out this node.js test to find out more examples for different padding.

Related

Huobi working python 3.6 example create url (including signature)

Working example to generate a valid url (including signature) for the Huobi API.
In the Huobi API documenation there is no explicit example that allows you to verify your signature creation method step by step.
My intention is to create that here, but I need help, because I haven't managed yet.
The following is supposed to be the recipe.
Note that once you have this working, substitute valid values for your API key + secret and timestamp:
import hmac
import hashlib
import base64
from urllib.parse import urlencode
API_KEY = 'dummy-key'
API_SECRET = 'dummy-secret'
timestamp = '2021-03-04T11:36:39'
params_dict = {
'AccessKeyId': API_KEY,
'SignatureMethod': 'HmacSHA256',
'SignatureVersion': '2',
'Timestamp': timestamp
}
params_url_enc = urlencode(sorted(params_dict.items()))
pre_signed = 'GET\n'
pre_signed += 'api.huobi.pro\n'
pre_signed += '/v1/account/accounts\n'
pre_signed += params_url_enc
sig_bytes = hmac.new(
API_SECRET.encode(),
pre_signed.encode(),
hashlib.sha256).hexdigest().encode()
sig_b64_bytes = base64.b64encode(sig_bytes)
sig_b64_str = sig_b64_bytes.decode()
sig_url = urlencode({'Signature': sig_b64_str})
url = 'https://api.huobi.pro/v1/account/accounts?'
url += params_url_enc + '&'
url += sig_url
print('API_KEY={}'.format(API_KEY))
print('API_SECRET={}'.format(API_SECRET))
print('timestamp={}'.format(timestamp))
print('params_dict={}'.format(params_dict))
print('params_url_enc={}'.format(params_url_enc))
print('pre_signed:\n{}'.format(pre_signed))
print('sig_bytes={}'.format(sig_bytes))
print('sig_b64_bytes={}'.format(sig_b64_bytes))
print('sig_b64_str={}'.format(sig_b64_str))
print('sig_url={}'.format(sig_url))
print('url={}'.format(url))
Gives:
API_KEY=dummy-key
API_SECRET=dummy-secret
timestamp=2021-03-04T11:36:39
params_dict={'AccessKeyId': 'dummy-key', 'SignatureMethod': 'HmacSHA256', 'SignatureVersion': '2', 'Timestamp': '2021-03-04T11:36:39'}
params_url_enc=AccessKeyId=dummy-key&SignatureMethod=HmacSHA256&SignatureVersion=2&Timestamp=2021-03-04T11%3A36%3A39
pre_signed:
GET
api.huobi.pro
/v1/account/accounts
AccessKeyId=dummy-key&SignatureMethod=HmacSHA256&SignatureVersion=2&Timestamp=2021-03-04T11%3A36%3A39
sig_bytes=b'1921de9f42284bc0449c5580f52a9f7e7e3a54a6e8befc0d320992e757517a6b'
sig_b64_bytes=b'MTkyMWRlOWY0MjI4NGJjMDQ0OWM1NTgwZjUyYTlmN2U3ZTNhNTRhNmU4YmVmYzBkMzIwOTkyZTc1NzUxN2E2Yg=='
sig_b64_str=MTkyMWRlOWY0MjI4NGJjMDQ0OWM1NTgwZjUyYTlmN2U3ZTNhNTRhNmU4YmVmYzBkMzIwOTkyZTc1NzUxN2E2Yg==
sig_url=Signature=MTkyMWRlOWY0MjI4NGJjMDQ0OWM1NTgwZjUyYTlmN2U3ZTNhNTRhNmU4YmVmYzBkMzIwOTkyZTc1NzUxN2E2Yg%3D%3D
url=https://api.huobi.pro/v1/account/accounts?AccessKeyId=dummy-key&SignatureMethod=HmacSHA256&SignatureVersion=2&Timestamp=2021-03-04T11%3A36%3A39&Signature=MTkyMWRlOWY0MjI4NGJjMDQ0OWM1NTgwZjUyYTlmN2U3ZTNhNTRhNmU4YmVmYzBkMzIwOTkyZTc1NzUxN2E2Yg%3D%3D
Also add header in sending:
{"Content-Type": "application/x-www-form-urlencoded"}
Unfortunately, when I substitute my own valid API key + secret and a proper UTC time stamp, I invariably receive:
{"status":"error","err-code":"api-signature-not-valid","err-msg":"Signature not valid: Verification failure [校验失败]","data":null}
So what is going wrong here?
Huobi API documentation is
https://huobiapi.github.io/docs/spot/v1/en/#introduction
To get all accounts, use endpoint GET /v1/account/accounts
from datetime import datetime
import requests
import json
import hmac
import hashlib
import base64
from urllib.parse import urlencode
#Get all Accounts of the Current User
AccessKeyId = 'xxxxx-xxxxx-xxxxx-xxxxx'
SecretKey = 'xxxxx-xxxxx-xxxxx-xxxxx'
timestamp = str(datetime.utcnow().isoformat())[0:19]
params = urlencode({'AccessKeyId': AccessKeyId,
'SignatureMethod': 'HmacSHA256',
'SignatureVersion': '2',
'Timestamp': timestamp
})
method = 'GET'
endpoint = '/v1/account/accounts'
base_uri = 'api.huobi.pro'
pre_signed_text = method + '\n' + base_uri + '\n' + endpoint + '\n' + params
hash_code = hmac.new(SecretKey.encode(), pre_signed_text.encode(), hashlib.sha256).digest()
signature = urlencode({'Signature': base64.b64encode(hash_code).decode()})
url = 'https://' + base_uri + endpoint + '?' + params + '&' + signature
response = requests.request(method, url)
accts = json.loads(response.text)
print(accts)
Subsequently, if you need to run another endpoint (note timestamp allowance is ±5 minutes),
example, to get account balance, use GET /v1/account/accounts/{account_id}/balance
#Get Account Balance of a Specific Account
account_id = accts['data'][0]['id']
method = 'GET'
endpoint = '/v1/account/accounts/{}/balance'.format(account_id)
pre_signed_text = method + '\n' + base_uri + '\n' + endpoint + '\n' + params
hash_code = hmac.new(SecretKey.encode(), pre_signed_text.encode(), hashlib.sha256).digest()
signature = urlencode({'Signature': base64.b64encode(hash_code).decode()})
url = 'https://' + base_uri + endpoint + '?' + params + '&' + signature
response = requests.request(method, url)
r = json.loads(response.text)
print(r)
The mistake was that I took the hexidigest of the hash, whereas the digest was needed.
Working recipe here that you can check numerically to validate your code:
import hmac
import hashlib
import base64
from urllib.parse import urlencode
API_KEY = 'dummy-key'
API_SECRET = 'dummy-secret'
timestamp = '2021-03-04T12:54:56'
params_dict = {
'AccessKeyId': API_KEY,
'SignatureMethod': 'HmacSHA256',
'SignatureVersion': '2',
'Timestamp': timestamp
}
params_url_enc = urlencode(
sorted(params_dict.items(), key=lambda tup: tup[0]))
pre_signed = 'GET\n'
pre_signed += 'api.huobi.pro\n'
pre_signed += '/v1/account/accounts\n'
pre_signed += params_url_enc
sig_bin = hmac.new(
API_SECRET.encode(),
pre_signed.encode(),
hashlib.sha256).digest()
sig_b64_bytes = base64.b64encode(sig_bin)
sig_b64_str = sig_b64_bytes.decode()
sig_url = urlencode({'Signature': sig_b64_str})
url = 'https://api.huobi.pro/v1/account/accounts?'
url += params_url_enc + '&'
url += sig_url
print('API_KEY={}'.format(API_KEY))
print('API_SECRET={}'.format(API_SECRET))
print('timestamp={}'.format(timestamp))
print('params_dict={}'.format(params_dict))
print('params_url_enc={}'.format(params_url_enc))
print('pre_signed:\n{}'.format(pre_signed))
print('sig_bin={}'.format(sig_bin))
print('sig_b64_bytes={}'.format(sig_b64_bytes))
print('sig_b64_str={}'.format(sig_b64_str))
print('sig_url={}'.format(sig_url))
print('url={}'.format(url))
Result:
$ python test_huobi_so.py
API_KEY=dummy-key
API_SECRET=dummy-secret
timestamp=2021-03-04T12:54:56
params_dict={'AccessKeyId': 'dummy-key', 'SignatureMethod': 'HmacSHA256', 'SignatureVersion': '2', 'Timestamp': '2021-03-04T12:54:56'}
params_url_enc=AccessKeyId=dummy-key&SignatureMethod=HmacSHA256&SignatureVersion=2&Timestamp=2021-03-04T12%3A54%3A56
pre_signed:
GET
api.huobi.pro
/v1/account/accounts
AccessKeyId=dummy-key&SignatureMethod=HmacSHA256&SignatureVersion=2&Timestamp=2021-03-04T12%3A54%3A56
sig_bin=b'_\xb9k\x82!\xb4B%A\xfe\x0c \xff\x07%JE\xbe\x82\x8b-<^\xb7\xfc\x06\x85G\xb5$\x81\xd7'
sig_b64_bytes=b'X7lrgiG0QiVB/gwg/wclSkW+gostPF63/AaFR7Ukgdc='
sig_b64_str=X7lrgiG0QiVB/gwg/wclSkW+gostPF63/AaFR7Ukgdc=
sig_url=Signature=X7lrgiG0QiVB%2Fgwg%2FwclSkW%2BgostPF63%2FAaFR7Ukgdc%3D
url=https://api.huobi.pro/v1/account/accounts?AccessKeyId=dummy-key&SignatureMethod=HmacSHA256&SignatureVersion=2&Timestamp=2021-03-04T12%3A54%3A56&Signature=X7lrgiG0QiVB%2Fgwg%2FwclSkW%2BgostPF63%2FAaFR7Ukgdc%3D

AEAD AES-256-GCM in Node.js

How to decrypt data encrypted by other languages with Node.js?
To describe the problem, we write some code in Python:
plain_text = 'some data'
nonce = 'some nonce string'
associated_data = 'some associated data'
key = '--- 32 bytes secret key here ---'
encrypting in python
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
import base64
aesgcm = AESGCM(key.encode())
encrypted = aesgcm.encrypt(nonce.encode(), plain_text.encode(), associated_data.encode())
print(aesgcm.decrypt(nonce.encode(), encrypted, associated_data.encode()))
ciphertext = base64.b64encode(encrypted)
Decrypting in Node.js, we don't need additional dependencies.
The crypto module has implemented GCM algorithm, but it is different in concept.
const crypto = require('crypto')
encrypted = Buffer.from(ciphertext, 'base64')
let decipher = crypto.createDecipheriv('AES-256-GCM', key, nonce)
decipher.setAuthTag(encrypted.slice(-16))
decipher.setAAD(Buffer.from(associated_data))
let output = Buffer.concat([
decipher.update(encrypted.slice(0, -16)),
decipher.final()
])
console.log(output.toString())

crypto.load_certificate / Get public key encryption

How is it possible from crypto.load_certificate to get the public key encryption ? (for example "RSA (2048 Bits").
I can get the public key easily as below :
from OpenSSL import crypto
cert = crypto.load_certificate(crypto.FILETYPE_PEM, open("certificate.crt")).read()
pubKey = cert.get_pubkey()
But I couldn't find anything in the documentation concerning the encryption. Any ideas?
Actually it is really simple :
from OpenSSL import crypto
cert = crypto.load_certificate(crypto.FILETYPE_PEM, open("certificate.crt")).read()
pubKey = cert.get_pubkey()
keySize = pubKey.bits()
if pubKey.type() == crypto.TYPE_RSA:
keyType = 'RSA'
elif pubKey.type() == crypto.TYPE_DSA:
keyType = 'DSA'
print(keyType + "-" + str(keySize))

Access Denied Signed URL Python3 using cloud front

I am trying to create signed urls for my s3 bucket to which only select people will have access to until the time expires.
I am not able to find the issue in my code. Please help
import boto
from boto.cloudfront import CloudFrontConnection
from boto.cloudfront.distribution import Distribution
import base64
import json
import rsa
import time
def lambda_handler(event, context):
url = "https://notYourUrl.com/example.html"
expires = int(time.time() + 36000)
pem = """-----BEGIN RSA PRIVATE KEY-----
myKey
-----END RSA PRIVATE KEY-----"""
Cloudfront console
key_pair_id = 'myKey'
policy = {
"Statement": [
{
"Resource":url,
"Condition":{
"DateLessThan":{"AWS:EpochTime":expires},
}
}
]
}
policy = json.dumps(policy)
private_key = rsa.PrivateKey.load_pkcs1(pem)
policy = policy.encode("utf-8")
signed = rsa.sign(policy, private_key, 'SHA-1')
policy = base64.b64encode(policy)
policy = policy.decode("utf-8")
signature = base64.urlsafe_b64encode(signed)
signature = signature.decode("utf-8")
policy = policy.replace("+", "-")
policy = policy.replace("=", "_")
policy = policy.replace("/", "~")
signature = signature.replace("+", "-")
signature = signature.replace("=", "_")
signature = signature.replace("/", "~")
print("%s?Expires=%s&Signature=%s&Key-Pair-Id=%s" % (url,expires, signature, key_pair_id))
When I test the file on lambda I am able to produce and print a URL but when I access the URL I receive an access denied error message from the XML file.
I am not sure what I am doing wrong at this point. To test if I am able to generated any SignedUrl I created a node.js lambda in which I am successfully able to generate the URL and even access my page.
<Error>
<Code>AccessDenied</Code>
<Message>Access denied</Message>
</Error>
After many failed tries to make my code work I decided to go with a different approach and used node.js to fullfill my needs. The code below works perfectly and I am able to generate signed url's
For now I used a hardcoded time value to test my code and will later on work on getting that dynamically using datetime.
var AWS = require('aws-sdk');
var keyPairId = 'myKeyPairId';
var privateKey = '-----BEGIN RSA PRIVATE KEY-----' + '\n' +
'-----END RSA PRIVATE KEY-----';
var signer = new AWS.CloudFront.Signer(keyPairId, privateKey);
exports.handler = function(event, context) {
var options = {url: "https://notYourUrl.com/example.html", expires: 1621987200, 'Content-Type': 'text/html'};
//console.log(options);
const cookies = signer.getSignedCookie(options);
const url = signer.getSignedUrl(options);
console.log("Printing URL "+url);
console.log(cookies);
};

Geocoding: TypeError: Unicode-objects must be encoded before hashing

Similar questions are seen and answered at SO, mine seems not falling into them.
I am following instruction from Google for geocoding.
The example in Google is outdated as urlparse no longer exists in python 3, instead it is in urllib.parse
So lightly modified the example code below:
import urllib.parse
def sign_url(input_url=None, secret=None):
""" Sign a request URL with a URL signing secret.
Usage:
from urlsigner import sign_url
signed_url = sign_url(input_url=my_url, secret=SECRET)
Args:
input_url - The URL to sign
secret - Your URL signing secret
Returns:
The signed request URL
"""
if not input_url or not secret:
raise Exception("Both input_url and secret are required")
url = urllib.parse.urlparse(input_url)
# We only need to sign the path+query part of the string
url_to_sign = url.path + "?" + url.query
# Decode the private key into its binary format
# We need to decode the URL-encoded private key
decoded_key = base64.urlsafe_b64decode(secret)
# Create a signature using the private key and the URL-encoded
# string using HMAC SHA1. This signature will be binary.
signature = hmac.new(decoded_key, url_to_sign, hashlib.sha1)
# Encode the binary signature into base64 for use within a URL
encoded_signature = base64.urlsafe_b64encode(signature.digest())
original_url = url.scheme + "://" + url.netloc + url.path + "?" + url.query
# Return signed URL
return original_url + "&signature=" + encoded_signature
I received error on signature = hmac.new(decoded_key, url_to_sign, hashlib.sha1) of below:
TypeError: Unicode-objects must be encoded before hashing
How do I get it fixed? Thank you very much.
After the function revised as below, it works as expected:
def sign_url(input_url=None, secret=None):
""" Sign a request URL with a URL signing secret.
Usage:
from urlsigner import sign_url
signed_url = sign_url(input_url=my_url, secret=SECRET)
Args:
input_url - The URL to sign
secret - Your URL signing secret
Returns:
The signed request URL
"""
if not input_url or not secret:
raise Exception("Both input_url and secret are required")
url = urllib.parse.urlparse(input_url)
# We only need to sign the path+query part of the string
url_to_sign = url.path + "?" + url.query
# Decode the private key into its binary format
# We need to decode the URL-encoded private key
decoded_key = base64.urlsafe_b64decode(secret)
# Create a signature using the private key and the URL-encoded
# string using HMAC SHA1. This signature will be binary.
signature = hmac.new(decoded_key, url_to_sign.encode('utf-8'), hashlib.sha1)
# Encode the binary signature into base64 for use within a URL
encoded_signature = base64.urlsafe_b64encode(signature.digest())
original_url = url.scheme + "://" + url.netloc + url.path + "?" + url.query
# Return signed URL
return original_url + "&signature=" + encoded_signature.decode('ascii')

Resources