don't do much hashing or encrypted server connections but trying to make the best of it,
I'm trying to play around with the coinbase API as their documentation is pretty direct https://docs.pro.coinbase.com/?python#signing-a-message
I've reached a point where I'm stuck getting the error 'bytes' object has no attribute 'encode' when trying pass my message variable into a new hmac, all my parameters for my key, secretkey, passphrase are all strings, I have tried encoding them as ascii and as base64 and as utf-8 and I get the same error
I'm assuming my encryption ordering is somehow the problem but I haven't been able to find anything useful on other stack overflow that matches my issue so far, hoping someone can at least point out what I'm missing
def get_accounts(self):
print("getting accounts")
secret = bytes(self.secret, 'UTF-8')
#unicode objects must be encoded before hashing?? what is that??
timestamp = str(time.time())
message = timestamp + 'GET' + self.baseurl + 'accounts' + ''
message = bytes(message, 'UTF-8')
hmac_key = base64.b64decode(secret)
signature = hmac.new(hmac_key, message, hashlib.sha256)
print("signature ",signature)
#getting stuck here
signature_b64 = signature.digest().encode('base64').rstrip('\n')
headers = {
'CB-ACCESS-SIGN': signature_b64,
'CB-ACCESS-TIMESTAMP': timestamp,
'CB-ACCESS-KEY': self.api_key,
'CB-ACCESS-PASSPHRASE': self.passphrase,
'Content-Type': 'application/json'
}
r = requests.get(self.baseurl + 'accounts', headers=headers)
print (r.json())
okay I got it to produce a normal base64 encoded string, the coinbase api says the signature isn't valid but that's technically a separate issue so I'll leave that alone for now
signature_b64 = base64.b64encode(bytes(signature, 'UTF-8'))
signature_b64 = signature_b64.decode("utf-8")
print('final',signature_b64)
Related
For the past month I've been trying to understand coinlist API (https://coinlist.co) since there is no API wrapper available it's been a hard task. I could figure it out that their API docs are really similar to coinbase exchange, and tried to extrapolate but with no success.
import json, hmac, hashlib, time, requests
from requests.auth import AuthBase
# Before implementation, set environmental variables with the names API_KEY and API_SECRET
API_KEY = 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'
API_SECRET = 'xxxx/xxxxxxxxxxxxxxxxxxxxx/xxxxxxxxxxxxxxxxx=='
# Create custom authentication for Coinlist API
class CoinlistWalletAuth(AuthBase):
def __init__(self, api_key, secret_key):
self.api_key = api_key
self.secret_key = secret_key
def __call__(self, request):
timestamp = str(int(time.time()))
message = timestamp + request.method + request.path_url + (request.body or '')
signature = hmac.new(self.secret_key, message, hashlib.sha256).hexdigest()
request.headers.update({
'CL-ACCESS-SIGN': signature,
'CL-ACCESS-TIMESTAMP': timestamp,
'CL-ACCESS-KEY': self.api_key,
})
return request
auth = CoinlistWalletAuth(API_KEY, API_SECRET)
#Test1 - Fetching account balance
response = requests.get('https://trade-api.coinlist.co/v1/accounts', auth=auth)
I am getting this TypeError: key: expected bytes or bytearray, but got 'str' when calling for the response.
Docs say - You must base64-encode the signature (the output of the sha256 HMAC). Why is it failing?
Two things:
TypeError: key: expected bytes or bytearray, but got 'str'
hmac.new(key, msg=None, digestmod='')
Return a new hmac object. key is a bytes or bytearray object giving the secret key. (...)
import base64
...
secret_key_as_bytes = base64.b64decode(self.secret_key, altchars=None)
The CL-ACCESS-SIG header is generated by creating a sha256 HMAC using the base64-decoded secret key on the prehash string timestamp + HTTP method + path + body (where '+' represents string concatenation) and base64-encode the output.
digest = hmac.new(secret_key_as_bytes, message, hashlib.sha256).digest()
signature = str(base64.b64encode(digest))
wish it's not too late
hmac.new()
function need the key argument to be bytes,so:
byte_key = bytes(self.secret_key, 'UTF-8') # key.encode() would also work in this case
but it's no done yet, if you go with this, you would get
TypeError: Unicode-objects must be encoded before hashing
to fix this , your message should be encoded.
message = message.encode()
h = hmac.new(byte_key, message, hashlib.sha256).hexdigest()
now it's done.
Howerver I got 400. sad.
I found someone have done this:
https://gist.github.com/badmofo/d109fbc447fea64d99e5ca58bdf53d7b
I am using Python's socket to respond to Websocket requests and I am facing problems with the Sec-WebSocket-Accept header. There might have been problems like this asked before but no one worked for me
The documentation at https://developer.mozilla.org states that for the connection to be established :
The server takes the value of the Sec-WebSocket-Key sent in the handshake request, appends 258EAFA5-E914-47DA-95CA-C5AB0DC85B11, takes SHA-1 of the new value, and is then base64 encoded.
The response I am currently sending is wrapped in a function that takes in the key from the request
and does what is said in the documentation
def socket101(self,template,key):
key = key.strip()
key += "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" #Add the key
key = sha1(key.encode()).digest() #Take the sha1
key = b64encode(key) #B64 encode it
return (b"HTTP/1.1 101 Switching Protocols\n"
+b"Content-Type: text/html\n"
+b"Connection: Upgrade\n"
+b"Upgrade: websocket\n"
+"Sec-WebSocket-Accept: {}\n".format(key).encode()
+b"\n"
)
Then I am calling the above method as follows
def AwaitMessage(client,address):
while True:
data = client.recv(1024)
try:
if str(data.strip()) != '':
headers = ParseHeaders(data) #Parses the request headers
HTTP_MSG = socket101(headers['Sec-WebSocket-Key']) #Call the function above passing the key
client.send(HTTP_MSG) #Send the message
except Exception as f:
pass
client.close()
But this does not want whatever I do and throws the following error:
Error during WebSocket handshake: Incorrect 'Sec-WebSocket-Accept' header value
The browser does not provide any information on why it is incorrect and I can't guess.
The JavaScript code is just making the connection with the socket.
const host = window.location.host
const PORT = 8000;
const socket = new WebSocket(`ws://${host}:${PORT}/ws/connection`)
Any ideas?
The following code worked for me
from base64 import b64encode
from hashlib import sha1
key = b64encode(sha1((key + GUID).encode()).digest()) #This
return (b"HTTP/1.1 101 Switching Protocols\n"
+b"Content-Type: text/html\n"
+b"Connection: Upgrade\n"
+b"Upgrade: websocket\n"
+b"Sec-WebSocket-Accept: " + key + b"\n" #Maybe this did something?
+b"\n"
)
I am almost sure that it is exactly the same as the thing I have above but the above does not work.
EDIT: The problem was that type(key) is bytes and thus f'{key}' will wrap quotes arround it, but on the snippet above it is concatinated with other bytes.
I'm trying to create a signature for an API call - for which the documentation provides these instructions:
timestamp = str(int(time.time()))
message = timestamp + request.method + request.path_url + (request.body or '')
signature = hmac.new(self.secret_key, message, hashlib.sha256).hexdigest()
However, I always get this error:
Exception has occurred: TypeError key: expected bytes or bytearray, but got 'str'
File "/Users/dylanbrandonuom/BouncePay_Code/src/coinbase/Coinbase_API.py", line 26, in __call__
signature = hmac.new(self.secret_key, message, hashlib.sha256).hexdigest()
File "/Users/dylanbrandonuom/BouncePay_Code/src/coinbase/Coinbase_API.py", line 40, in <module>
r = requests.get(api_url + 'user', auth=auth)
I've tried changing
signature = hmac.new(self.secret_key, message, hashlib.sha256).hexdigest()
to
signature = hmac.new(b'self.secret_key', message, hashlib.sha256).hexdigest()
but had no success.
Here is the second part of the error:
api_url = 'https://api.coinbase.com/v2/'
auth = CoinbaseWalletAuth(API_KEY, API_SECRET)
r = requests.get(api_url + 'user', auth=auth)
Is anyone able to let me know why this keeps occurring?
I'm thinking it might be the message variable with request.method and request.path_url, but I'm not sure.
The error message you're seeing tells you that you're passing a (unicode) string as the key argument to hmac.new(), but it expects bytes (or a bytearray).
This means that self.secret_key is a string, rather than a bytes object. There's no indication in your question where in your code self.secret_key is being assigned, but on the assumption that it's a constant somewhere, it might look like this:
SECRET = 'some secret key'
If so, changing that line to something like
SECRET = b'some secret key'
… ought to work. If you're assigning self.secret_key in some other way, it's impossible to know how to fix the problem without seeing that code.
The organization I work for is starting to use Canvas LMS and I was in charged of pulling out the platform's data and in order to help with data insights.
It's great that Canvas LMS offers a Data API but I had a hard time fiding a Python wrapper to use. I wanted to interact with it using Python which is part of our official stack here.
I know there is the official documentation in (https://portal.inshosteddata.com/docs/api) but I'm not really used with the authorization method and it would be so much easier to have a sample code.
So, how should I start my python code to interact with the Canvas LMS Data API?
Here we go!
I finally got it done with the help of the Canvas community through their website (https://community.canvaslms.com/thread/7423). I'm also posting question and answer here because I believe StackOverflow is easier to find answers.
Hope this will be useful to someone else!
#!/usr/bin/python
#imports
import datetime
import requests
import hashlib
import hmac
import base64
import json
#Get the current time, printed in the right format
def nowAsStr():
currentTime = datetime.datetime.utcnow()
prettyTime = currentTime.strftime('%a, %d %b %Y %H:%M:%S GMT')
return prettyTime
#Set up the request pieces
apiKey = 'your_key'
apiSecret = 'your_secret'
method = 'GET'
host = 'api.inshosteddata.com'
path = '/api/account/self/dump'
timestamp = nowAsStr()
requestParts = [
method,
host,
'', #content Type Header
'', #content MD5 Header
path,
'', #alpha-sorted Query Params
timestamp,
apiSecret
]
#Build the request
requestMessage = '\n'.join(requestParts)
print (requestMessage.__repr__())
hmacObject = hmac.new(apiSecret, '', hashlib.sha256)
hmacObject.update(requestMessage)
hmac_digest = hmacObject.digest()
sig = base64.b64encode(hmac_digest)
headerDict = {
'Authorization' : 'HMACAuth ' + apiKey + ':' + sig,
'Date' : timestamp
}
#Submit the request/get a response
uri = "https://"+host+path
print (uri)
print (headerDict)
response = requests.request(method='GET', url=uri, headers=headerDict, stream=True)
#Check to make sure the request was ok
if(response.status_code != 200):
print ('Request response went bad. Got back a ', response.status_code, ' code, meaning the request was ', response.reason)
else:
#Use the downloaded data
jsonData = response.json()
print json.dumps(jsonData, indent=4)
I'm writing a script of OAuth in Python.
For testing this, I use Twitter API. But it is not working well.
def test():
params = {
"oauth_consumer_key": TWITTER_OAUTH_CONSUMER_KEY,
"oauth_nonce": "".join(random.choice(string.digits + string.letters) for i in xrange(7)),
"oauth_signature_method": "HMAC-SHA1",
"oauth_timestamp": str(int(time.time())),
"oauth_token": res_dict["oauth_token"],
"oauth_version": "1.0",
}
status = {"status": u"Always_look_on_the_bright_side_of_life".encode("UTF-8")}
print status
params.update(status)
url = "http://twitter.com/statuses/update.xml"
key = "&".join([TWITTER_OAUTH_CONSUMER_SECRET, res_dict["oauth_token_secret"]])
msg = "&".join(["POST", urllib.quote(url,""),
urllib.quote("&".join([k+"="+params[k] for k in sorted(params)]), "-._~")])
print msg
signature = hmac.new(key, msg, hashlib.sha1).digest().encode("base64").strip()
params["oauth_signature"] = signature
req = urllib2.Request(url,
headers={"Authorization":"OAuth", "Content-type":"application/x-www-form-urlencoded"})
req.add_data("&".join([k+"="+urllib.quote(params[k], "-._~") for k in params]))
print req.get_data()
res = urllib2.urlopen(req).read()
print res
This script (status="Always_look_on_the_bright_side_of_life") is working.
But, in case status is "Always look on the bright side of life"(replaced underscore with space), it isn't working(is returning HTTP Error 401: Unauthorized).
I referenced this question, but failed.
Please give me some advice. Thank you.
I got the same problem in OAuth with FaceBook a while ago. The problem is that the signature validation on server side fails. See your signature generation code here:
msg = "&".join(["POST", urllib.quote(url,""),
urllib.quote("&".join([k+"="+params[k] for k in sorted(params)]), "-._~")])
print msg
signature = hmac.new(key, msg, hashlib.sha1).digest().encode("base64").strip()
It uses the raw (non-encoded) form of the string to generate the signature. However, the server side generates validates the signature against the URL quoted string:
req.add_data("&".join([k+"="+urllib.quote(params[k], "-._~") for k in params]))
To fix the code, you need to do fix this line by creating the signature from the url encoded parameter:
msg = "&".join(["POST", urllib.quote(url,""),
urllib.quote("&".join([k+"="+urllib.quote(params[k], "-._~") for k in sorted(params)]), "-._~")])
The easiest way to fix this is to add status = urllib.quote(status) after status = {"status": u"Always_look_on_the_bright_side_of_life".encode("UTF-8")}. This will escape the spaces and other special characters as required.