How to Encrpyt message using PGPY in Python3 - python

I have tried following code to encrypt plaintext message using public key and getting an error.
import pgpy
key,_ = pgpy.PGPKey.from_file('public_key.asc')
data = "This is simple message"
msg = pgpy.PGPMessage.new(data, cleartext=True)
encrypted_message = key.encrypt(msg)
print (encrypted_message)
Error -
Traceback (most recent call last):
File "pgp_encrypt.py", line 8, in <module>
encrypted_message = key.encrypt(msg)
File "/home/<path>/.local/lib/python3.8/site-packages/pgpy/decorators.py", line 125, in _action
with self.usage(key, kwargs.get('user', None)) as _key:
File "/usr/lib/python3.8/contextlib.py", line 113, in __enter__
return next(self.gen)
File "/home/<path>/.local/lib/python3.8/site-packages/pgpy/decorators.py", line 96, in usage
raise PGPError("Key {keyid:s} does not have the required usage flag {flags:s}".format(**em))
pgpy.errors.PGPError: Key 2F0B2A386761A2B8 does not have the required usage flag EncryptStorage, EncryptCommunications
I have followed examples from this link - https://pgpy.readthedocs.io/en/latest/examples.html

Only private key is being used to both decrypt and encrypt in this example.
To encrypt a simple message:
import pgpy
from pgpy.constants import PubKeyAlgorithm, KeyFlags, HashAlgorithm, SymmetricKeyAlgorithm, CompressionAlgorithm
import json
import warnings
key_decrypt,_ = pgpy.PGPKey.from_file('priv.asc')
data = "Encrypt this"
def pgpy_create_key(key):
warnings.filterwarnings("ignore")
uid = pgpy.PGPUID.new('user', email='user#example.gov')
with key.unlock(""):
key.add_uid(uid, usage={KeyFlags.Sign, KeyFlags.EncryptCommunications, KeyFlags.EncryptStorage},
hashes=[HashAlgorithm.SHA256, HashAlgorithm.SHA384, HashAlgorithm.SHA512, HashAlgorithm.SHA224],
ciphers=[SymmetricKeyAlgorithm.AES256, SymmetricKeyAlgorithm.AES192, SymmetricKeyAlgorithm.AES128],
compression=[CompressionAlgorithm.ZLIB, CompressionAlgorithm.BZ2, CompressionAlgorithm.ZIP, CompressionAlgorithm.Uncompressed])
return key
key_encrypt = pgpy_create_key(key_decrypt)
def pgpy_encrypt(key, data):
message = pgpy.PGPMessage.new(data)
enc_message = key.pubkey.encrypt(message)
return bytes(enc_message)
pgpy_enc = pgpy_encrypt(key_encrypt, json.dumps(data))
print (pgpy_enc)
To decrypt a message:
import pgpy
import json
key_decrypt,_ = pgpy.PGPKey.from_file('priv.asc')
encrypted_data =b"\xc1\xc0L\x03/\x0b*8ga\xa2\xb8\x01\x07\xfb\x074\xf3\x06\x17Cu\xcf\xc7q\xe0\xbcn`}\xcc\xb1\xbe\x02\xd0\xf9X^\xbf\x836{-9\xd6\x0b\xe3\x9f\x82\xf0\xfc4\x83\xec\xcb\x95\x06\x9e\xd5e\xf5\x7fo\xf2\x99\x07\xa9\xe5\x958\x88\xf5U\x83\x00\xa8\x99\xa38\x1ec\x8a\x87d\xf9\xd7\x94\xeb\xfej\xf5G\xa2\xd6\xc9\xde[N|1\x08Lb\x95\xc6\x1c\xf6!\xb1\xe4TL\x08\x9a:\xbd5\xa3\xfc\xb5m\x94\xea\xff\r\x07Z3\xe7Q\xb9\x05ya!\xab\x1d_H\x82\xa4\x91\xeb\n \xe6\xe4\x90h\x1f\x8f\xcbA\xff\xcau\xa0i\xb35)\xe6\x1b\x07\x18_\xdf\x8a\x12EH\xbaec\xd9^\xbf\xa5\x88\xe26T\x94\xc8AS(\xc0\xbd\x1b\xba\xff\xd2a\xe4q\xa2\x850\xca\xc88-#3\xd3<\xfc\xe1\xce\xd3\xbf\x1d`JoK#\xb7%\x93\xb2\xb8nZ\xdfs\xf8\xae\xf4\xff\xc9\xf9Kl\x0f\xf9v\x1a{\xa2N\x82X\xb7D\x0e\xf9\xef~\x9b\xa56\x11\xf2\x8f\x0c?*\xa1\n\xd5\xad\x13\x08\x8ak\xeb6_\xac\xd2D\x01\xae)a[+3c*\xd1'\n\n\x06\xa9J\n\x8a\xe2\xca{\xa7\x8b$\xd9\xa6\xbas\xeb\xb4d-\x10\xd7\xeb\xc5\xb4\x12\xa89\xda\xec\xc6\x89g\xf9\xf9\xf9-'\xc0\xc2\xba\xaei\x03\r\xaa\xfc\xfa\x0e\x08\x9a\xc3\x8c\xa9A-"
def pgpy_decrypt(key, enc_data):
message = pgpy.PGPMessage.from_blob(enc_data)
with key.unlock(""):
return (key.decrypt(message).message)
decrypted_data = pgpy_decrypt(key_decrypt, encrypted_data)
print (decrypted_data)

To add to the above, here's an example that works in a single run and that doesn't require saving the key to a file. Of course, in real life, you would want to save the key to a file as #krishna reddy has assumed. But if you just want to see how the API is utilized I recommend looking at the following:
from pgpy import PGPUID, PGPKey, PGPMessage
from pgpy.constants import (
PubKeyAlgorithm,
KeyFlags,
HashAlgorithm,
SymmetricKeyAlgorithm,
CompressionAlgorithm,
)
# 1. Recipient sets up user, and generates a key for that user
uid = PGPUID.new("Abraham Lincoln", comment="Honest Abe", email="abraham.lincoln#whitehouse.gov")
key = PGPKey.new(PubKeyAlgorithm.RSAEncryptOrSign, 4096)
key.add_uid(
uid,
usage={KeyFlags.Sign, KeyFlags.EncryptCommunications, KeyFlags.EncryptStorage},
hashes=[HashAlgorithm.SHA256, HashAlgorithm.SHA384, HashAlgorithm.SHA512, HashAlgorithm.SHA224],
ciphers=[SymmetricKeyAlgorithm.AES256, SymmetricKeyAlgorithm.AES192, SymmetricKeyAlgorithm.AES128],
compression=[
CompressionAlgorithm.ZLIB,
CompressionAlgorithm.BZ2,
CompressionAlgorithm.ZIP,
CompressionAlgorithm.Uncompressed,
],
)
# Typically, recipient then saves the key information to a file on their server
# 2. Recipient publishes the public key.
print(f"public key:\n{key.pubkey}")
# 3. Sender retrieves pubkey.
# Here we use `from_blob`. But typically you'd use `from_file`:
pubkey, _ = PGPKey.from_blob(str(key.pubkey))
# 4. Sender prepares a message
message = PGPMessage.new("Hello PGP! You're so Clever!!!!")
print(f"plaintext: [{message.message}]")
# 5. Sender encrypts the message using the public key
ciphertext = str(pubkey.encrypt(message))
print(f"cipherbytes: [{ciphertext}]")
# 6. Sender sends the ciphertext
# ...
# key below would typically be reconstructed from its saved file
# 7. Recipient decrypts the cyphertext
cipher_msg = PGPMessage.from_blob(ciphertext)
decrypted = key.decrypt(cipher_msg).message
print(f"decrypted: [{decrypted}]")
assert decrypted == message.message
Running the script results in output like the following:
public key:
-----BEGIN PGP PUBLIC KEY BLOCK-----
xsFNBGFMqcABEADJaKvhmgNVqwWOAL8h/tdD9ESlGd0PUquJDenbvEwv1VH7EIWa
nB8fOb/klBeauN9RSzL7VGaCpMvm5Rjz+Z8Cx4mB5yGkABtEdGSvdR3U8z11L5bF
... etc ...
nq/nCRHynRTk7WDnm5AM29mdGE5dd7C9wH6OodY7jXFnPAJZM7a75++XHsCwgoc+
2XdmnSXkFKQT9JIP4VEfV8pqKgBiwE5pQTYPGQ==
=cfgq
-----END PGP PUBLIC KEY BLOCK-----
plaintext: [Hello PGP! You're so Clever!!!!]
ciphertext: [<IntegrityProtectedSKEDataV1 [tag 18][v1] at 0x1021b81f0>]
decrypted: [Hello PGP! You're so Clever!!!!]

Related

Decrypt Kamstrup WMBUS in Python

I have a Kamstrup WMbus water meter sending out frames like this:
21442D2C529027581B168D2814900939201F0775C6452FBBAC155B46A546035219D51AB8
I am trying to decrypt this in Python.
Breaking the frame up I have the following values:
Key 16ce383ebc0790e928215525cd4f7abf
Input IV 2D2C529027581B162890093920000000
Input Data 1F0775C6452FBBAC155B46A546035219D5
Pasting this into http://www.cryptogrium.com/aes-ctr.html results in a valid decrypted frame:
bbe57934ddc46a71004400000044000000
I have tried both PyCrypto and PyCryptodome but neither gives the same correct answer than cryptogrium.com.
from Cryptodome.Cipher import AES
# ctr = Crypto.Util.Counter.new(128, initial_value=int("0000002039099028161B582790522C2D", 16))
# cipher = Crypto.Cipher.AES.new(ecryptionKey, Crypto.Cipher.AES.MODE_CTR, counter=ctr)
# secret = Secret()
spec = AES.new(ecryptionKey, AES.MODE_CTR, iv=inputIV)
dataDec = cipher.decrypt(data)
The first commented out approach runs but gives the wrong result.
The second one stops with the error:
TypeError: Invalid parameters for CTR mode: {'iv': bytearray(b"-,R\x90\'X\x1b\x16(\x90\t9 \x00\x00\x00")}
In C# we are using the following which works:
IBufferedCipher cipher = CipherUtilities.GetCipher("AES/CTR/NoPadding");
ParametersWithIV ivAndKey = new ParametersWithIV(new KeyParameter(keyBytes), inputIVBytes);
cipher.Init(false, ivAndKey);
...
int length1 = cipher.ProcessBytes(data, 0, data.Length, outBuf, 0);
int length2 = cipher.DoFinal(outBuf, length1);
...
I am confused because C# uses the parameters I have: key, data, IV
But Python expects: key, data, counter
Does anyone have an example how I can decrypt this in Python3? Or maybe explain how I should use the IV to set up the counter for AES-CNT?
In the end one of the examples from https://www.programcreek.com/python/example/87998/Crypto.Cipher.AES.MODE_CTR put me on the right track. After fixing a couple of typos in my code it worked correctly.
def kamstrupDecrypt(meterSerial, frame, payload):
inputIV = getInputIV(frame)
print("IV:", binascii.hexlify(inputIV))
ecryptionKey = getEncryptionKey(meterSerial)
print("Key:", binascii.hexlify(ecryptionKey))
counter = Counter.new(128, initial_value = bytes_to_long(inputIV))
cipher = AES.new(ecryptionKey, AES.MODE_CTR, counter=counter)
payloadDec = cipher.decrypt(payload)
print("Decrypted: ", binascii.hexlify(payloadDec))
return payloadDec

Why Python and Node.js's HMAC result is different in this code?

Recently, I have a task to make HMAC to communicate API server.
I got a sample code of node.js version which makes HMAC of message. Using concept and sample, I've got to make a python code which is equivalent with node.js version but result is different, but I have no idea why.
Please review both code and help finding the difference.
Python 3.0
import hmac
import string
import hashlib
import base64
secret = 'PYPd1Hv4J6'
message = '1515928475.417'
key = base64.b64encode(secret.encode('utf-8'))
hmac_result = hmac.new(key, message.encode('utf-8'), hashlib.sha512)
print(base64.b64encode(hmac_result.digest()))
Result (Python 3.6)
b'7ohDRJGMGYjfHojnrvNpM3YM9jb+GLJjbQvblzrE17h2yoKfIRGEBSjfOqQFO4iKD7owk+gSciFxFkNB+yPP4g=='
Node.JS
var crypto = require('crypto');
var secret = 'PYPd1Hv4J6';
var message = '1515928475.417'
var key = Buffer(secret, 'base64');
var hmac = crypto.createHmac('sha512', key);
var hmac_result = hmac.update(message).digest('base64');
console.log(hmac_result)
Result (Node.JS 6.11)
m6Z/FxI492VXKDc16tO5XDNvty0Tmv0b1uksSbiwh87+4rmg43hEXM0WmWzkTP3aXB1s5rhm05Hu3g70GTrdEQ==
Your input keys are different, so the outputs will be different.
Node:
var secret = 'PYPd1Hv4J6';
var message = '1515928475.417'
var key = Buffer(secret, 'base64'); // buffer of bytes from the base64-encoded string 'PYPd1Hv4J6'
// <Buffer 3d 83 dd d4 7b f8 27>
Python:
secret = 'PYPd1Hv4J6'
message = '1515928475.417'
key = base64.b64encode(secret.encode('utf-8')) # did you mean b64decode here?
I was able to get them to match by stripping out the base64ing of everything:
Python:
import hmac
import string
import hashlib
import base64
secret = 'PYPd1Hv4J6'
message = '1515928475.417'
key = secret.encode('utf-8')
hmac_result = hmac.new(key, message.encode('utf-8'), hashlib.sha512)
print(base64.b64encode(hmac_result.digest()))
Output:
b'jezLNuBz37FoACm4LdLSqOQ5C93cuGID9a8MQmOZntXklDV3SvWdNfqndzK0a54awKeHY+behFiv4FYyILRoGQ=='
Javascript:
var crypto = require('crypto');
var secret = 'PYPd1Hv4J6';
var message = '1515928475.417'
var hmac = crypto.createHmac('sha512', secret);
var hmac_result = hmac.update(message).digest('base64');
console.log(hmac_result)
Output:
jezLNuBz37FoACm4LdLSqOQ5C93cuGID9a8MQmOZntXklDV3SvWdNfqndzK0a54awKeHY+behFiv4FYyILRoGQ==
Equivalent/Expected python code's is below.
import hmac
import string
import hashlib
import base64
secret = 'PYPd1Hv4J6=='
message = '1515928475.417'
key = base64.b64decode (secret.encode('utf-8'))
hmac_result = hmac.new(key, message.encode('utf-8'), hashlib.sha512)
print(base64.b64encode(hmac_result.digest()))
Padding '=' to targeted and decoding part was important.
Thank you.

Implement new SHA256 signature for Redsys/Sermepa in Python

I have a django-based application that uses Redsys (earlier Sermepa) as a credit card payment platform. Recently we have received a message that urge us to migrate from old SHA1 signatures to SHA256 in order to keep using the platform securely.
Platform provide several examples of how to make the new requests generating a new signature, but only using PHP and Java. Now I am trying to port the provided solutions to Python without success.
This is a sample working code in PHP (with fake key/data):
<?php
// Init form with default data
$order = "ABCDEFGHI";
$merchant_parameters = array(
"DS_MERCHANT_AMOUNT" => "1000",
"DS_MERCHANT_ORDER" => strval($order) ,
"DS_MERCHANT_MERCHANTCODE" => "012345678",
"DS_MERCHANT_CURRENCY" => "978",
"DS_MERCHANT_TRANSACTIONTYPE" => "0",
"DS_MERCHANT_TERMINAL" => "1",
);
// Key definition, decoding, and encrypt using $order
$key = 'Bg8HYhVT79PDvOxbI/Newcm31QY/9999';
echo 'key --> ' , $key , '<br/><br/>';
$key_decoded = base64_decode($key);
echo 'key-decoded --> ' , $key_decoded , '<br/><br/>';
$ciphertext = mcrypt_encrypt(MCRYPT_3DES, $key_decoded, $order, MCRYPT_MODE_CBC, implode(array_map("chr", array(0,0,0,0,0,0,0,0))) );
echo 'ciphertext --> ' , $ciphertext , '<br/><br/>';
// Transform array in encoded json
$parameters = json_encode($merchant_parameters);
echo 'parameters --> ' , $parameters , '<br/><br/>';
$parameters_encoded = base64_encode($parameters);
echo 'parameters_encoded --> ' , $parameters_encoded , '<br/><br/>';
// Calculate MAC256 of encoded array
$mac_256 = hash_hmac('sha256', $parameters_encoded, $ciphertext, true);
echo 'mac_256 --> ' , $mac_256 , '<br/><br/>';
// Encode MAC256 in base64 to get the signature
$mac_256_encoded = base64_encode($mac_256);
echo 'mac_256_encoded --> ' , $mac_256_encoded , '<br/><br/>';
?>
And this is my equivalent code in Python, with the same fake data:
# -*- encoding: utf-8 -*-
from pyDes import triple_des, CBC, PAD_PKCS5
import hashlib, json, base64, hmac
from json import JSONEncoder
# Init form with default data
order = "ABCDEFGHI"
merchant_parameters = {}
merchant_parameters['DS_MERCHANT_AMOUNT'] = "1000"
merchant_parameters['DS_MERCHANT_ORDER'] = order
merchant_parameters['DS_MERCHANT_MERCHANTCODE'] = "012345678"
merchant_parameters['DS_MERCHANT_CURRENCY'] = "978"
merchant_parameters['DS_MERCHANT_TRANSACTIONTYPE'] = "0"
merchant_parameters['DS_MERCHANT_TERMINAL'] = "1"
# Key definition, decoding, and encrypt using order
key = 'Bg8HYhVT79PDvOxbI/Newcm31QY/9999'
print '\n\nkey --> %s' % key
key_decoded = base64.b64decode(key)
print '\n\nkey_decoded --> %s' % key_decoded
k = triple_des(key_decoded, CBC, "\0\0\0\0\0\0\0\0", pad=None, padmode=PAD_PKCS5)
ciphertext = k.encrypt(order)
print '\n\nciphertext --> %s' % ciphertext
# Transform array in encoded json (with no lines
parameters = (json.dumps(merchant_parameters)).encode()
print '\n\nparameters --> %s' % parameters
parameters_encoded = ''.join(unicode(base64.encodestring(parameters), 'utf-8').splitlines())
print '\n\nparameters_encoded --> %s' % parameters_encoded
# Calculate MAC256 of encoded array
mac_256 = hmac.new(ciphertext, parameters_encoded, digestmod=hashlib.sha256).hexdigest()
print '\n\nmac_256 --> %s' % mac_256
# Encode MAC256 in base64 to get the signature
mac_256_encoded = base64.b64encode(mac_256)
print '\n\nmac_256_encoded --> %s' % mac_256_encoded
As can be seen if running the codes, resultant signatures are not equivalent, thus the payment platform rejects my Python request.
Can anybody notice where am I wrong regarding the signature generation?
Thanks in advance,
Álvaro.
Got the same problem.
Actually, after JSON encoding I have had to clean the blank spaces between key/value pairs up.
But I am getting different signatures for the ciphertext. That causes that final signature is different from the examples.
Notice that in example code they do a trailing pad for order number in the 3DES encryption.
iv = b'\0\0\0\0\0\0\0\0'
k = DES3.new(key, DES3.MODE_CBC, iv)
ceros = b'\0'*(len(DS_MERCHANT_ORDER)%8)
clave = k.encrypt(DS_MERCHANT_ORDER+ceros.encode('UTF-8'))
Thanks
EDITED:
Finally I fixed it:
To get the specific per operation key:
iv = b'\0\0\0\0\0\0\0\0'
k = DES3.new(key, DES3.MODE_CBC, iv)
ceros = b'\0'*(len(DS_MERCHANT_ORDER)%8)
claveOp = k.encrypt(DS_MERCHANT_ORDER+ceros.encode('UTF-8'))
To get the hmac.sha256:
from Crypto.Hash import HMAC, SHA256
# Realizo la codificacion SHA256
dig = HMAC.new(claveOp , msg=DS_MERCHANT_PARAMETERS, digestmod=SHA256).digest()
signatureEnc = base64.b64encode(dig)
Hope it helps you
Hie,
finally I publish in pypi new version about redsys client. https://pypi.python.org/pypi/redsys/
Thanks Álvaro about to do sha256 signature.

RSA generate private key from data with python

I have a cipher message in base64 and a pubkey.pem with the public key information. So because the key is small(576) I have recovered all the needed information to reconstruct the private key : p,q and d.
Now I want to decipher the message but I don't know how to do it. Indeed if I want to use the decrypt function I need an priv_key object but I don't know how to generate it from (n,e,d)
from Crypto.PublicKey import RSA
from base64 import b64decode
#message I want to decipher
msg="e8oQDihsmkvjT3sZe+EE8lwNvBEsFegYF6+OOFOiR6gMtMZxxba/bIgLUD8pV3yEf0gOOfHuB5bC3vQmo7bE4PcIKfpFGZBA"
pub_key64 = 'MGQwDQYJKoZIhvcNAQEBBQADUwAwUAJJAMLLsk/b+SO2Emjj8Ro4lt5FdLO6WHMMvWUpOIZOIiPu63BKF8/QjRa0aJGmFHR1mTnG5Jqv5/JZVUjHTB1/uNJM0VyyO0zQowIDAQAB'
pub_keyDER = b64decode(pub_key64)
pub_key_obj = RSA.importKey(pub_keyDER) #my weak public key
... # how to generate priv_key_obj ???
dsmg=priv_key_obj.decrypt(msg)
You can construct a private from components in the following way (documentation):
from Crypto.PublicKey import RSA
# assume d was correctly calculated
n = 1234....L
e = 65537L
d = 43434...L
private_key = RSA.construct((n, e, d))
dsmg = private_key.decrypt(msg)

PyCrypto: Attribute Error: 'NoneType' object has no attribute 'oid' while using PKCS1_v1_5

SHA = hashlib.sha1()
Eh = SHA.update(chunk)
HRSA.signSHA(Eh,RSAprivatekey)
RSAprivatekey is read in HRSA module and passed as argument to this function:
RSAprivatekey = RSA.importKey(infile.read())
infile points to the 'privatekey.txt' which contained only the RSAprivatekey.
HRSA is a module I've created which basically does this:
def signSHA(hash, key):
signer = PKCS1_v1_5.new(key)
D = signer.sign(hash)
return D
I'm being shown the following error:
File "D:\Study\Sem V\Hybrid Encryption\Phase 2\HRSA.py", line 57, in signSHA
D = signer.sign(hash)
File "C:\Python33\lib\site-packages\Crypto\Signature\PKCS1_v1_5.py", line 110, in sign
em = EMSA_PKCS1_V1_5_ENCODE(mhash, k)
File "C:\Python33\lib\site-packages\Crypto\Signature\PKCS1_v1_5.py", line 211, in EMSA_PKCS1_V1_5_ENCODE
digestAlgo = DerSequence([hash.oid, DerNull().encode()])
AttributeError: 'NoneType' object has no attribute 'oid'
How could I fix this since it's a bug with the PyCrypto code?
hashfunc.update(arg) doesn't return anything. It's there to update the internal state of the hashing function with new input data. If you want to convert the internal state into a hash, then you need to call either hashfunc.digest() or hashfunc.hexdigest().
It would look like this:
Eh = hashlib.sha1(chunk).digest()
HRSA.signSHA(Eh, RSAprivatekey)
You must use the PyCrypto module for SHA1, so:
from Crypto.Hash import SHA1
sha_obj = SHA1.new()
sha_obj.update(chunk)
HRSA.signSHA(sha_obj,RSAprivatekey)
The reason is that the PKCS#1v1.5 signature embeds the ASN.1 Object ID of the hash, but the SHA-1 object obtained from the standard library does not include/know it.
As mentioned, hashlib does not work well with PyCryptodome.
To provide a little more detailed example:
from Crypto.Hash import SHA256
from Crypto.Signature import pkcs1_15
from Crypto.PublicKey import RSA
# Generate a new RSA key pair with the specified key length
key_length = 1024
key = RSA.generate(key_length)
# Get the RSA keys
private_key = key.export_key()
public_key = key.publickey().export_key()
msg = b"The aliens are coming!"
hash = SHA256.new(msg)
# Generate the signature
signer = pkcs1_15.new(RSA.import_key(private_key))
signature = signer.sign(hash)
verifier = pkcs1_15.new(RSA.import_key(public_key))
#Exception will be thrown if verification fails
verifier.verify(hash, signature)

Categories