Amazon selling-partner-api feed encrypt file with Python - python

I tried GreatFeedDocument, then I received a status code is 200, but the get feed result's status:
"processingStatus": "FATAL"
I have too many times tried but I can't understand,
How I can encrypt the XML file?
Here is my python script.
from aws_auth import Auth
import requests
import json
from pprint import pprint
import base64, os
from Crypto.Util.Padding import pad
from Crypto.Cipher import AES
from Crypto.Hash import SHA256
ownercd = "****"
sp_auth = Auth(ownercd)
def pad(s):
# Data will be padded to 16 byte boundary in CBC mode
return s + b"\0" * (AES.block_size - len(s) % AES.block_size)
def getKey(password):
# Use SHA256 to hash password for encrypting AES
hasher = SHA256.new(password.encode())
return hasher.digest()
# Encrypt message with password
def encrypt(message, key, iv, key_size=256):
message = pad(message)
cipher = AES.new(key, AES.MODE_CBC, iv)
return iv + cipher.encrypt(message)
# Encrypt file
def encrypt_file(file_name, key, iv):
# Open file to get file Data
with open(file_name, "rb") as fo:
plaintext = fo.read()
# Encrypt plaintext with key has been hash by SHA256.
enc = encrypt(plaintext, key, iv)
# write Encrypted file
with open(file_name + ".enc", "wb") as fo:
fo.write(enc)
return enc
if sp_auth != False:
x_amz_access_token = sp_auth[0] # AWS SP-api access token
AWSRequestsAuth = sp_auth[1] # AWS signature
config = sp_auth[2] # From mongo's config
feed_headers = {
"Content-Type": "application/json",
"x-amz-access-token": x_amz_access_token,
}
contentType = {"contentType": "application/xml; charset=UTF-8"}
# [1.1] Create a FeedDocument
creat_feed_res = requests.post(
config["BASE_URL"] + "/feeds/2020-09-04/documents",
headers=feed_headers,
auth=AWSRequestsAuth,
data=json.dumps(contentType),
)
# [1.2] Store the response
CreatFeedResponse = creat_feed_res.json()["payload"]
feedDocumentId = CreatFeedResponse["feedDocumentId"]
initializationVector = CreatFeedResponse["encryptionDetails"][ "initializationVector"]
url = CreatFeedResponse["url"]
key = CreatFeedResponse["encryptionDetails"]["key"]
# [1.3] Upload and encrypt document
filename = "carton.xml"
iv = base64.b64decode(initializationVector)
encrypt_data = encrypt_file(filename, getKey(key), iv)
headers = {"Content-Type": "application/xml; charset=UTF-8"}
res = requests.put(url, headers=headers, data=encrypt_data)
print(res.status_code) # 200
print(res.request.body) # b'8L^\xbeY\xf....
print(res.content.decode())
It is the GetFeed Response:
Can anyone help me with that? Thanks in advance!

I solved my problem.
Here is my example code.
import requests
import json
from pprint import pprint
import base64, os
from Crypto.Util.Padding import pad
from Crypto.Cipher import AES
from Crypto.Hash import SHA256
from base64 import b64encode
import pyaes
dirname = os.path.dirname(os.path.abspath(__file__))
xmlFile_Name = dirname + "/template/carton.xml"
def encrypt(key, initializationVector):
# Create random 16 bytes IV, Create 32 bytes key
key = base64.b64decode(key)
iv = base64.b64decode(initializationVector)
# Encryption with AES-256-CBC
if os.path.isfile(xmlFile_Name) == False:
print("===== CARTON FILE NOT FOUND FROM ===== : {}".format(xmlFile_Name))
sys.exit()
with open(xmlFile_Name, "r") as fo:
plaintext = fo.read()
# print(plaintext)
encrypter = pyaes.Encrypter(pyaes.AESModeOfOperationCBC(key, iv))
ciphertext = encrypter.feed(plaintext.encode("utf8"))
ciphertext += encrypter.feed()
res = requests.put(
url,
data=ciphertext,
headers=contentType,
)
print("STATUS UPLOAD: ", res.status_code) # 200
if res.status_code == 200:
print("===== FILE UPLOAD DONE =====")
# REQUEST
feed_headers = {
"Content-Type": "application/json",
"x-amz-access-token": x_amz_access_token,
}
contentType = {"contentType": "application/xml; charset=UTF-8"}
creat_feed_res = requests.post(
config["BASE_URL"] + "/feeds/2020-09-04/documents",
headers=feed_headers,
auth=AWSRequestsAuth,
data=json.dumps(contentType),
)
# [1.2] Store the response
CreatFeedResponse = creat_feed_res.json()["payload"]
feedDocumentId = CreatFeedResponse["feedDocumentId"]
initializationVector = CreatFeedResponse["encryptionDetails"][ "initializationVector"]
url = CreatFeedResponse["url"]
key = CreatFeedResponse["encryptionDetails"]["key"]
# print(feedDocumentId)
# print("KEY =>", key)
# print("IV =>", initializationVector)
# print(url) #s3 url
contentType = {"Content-Type": "application/xml; charset=UTF-8"}
encryptFile = encrypt(key, initializationVector)
# [3.1] Create a feed
feedType = "POST_FBA_INBOUND_CARTON_CONTENTS"
marketplaceIds = "A1VC38T7*****"
inputFeedDocumentId = feedDocumentId
createdFeedURL = "https://sellingpartnerapi-fe.amazon.com/feeds/2020-09-04/feeds"
createFeedBody = {
"inputFeedDocumentId": inputFeedDocumentId,
"feedType": feedType,
"marketplaceIds": [marketplaceIds],
}
resCreatFeed = requests.post(
createdFeedURL,
headers=feed_headers,
auth=AWSRequestsAuth,
data=json.dumps(createFeedBody),
)
createFeedStatusCode = resCreatFeed.json()
if resCreatFeed.status_code == 202:
# print("Steph 2).")
feedId = createFeedStatusCode["payload"]["feedId"]
print("FEED ID: ", feedId)
# [3.2] CET a feed
getFeed = requests.get(
config["BASE_URL"] +
"/feeds/2020-09-04/feeds/{}".format(str(feedId)),
headers=feed_headers,
auth=AWSRequestsAuth,
)
pprint(getFeed.json()['payload'])
Here is my Get Feed response:
{'payload': {
'createdTime': '2021-02-24T07:43:22+00:00',
'feedId': '14795****',
'feedType': 'POST_FBA_INBOUND_CARTON_CONTENTS',
'marketplaceIds': ['A1VC38T7****'],
'processingStatus': 'DONE'}
}
Then I used the cartonID, I could download the get labels PDF file from the URL.
PackageLabelsToPrint is your cartonID you must set the CartonID here, for example:
Last, I could download a PDF file using the URL
Here is my GetLabel:

Related

Error decrypting using AES GCM in Python; AttributeError: 'Cipher' object has no attribute 'decrypt'

I am sending some information to a https server and receving a response. To increase the privary, I want to use some kind of encryption.
I am sending json data and I am encrypting the data as following
I am using Python3.6
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
from cryptography.hazmat.primitives.kdf.hkdf import HKDF
from cryptography.hazmat.primitives import hashes, hmac
from cryptography.hazmat.backends import default_backend
salt = os.urandom(16)
password = b'password'
kdf = PBKDF2HMAC(
algorithm=hashes.SHA256(),
length=32,
salt=salt,
iterations=10000
)
key = kdf.derive(password)
cipher = Cipher(algorithms.AES(key), modes.GCM(salt), backend=default_backend())
decryptor = cipher.decryptor()
encryptor = cipher.encryptor()
headers = {'Content-Type': 'application/json'}
data = {'xxxx': xxxx, 'xxxx': xxxx}
encrypted_data = encryptor.update(json.dumps(data).encode('utf-8')) + encryptor.finalize()
tag = encryptor.tag
Now, I am trying to decrypt the data as following
encrypted_data_b64 = request['encrypted_data']
tag_b64 = request['tag']
salt_b64 = request['salt']
encrypted_data = base64.b64decode(encrypted_data_b64)
tag = base64.b64decode(tag_b64)
salt = base64.b64decode(salt_b64)
password = b'password'
kdf = PBKDF2HMAC(
algorithm=hashes.SHA256(),
length=32,
salt=salt,
iterations=10000
)
key = kdf.derive(password)
cipher = Cipher(algorithms.AES(key), modes.GCM(salt), backend=default_backend())
decrypted_data = cipher.decrypt(encrypted_data, tag)
getting error :
AttributeError: 'Cipher' object has no attribute 'decrypt'
Also, tried
decryptor = cipher.decryptor()
decrypted_data = decryptor.update(encrypted_data) + decryptor.finalize()
Gettting error:
"Authentication tag must be provided when decrypting." ValueError: Authentication tag must be provided when decrypting.
So, I tried
decrypted_data = decryptor.update(encrypted_data) +
decryptor.finalize(tag) TypeError: finalize() takes 1 positional
argument but 2 were given
Any suggestion will be helpful.

AES 128 CTR random access decryption with cryptography.hazmat in Python

In theory AES encryption in CTR mode allows for decrypting at any block index (that is you don't have to decrypt the whole encrypted data from the begining if you want to decrypt only from a certain position/block index). You just add to the initial value of the counter the desired block index and with this updated counter you can start decrypting.
In practice I am trying to do this in Python with the cryptography.hazmat library but I am not able to do it.
I managed to do this using the pycryptodome module by specifying the initial_value=0 for the full file encryption and inital_value=desiredAESBlockIndex when constructing the cipher for decrypting at a specified AES block index.
Here is my code (using the cryptography module):
import os
import sys
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
def tstFullFileCryptography(aFilePath, aKey, aIV):
fileRawData = b''
with open(aFilePath, 'rb') as file:
fileRawData = file.read()
cipher = Cipher(algorithms.AES(aKey), modes.CTR(aIV))
encryptor = cipher.encryptor()
encryptedData = encryptor.update(fileRawData) + encryptor.finalize()
with open(aFilePath + ".cry", 'wb') as file:
file.write(encryptedData)
######### test #############
decryptor = cipher.decryptor()
decryptedData = decryptor.update(encryptedData) + decryptor.finalize()
if decryptedData == fileRawData:
print("OK - cryptography: full encryption")
else:
print("ERROR - cryptography: full encryption")
def tstCryptographyFromIndex(aFilePath, aKey, aIV):
algAES = algorithms.AES(aKey)
# the desired AES block index and size of data to decrypt
tstAESBlockIndex = 1
tstSizeInBytes = 16 * 13
tstRaw = b''
with open(aFilePath, 'rb') as file:
file.seek(tstAESBlockIndex * algAES.block_size)
tstRaw = file.read(tstSizeInBytes)
tstEncryptedData = b''
with open(aFilePath + ".cry", 'rb') as file:
file.seek(tstAESBlockIndex * algAES.block_size)
tstEncryptedData = file.read(tstSizeInBytes)
print(f"\ntstEncryptedData: {tstEncryptedData}\n")
################ decrypt from desired block index; advance iv
ivNum = int.from_bytes(aIV, byteorder=sys.byteorder)
incrementedIVNum = ivNum + tstAESBlockIndex
incrementedIV = incrementedIVNum.to_bytes(16, byteorder=sys.byteorder)
decryptor = Cipher(algAES, modes.CTR(incrementedIV)).decryptor()
tstDecryptedData = decryptor.update(tstEncryptedData) + decryptor.finalize()
print(f"tstDecryptedData: {tstDecryptedData}\n")
if tstRaw == tstDecryptedData:
print("OK - cryptography: index decryption")
else:
print("ERROR - cryptography: index decryption")
def main():
key = os.urandom(32)
iv = os.urandom(16)
tstFullFileCryptography("C:\\work\\python\\tstdata\\video.mp4", key, iv)
tstCryptographyFromIndex("C:\\work\\python\\tstdata\\video.mp4", key, iv)
return 0
if __name__ == "__main__":
main()
Here is the code after fixing You had 2 errors in your code that cause your decryption and file seeking to be incorrect.
import os
import sys
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
def tstFullFileCryptography(aFilePath, aKey, aIV):
fileRawData = b''
with open(aFilePath, 'rb') as file:
fileRawData = file.read()
cipher = Cipher(algorithms.AES(aKey), modes.CTR(aIV))
encryptor = cipher.encryptor()
encryptedData = encryptor.update(fileRawData) + encryptor.finalize()
with open(aFilePath + ".cry", 'wb') as file:
file.write(encryptedData)
######### test #############
decryptor = cipher.decryptor()
decryptedData = decryptor.update(encryptedData) + decryptor.finalize()
if decryptedData == fileRawData:
print("OK - cryptography: full encryption")
else:
print("ERROR - cryptography: full encryption")
def tstCryptographyFromIndex(aFilePath, aKey, aIV):
algAES = algorithms.AES(aKey)
# the desired AES block index and size of data to decrypt
tstAESBlockIndex = 1
tstSizeInBytes = 16 * 13
tstRaw = b''
with open(aFilePath, 'rb') as file:
file.seek(tstAESBlockIndex * 16)
tstRaw = file.read(tstSizeInBytes)
tstEncryptedData = b''
with open(aFilePath + ".cry", 'rb') as file:
file.seek(tstAESBlockIndex * 16)
tstEncryptedData = file.read(tstSizeInBytes)
print(f"\ntstEncryptedData: {tstEncryptedData}\n")
################ decrypt from desired block index; advance iv
ivNum = int.from_bytes(aIV, byteorder="big")
incrementedIVNum = ivNum + tstAESBlockIndex
incrementedIV = incrementedIVNum.to_bytes(16, byteorder="big")
decryptor = Cipher(algAES, modes.CTR(incrementedIV)).decryptor()
tstDecryptedData = decryptor.update(tstEncryptedData) + decryptor.finalize()
print(f"tstDecryptedData: {tstDecryptedData}\n")
if tstRaw == tstDecryptedData:
print("OK - cryptography: index decryption")
else:
print("ERROR - cryptography: index decryption")
def main():
key = os.urandom(32)
iv = os.urandom(16)
tstFullFileCryptography("text", key, iv)
tstCryptographyFromIndex("text", key, iv)
return 0
if __name__ == "__main__":
main()
First error is that you use incorrect block_size to seek since seeking is by byte level while block_size is at bit level = 128 in your case.
Second error is sys.byte_order which is little in case you use x86 processor while all the encryption ops were done in big endianness so you are basically reversing bytes leading to incorrect decryption fixing both errors now let me decrypt successfully.

Python RSA message encryption "Plaintext is too long"?

I'm using the following code to implement a basic RSA solution for encrypting and decrypting data, but when I try to encrypt any text that is longer than 87 characters I get an error saying Plaintext is too long from the file Crypto/Cipher/PKCS1_OAEP.py.
I read on other questions that RSA cannot encrypt and decrypt large sets of data but I'm not sure if that's true. If it is, what other options can I use to encrypt any amount of data?
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP
from Crypto.Signature import PKCS1_v1_5
from Crypto.Hash import SHA512, SHA384, SHA256, SHA, MD5
from Crypto import Random
from base64 import b64encode, b64decode
hash = "SHA-256"
def newkeys(keysize):
random_generator = Random.new().read
key = RSA.generate(keysize, random_generator)
private, public = key, key.publickey()
return public, private
def encrypt(message, pub_key):
cipher = PKCS1_OAEP.new(pub_key)
return cipher.encrypt(message)
i fixed it like this : [This is on my code i havent edited yours]
with open(file, 'rb') as f:
fernet_key = f.read()
if len(fernet_key) > 80 :
fernet_key1 = ''
tostart = 0
toadd = 0
flag_tostart = 0
while True:
if fernet_key1 == fernet_key:
break
else:
flag_tostart = flag_tostart +1
if flag_tostart == '2'or flag_tostart > 2 or flag_tostart == 2:
tostart = tostart + 80
toadd = toadd +80
if tostart == 0 :
tostart = 0
else:
tostart = tostart + 80
fernet_key[tostart:toadd] += fernet_key1
public_crypter = PKCS1_OAEP.new(public_key)
with open(file, 'wb') as f:
enc_fernet_key =
public_crypter.encrypt(fernet_key1)
f.write(enc_fernet_key)

Python error decoding a base64 string of a hazmart Cipher

I have a function that encodes an encrypted string (using hazmat Cipher) in base64 representation, but when I try to decrypt it later using a different function I am getting an error:
web_1 | File "/code/cart/models.py", line 58, in decrypt_hash_id
web_1 | int_id = int(padded_id.decode('utf-8').lstrip(' '))
web_1 | UnicodeDecodeError: 'utf-8' codec can't decode byte 0x86 in position 0: invalid start byte
My encryption function is:
from django.conf import settings
from base64 import urlsafe_b64encode, urlsafe_b64decode
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
def url_safe_encrypted_id(self):
backend = default_backend()
key = settings.URL_ENCRYPTION_KEY
iv = settings.URL_ENCRYPTION_IV
cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=backend)
encryptor = cipher.encryptor()
ct = encryptor.update(str(self.id).rjust(16).encode()) + encryptor.finalize()
return urlsafe_b64encode(ct)
However when I try to decrypt this base64 string I am sometimes getting an error. My method for decrypting is:
def decrypt_hash_id(hash):
# Decode order_id
backend = default_backend()
key = settings.URL_ENCRYPTION_KEY
iv = settings.URL_ENCRYPTION_IV
cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=backend)
decryptor = cipher.decryptor()
padded_id = decryptor.update(urlsafe_b64decode((hash + '===').encode())) + decryptor.finalize()
int_id = int(padded_id.decode().lstrip(' '))
return int_id
Can anyone help to see where the problem might lie? Probably 99% of the time it's working correctly, but occasionally I will get the error shown above.
I got the problem you have. It is in the line:
padded_id = decryptor.update(urlsafe_b64decode((hash + '===').encode())) + decryptor.finalize()
just modify it to:
padded_id = decryptor.update(urlsafe_b64decode(hash.decode())) + decryptor.finalize()
Here is a full example code, but with some modification to run:
from base64 import urlsafe_b64encode, urlsafe_b64decode
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
class settings:
URL_ENCRYPTION_KEY=b'1234567890123456'
URL_ENCRYPTION_IV=b'1234567890123456'
def url_safe_encrypted_id(text):
backend = default_backend()
key = settings.URL_ENCRYPTION_KEY
iv = settings.URL_ENCRYPTION_IV
cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=backend)
encryptor = cipher.encryptor()
ct = encryptor.update(text.rjust(16).encode()) + encryptor.finalize()
return urlsafe_b64encode(ct)
def decrypt_hash_id(hash):
# Decode order_id
backend = default_backend()
key = settings.URL_ENCRYPTION_KEY
iv = settings.URL_ENCRYPTION_IV
cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=backend)
decryptor = cipher.decryptor()
padded_id = decryptor.update(urlsafe_b64decode(hash.decode())) + decryptor.finalize()
int_id = int(padded_id.decode().lstrip(' '))
return int_id
text='10'
enc=url_safe_encrypted_id(text)
dec=decrypt_hash_id(enc)
print(text)
print(enc)
print(dec)
# output:
# 10
# b'l5DfiYydiPUCzGK7eLXOFQ=='
# 10
Good Luck
You can try this:
import base64
def encrypt():
string = "Hello world this is a sample string"
str_bytes = sample_string.encode("ascii")
base64_bytes = base64.b64encode(str_bytes)
base64_string = base64_bytes.decode("ascii")
print(f"Encoded string: {base64_string}")
def decrypt():
base64_string =" R2Vla3NGb3JHZWVrcyBpcyB0aGUgYmVzdA =="
base64_bytes = base64_string.encode("ascii")
sample_string_bytes = base64.b64decode(base64_bytes)
sample_string = sample_string_bytes.decode("ascii")
print(f"Decoded string: {sample_string}")

How to encrypt messages with ECC in pycryptodom

I'm using Hybrid encryption(RSA+AES) but the length was large and now i want to use ECC instead of RSA, but in pycryptodom there is no implementation for that..
this is my RSA code
def generate_keys():
key = RSA.generate(1024)
private_key = key.exportKey(format='PEM', pkcs=8,
protection="scryptAndAES128-CBC")
f = open("private_key.pem", "wb")
f.write(private_key)
public_key = key.publickey().exportKey('PEM')
f = open("public_key.pem", "wb")
f.write(public_key)
f.close()
def encrypt(username, msg):
#get the reciever's public key
f = open("{}.pem".format(username)) # a.salama.pem
recipient_key = RSA.import_key(f.read())
f.close()
# Encrypt the session key with the reciever's public RSA key
cipher_rsa = PKCS1_OAEP.new(recipient_key)
# Encrypt the data with the AES session key
session_key = get_random_bytes(16)
cipher_aes = AES.new(session_key, AES.MODE_EAX)
ciphertext, tag = cipher_aes.encrypt_and_digest(msg.encode('utf-
8'))
encrypted_data = cipher_rsa.encrypt(session_key) +
cipher_aes.nonce + tag + ciphertext
encrypted_data = base64.b64encode(encrypted_data)
return encrypted_data
And after trying to use ECC+AES the code will be
from Crypto.PublicKey import ECC
from Crypto.Random import get_random_bytes
from Crypto.Cipher import AES, PKCS1_OAEP
import base64
def generate_keys():
key = ECC.generate(curve='P-256') #3072 RSA
private_key = key.export_key(format='PEM')
f = open('private_key.pem','wt')
f.write(private_key)
f.close()
public_key = key.public_key().export_key(format='PEM')
f = open('public_key.pem','wt')
f.write(public_key)
f.close()
def encrypt(username, msg):
#get the reciever's public key
f = open("{}.pem".format(username), 'rt') # a.salama.pem
recipient_key = ECC.import_key(f.read())
f.close()
# Encrypt the session key with the reciever's public RSA key
cipher_rsa = PKCS1_OAEP.new(recipient_key)
# Encrypt the data with the AES session key
session_key = get_random_bytes(16)
#we use the EAX mode to allow detection of unauthorized
modifications.
cipher_aes = AES.new(session_key, AES.MODE_EAX)
ciphertext, tag = cipher_aes.encrypt_and_digest(msg.encode('utf-
8'))
encrypted_data = cipher_rsa.encrypt(session_key) +
cipher_aes.nonce + tag + ciphertext
encrypted_data = base64.b64encode(encrypted_data)
return encrypted_data.decode()
This gives me error in this line
cipher_rsa = PKCS1_OAEP.new(recipient_key)
but i want to encrypt the session key with the public key, how to do this with pycryptodome or any other way
Pycryptodome does not support elliptic curve-based encryption (ECC encryption).
Use the ECIES algorithm instead, e.g. this Python library: https://github.com/kigawas/eciespy
The ECIES (Elliptic Curve Integrated Encryption Scheme) is hybrid encryption scheme, which combines ECC public-key cryptography to asymmetrically encrypt a session key, used later to encrypt the input data with a symmetric cipher (e.g. with AES-GCM).
I know this is an old question, but to anyone else that comes here:
You can use Pycryptodome or Cryptography for this now. Using Pycrptodome for example:
from Crypto.PublicKey import ECC
def get_or_create_public_key(filename: str = "private_key.pem"):
""" Helper function to retrieve public key """
private_key_file = os.path.join(settings.BASE_DIR, filename)
if os.path.exists(private_key_file):
file = open(private_key_file, "rt")
private_key = ECC.import_key(file.read(), passphrase=settings.SECRET_KEY)
else:
private_key = ECC.generate(curve="P-256")
file = open(private_key_file, "wt")
file.write(
private_key.export_key(
format="PEM",
use_pkcs8=True,
passphrase=settings.SECRET_KEY,
protection="PBKDF2WithHMAC-SHA1AndAES128-CBC",
)
)
file.close()
public_key = private_key.public_key()
return public_key.export_key(format="PEM")

Categories

Resources