jwe cannot encrypt data correctly by jwcrypto - python

I has a requirement to generate encrypted data by jwe. The implementation of ruby can work correctly. But the python implementation cannot work correctly.
The ruby implementation
require 'jwe'
key = OpenSSL::PKey::RSA.new File.read 'public.pem'
payload = {user:"admin"}.to_json
puts JWE.encrypt(payload, key, enc: 'A192GCM')
The python implementation
from jwt import jwk_from_pem
from jwcrypto import jwe,jwk
from jwcrypto.common import json_encode
import json
with open("public.pem", "rb") as f:
key = jwk.JWK.from_pem(f.read())
key = key.public()
token = jwe.JWE(u'{user:"admin"}', json_encode({"alg":"RSA-OAEP","enc":"A192GCM"}))
token.add_recipient(key)
result = token.serialize()
result = json.loads(result)
print(result["protected"] + "." + result["encrypted_key"])
I have reffered the examples of jwcrypto. But the generated token is not correct.

fixed. I should use compact instead of appending data manually.
from jwt import jwk_from_pem
from jwcrypto import jwe,jwk
from jwcrypto.common import json_encode
with open("public.pem", "rb") as f:
key = jwk.JWK.from_pem(f.read())
key = key.public()
token = jwe.JWE('{"user":"admin"}', json_encode({"alg":"RSA-OAEP","enc":"A192GCM"}))
token.add_recipient(key)
result = token.serialize(compact=True)
print(result)

Related

How to validate data received via the Telegram's Web App

I'm trying to validate WebApp data but the result is not what I wanted.
Telegram documentation:
data_check_string = ...
secret_key = HMAC_SHA256(<bot_token>, "WebAppData")
if (hex(HMAC_SHA256(data_check_string, secret_key)) == hash) {
// data is from Telegram
}
MyCode:
BOT_TOKEN = '5139539316:AAGVhDje2A3mB9yA_7l8-TV8xikC7KcudNk'
data_check_string = 'query_id=AAGcqlFKAAAAAJyqUUp6-Y62&user=%7B%22id%22%3A1246866076%2C%22first_name%22%3A%22Dante%22%2C%22last_name%22%3A%22%22%2C%22username%22%3A%22S_User%22%2C%22language_code%22%3A%22en%22%7D&auth_date=1651689536&hash=de7f6b26aadbd667a36d76d91969ecf6ffec70ffaa40b3e98d20555e2406bfbb'
data_check_arr = data_check_string.split('&')
needle = 'hash='
hash_item = ''
telegram_hash = ''
for item in data_check_arr:
if item[0:len(needle)] == needle:
telegram_hash = item[len(needle):]
hash_item = item
data_check_arr.remove(hash_item)
data_check_arr.sort()
data_check_string = "\n".join(data_check_arr)
secret_key = hmac.new("WebAppData".encode(), BOT_TOKEN.encode(), hashlib.sha256).digest()
calculated_hash = hmac.new(data_check_string.encode(), secret_key, hashlib.sha256).hexdigest()
print(calculated_hash == telegram_hash) # print False
I'm trying to validate webapp data in python, but my code didn't give the intended result.
the hash which my code gives me is different from the telegram's one.
UPDATE: valid data added, and bot-token has been changed.
You need to unquote data_check_string
from urllib.parse import unquote
data_check_string = unquote('query_id=AAGcqlFKAAAAAJyqUUp6-Y62&user=%7B%22id%22%3A1246866076%2C%22first_name%22%3A%22Dante%22%2C%22last_name%22%3A%22%22%2C%22username%22%3A%22S_User%22%2C%22language_code%22%3A%22en%22%7D&auth_date=1651689536&hash=de7f6b26aadbd667a36d76d91969ecf6ffec70ffaa40b3e98d20555e2406bfbb')
And swap the arguments
calculated_hash = hmac.new(secret_key, data_check_string.encode(), hashlib.sha256).hexdigest()
You can replace the for-loops with a couple of lines (already incorporates kurdyukovpv's suggestion to unquote the query string):
data_check_string = sorted([ chunk.split("=") for chunk in unquote(data_check_string).split("&")
if chunk[:len("hash=")]!="hash="],
key=lambda x: x[0])
data_check_string = "\n".join([f"{rec[0]}={rec[1]}" for rec in data_check_string])
EDIT: Figured I might as well just post the entire working function I got out of this thread ) :
import hmac
import hashlib
from urllib.parse import unquote
def validate(hash_str, init_data, token, c_str="WebAppData"):
"""
Validates the data received from the Telegram web app, using the
method documented here:
https://core.telegram.org/bots/webapps#validating-data-received-via-the-web-app
hash_str - the has string passed by the webapp
init_data - the query string passed by the webapp
token - Telegram bot's token
c_str - constant string (default = "WebAppData")
"""
init_data = sorted([ chunk.split("=")
for chunk in unquote(init_data).split("&")
if chunk[:len("hash=")]!="hash="],
key=lambda x: x[0])
init_data = "\n".join([f"{rec[0]}={rec[1]}" for rec in init_data])
secret_key = hmac.new(c_str.encode(), token.encode(),
hashlib.sha256 ).digest()
data_check = hmac.new( secret_key, init_data.encode(),
hashlib.sha256)
return data_check.hexdigest() == hash_str

Evernote access token Python

I am trying to retrieve Evernote OAuth access token through Python by following the documentation here. However, multiple attempts, I am not able to retrieve the temporary access token which is the very first step in this 3 legged authorization.
Any idea what am I doing wrong here?
import time
import base64
import random
import uuid
import urllib
import collections
import urllib.parse
import hmac
import hashlib
import binascii
import requests
def escape(s):
return urllib.parse.quote(s, safe='~')
def get_nonce():
return uuid.uuid4().hex
def stringify_parameters(parameters):
output = ''
ordered_parameters = {}
ordered_parameters =
collections.OrderedDict(sorted(parameters.items()))
counter = 1
for k, v in ordered_parameters.items():
output += escape(str(k)) + '=' + escape(str(v))
if counter < len(ordered_parameters):
output += '&'
counter += 1
return output
oauth_parameters={
'oauth_timestamp': str(int(time.time())),
'oauth_signature_method': "HMAC-SHA1",
'oauth_version': "1.0",
'oauth_nonce': get_nonce(),
'oauth_consumer_key': 'consumerkey',
'oauth_callback':'http://localhost'
}
string_parameters=stringify_parameters(oauth_parameters)
secret='secret'
signature = hmac.new(secret.encode(), string_parameters.encode(),hashlib.sha1).digest()
oauth_parameters['oauth_signature']=escape(base64.b64encode(signature).decode())
res=requests.get('https://sandbox.evernote.com/oauth?'+stringify_parameters(oauth_parameters))
print(res.status_code)
I think the way you create a signature is incorrect. This works for me:
key = (escape(secret)+'&').encode()
message = ('GET&' + escape('https://sandbox.evernote.com/oauth') + '&' + escape(string_parameters)).encode()
signature = hmac.new(key, message, hashlib.sha1).digest()
oauth_parameters['oauth_signature'] = base64.b64encode(signature).decode()
res = requests.get('https://sandbox.evernote.com/oauth?' + stringify_parameters(oauth_parameters))

Using AES Encrypt get raise TypeError("Only byte strings can be passed to C code")

i try to encrypt the text with python and then i execute my code i get an error :
import base64
import boto3
from Crypto.Cipher import AES
PAD = lambda s: s + (32 - len(s) % 32) * ' '
def get_arn(aws_data):
return 'arn:aws:kms:{region}:{account_number}:key/{key_id}'.format(**aws_data)
def encrypt_data(aws_data, plaintext_message):
kms_client = boto3.client(
'kms',
region_name=aws_data['region'])
data_key = kms_client.generate_data_key(
KeyId=aws_data['key_id'],
KeySpec='AES_256')
cipher_text_blob = data_key.get('CiphertextBlob')
plaintext_key = data_key.get('Plaintext')
# Note, does not use IV or specify mode... for demo purposes only.
cypher = AES.new(plaintext_key, AES.MODE_ECB)
encrypted_data = base64.b64encode(cypher.encrypt(PAD(plaintext_message)))
# Need to preserve both of these data elements
return encrypted_data, cipher_text_blob
def main():
# Add your account number / region / KMS Key ID here.
aws_data = {
'region': 'eu-west-1',
'account_number': '701177775058',
'key_id': 'd67e033d-83ac-4b5e-93d4-aa6cdc3e292e',
}
# And your super secret message to envelope encrypt...
plaintext = PAD('Hello, World!')
# Store encrypted_data & cipher_text_blob in your persistent storage. You will need them both later.
encrypted_data, cipher_text_blob = encrypt_data(aws_data, plaintext)
print(encrypted_data)
if __name__ == '__main__':
main()
i Get : raise TypeError("Only byte strings can be passed to C code")
TypeError: Only byte strings can be passed to C code
Maybe whom know why? and how can i fix it ? please suggest!
Writing #Jeronimo's comment as an answer here, I was stuck with this same problem too and this helped.
Append a .encode("utf-8") to whatever you are passing to cypher.encrypt() function.
cypher.encrypt(PAD(plaintext_message).encode("utf-8"))
Note: this seems to be for python 3.x. For 2.x this same solution may not work.

How to achieve C# RSACryptoServiceProvider-like encryption in Python?

So, in C# I have the following code:
public static void Main (string[] args)
{
publicKeyXml = "<Modulus>mFCubVhPGG+euHuVQbNObqod/Ji0kRe+oh2OCFR7aV09xYiOklqFQ8jgIgAHvyCcM1JowqfFeJ5jV9up0Lh0eIiv3FPRu14aQS35kMdBLMebSW2DNBkfVsOF3l498WWQS9/THIqIaxbqwRDUxba5btBLTN0/A2y6WWiXl05Xu1c=</Modulus><Exponent>AQAB</Exponent>";
RSACryptoServiceProvider rSACryptoServiceProvider = new RSACryptoServiceProvider ();
rSACryptoServiceProvider.FromXmlString (publicKeyXml);
Console.WriteLine(Convert.ToBase64String (rSACryptoServiceProvider.Encrypt (Encoding.ASCII.GetBytes(args[0]), false)));
}
Which when I use to encrypt a message, it works just fine on a remote server (to which I have no source code for). However, when trying to do a similar thing in Python with PyCrypto, the remote server cannot decrypt.
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5
KEY = RSA.importKey(open('login.key').read()) # Converted to standard format
KEY_CIPHER = PKCS1_v1_5.new(KEY)
testmsg = KEY_CIPHER.encrypt("test msg").encode('base64').replace('\n', '')
# send testmsg down a socket
# Response: {"info":"java.lang.IllegalArgumentException: Could not decrypt.","msg":"Fail"}
Any thoughts as to why this would be the case?
OK, in my case it was rather odd. The server side was expecting my stuff backwards. To solve, I simply did this:
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5
KEY = RSA.importKey(open('login.key').read()) # Converted to standard format
KEY_CIPHER = PKCS1_v1_5.new(KEY)
testmsg = KEY_CIPHER.encrypt("test msg").encode('base64').replace('\n', '')
testmsg = "".join(reversed([testmsg[i:i+2] for i in range(0, len(testmsg), 2)]))
Modulus = "mFCubVhPGG+euHuVQbNObqod/Ji0kRe+oh2OCFR7aV09xYiOklqFQ8jgIgAHvyCcM1JowqfFeJ5jV9up0Lh0eIiv3FPRu14aQS35kMdBLMebSW2DNBkfVsOF3l498WWQS9/THIqIaxbqwRDUxba5btBLTN0/A2y6WWiXl05Xu1c="
Exponent = "AQAB"
mod_raw = b64decode(Modulus)
exp_raw = b64decode(Exponent)
mod = int(mod_raw.encode('hex'), 16)
exp = int(exp_raw.encode('hex'), 16)
seq = asn1.DerSequence()
seq.append(mod)
seq.append(exp)
der = seq.encode()
keyPub = RSA.importKey(der)
print base64.b64encode(keyPub.encrypt('test msg', 0)[0])

Fetching language detection from Google api

I have a CSV with keywords in one column and the number of impressions in a second column.
I'd like to provide the keywords in a url (while looping) and for the Google language api to return what type of language was the keyword in.
I have it working manually. If I enter (with the correct api key):
http://ajax.googleapis.com/ajax/services/language/detect?v=1.0&key=myapikey&q=merde
I get:
{"responseData": {"language":"fr","isReliable":false,"confidence":6.213709E-4}, "responseDetails": null, "responseStatus": 200}
which is correct, 'merde' is French.
so far I have this code but I keep getting server unreachable errors:
import time
import csv
from operator import itemgetter
import sys
import fileinput
import urllib2
import json
E_OPERATION_ERROR = 1
E_INVALID_PARAMS = 2
#not working
def parse_result(result):
"""Parse a JSONP result string and return a list of terms"""
# Deserialize JSON to Python objects
result_object = json.loads(result)
#Get the rows in the table, then get the second column's value
# for each row
return row in result_object
#not working
def retrieve_terms(seedterm):
print(seedterm)
"""Retrieves and parses data and returns a list of terms"""
url_template = 'http://ajax.googleapis.com/ajax/services/language/detect?v=1.0&key=myapikey&q=%(seed)s'
url = url_template % {"seed": seedterm}
try:
with urllib2.urlopen(url) as data:
data = perform_request(seedterm)
result = data.read()
except:
sys.stderr.write('%s\n' % 'Could not request data from server')
exit(E_OPERATION_ERROR)
#terms = parse_result(result)
#print terms
print result
def main(argv):
filename = argv[1]
csvfile = open(filename, 'r')
csvreader = csv.DictReader(csvfile)
rows = []
for row in csvreader:
rows.append(row)
sortedrows = sorted(rows, key=itemgetter('impressions'), reverse = True)
keys = sortedrows[0].keys()
for item in sortedrows:
retrieve_terms(item['keywords'])
try:
outputfile = open('Output_%s.csv' % (filename),'w')
except IOError:
print("The file is active in another program - close it first!")
sys.exit()
dict_writer = csv.DictWriter(outputfile, keys, lineterminator='\n')
dict_writer.writer.writerow(keys)
dict_writer.writerows(sortedrows)
outputfile.close()
print("File is Done!! Check your folder")
if __name__ == '__main__':
start_time = time.clock()
main(sys.argv)
print("\n")
print time.clock() - start_time, "seconds for script time"
Any idea how to finish the code so that it will work? Thank you!
Try to add referrer, userip as described in the docs:
An area to pay special attention to
relates to correctly identifying
yourself in your requests.
Applications MUST always include a
valid and accurate http referer header
in their requests. In addition, we
ask, but do not require, that each
request contains a valid API Key. By
providing a key, your application
provides us with a secondary
identification mechanism that is
useful should we need to contact you
in order to correct any problems. Read
more about the usefulness of having an
API key
Developers are also encouraged to make
use of the userip parameter (see
below) to supply the IP address of the
end-user on whose behalf you are
making the API request. Doing so will
help distinguish this legitimate
server-side traffic from traffic which
doesn't come from an end-user.
Here's an example based on the answer to the question "access to google with python":
#!/usr/bin/python
# -*- coding: utf-8 -*-
import json
import urllib, urllib2
from pprint import pprint
api_key, userip = None, None
query = {'q' : 'матрёшка'}
referrer = "https://stackoverflow.com/q/4309599/4279"
if userip:
query.update(userip=userip)
if api_key:
query.update(key=api_key)
url = 'http://ajax.googleapis.com/ajax/services/language/detect?v=1.0&%s' %(
urllib.urlencode(query))
request = urllib2.Request(url, headers=dict(Referer=referrer))
json_data = json.load(urllib2.urlopen(request))
pprint(json_data['responseData'])
Output
{u'confidence': 0.070496580000000003, u'isReliable': False, u'language': u'ru'}
Another issue might be that seedterm is not properly quoted:
if isinstance(seedterm, unicode):
value = seedterm
else: # bytes
value = seedterm.decode(put_encoding_here)
url = 'http://...q=%s' % urllib.quote_plus(value.encode('utf-8'))

Categories

Resources